//CowBull.java -- required haBull.java /*** CowBull.c -- 電腦猜人想的 Bull & Cow game * @CopyLeft by tsaiwn@csie.nctu.edu.tw ****************************************/ import java.io.*; import java.util.*; // StringTokenizer in this package class CowBull { public static void main(String xx[ ]) { new CowBull( ); } CowBull( ) { // 單機版的 constructor prepareIO( ); // 把鍵盤和螢幕 stream 準備好 play( ); } CowBull(BufferedReader cin, PrintStream cout, Brother him) { this.cin = cin; this.cout = cout; this.sys = him; // 指回去正在 run 我的 Brother play( ); } BufferedReader cin; PrintStream cout; Brother sys; // who is running me String record = " "; void putRecord(int guess, int na, int nb) { record += " "+guess+"("+na+"A " +nb+"B), "; } void prepareIO( ) { cin = new BufferedReader( new InputStreamReader( System.in ) ); cout = new PrintStream( System.out); } /// int possible[ ] = new int[5040]; // global variable int npsible; // global variable 記著有幾個可能答案 // int rand( ) { return (int) (Math.random( ) * 32767 ); } // C 程式中原來的 main int play( ){ int n, tmp, nguess, nbull, ncow; int yn, myGuess; //long now= time(0); //srand( now &0xffff); /* randomize, see "man srand" */ record= " ** " + sys.name + ": "; boolean quit = false; // 不離開, 要繼續玩 :-) while( ! quit ) { //again: npsible = prepareAnswers( ); // 放在 possible[5040] nbull = ncow = nguess = 0; while(nbull != 4) { // 由所有可能答案中隨便挑一個當 myGuess, 並印出說猜該數 // n = rand( ) % npsible; /* 0.. npsible -1 */ tmp = possible[0]; possible[0] = possible[n]; possible[n] = tmp; myGuess = possible[0]; /* 永遠猜第 0 個比較好寫 */ cout.printf( "I guess your number" + " is %04d, ... ", myGuess); ++nguess; // 猜的次數 /// /// 由 user 取得幾A放 nbull, 幾 B 放 ncow cout.print("\r\n \t\t 幾 A 幾 B? "); nbull = getAB( ); if(nbull >= 3000) { // he keyin 2 answers nbull = nbull %100; ncow = nbull %10; nbull = nbull / 10; }else if(nbull != 4) { // 只回答 幾 A, 且不是 4 A cout.print("\r \t\t " + nbull + " A 以及 幾 B? "); ncow = getInt( ); } //getAnswer(&nbull, &ncow); putRecord(myGuess, nbull, ncow); // proof if(nbull == 4){ if(nguess<5)cout.printf( "才 %d 次", nguess); cout.printf("喔 Yeah 我猜到了!\n"); if(nguess>6)cout.printf("這麼衰..猜了 %d 次!\n", nguess); sys.log(3, " -->got "+myGuess+" in "+nguess+" times " + " with " + sys.name); break; } cout.printf("%04d : %d A %d B; ", myGuess, nbull, ncow); /// /* 還沒猜對, 把不可能的答案刪除並修正 npsible */ npsible = reduceAnswers(myGuess, nbull, ncow); if(npsible <= 0){ cout.printf( "You 欺騙我!\n"); sys.log(5, " -->??? "+myGuess+" in "+nguess+" times " + " cheating by " + sys.name); sys.log(5, " ??? "+record); // proof cout.printf("\r 你看: " +record); break; } } cout.printf("\r\n\t 要不要繼續玩下一攤(yes, no)? "); record= " " + sys.name + ": "; // reset record yn = askPlayAgain(); if(yn == 0) quit = true; //goto again; }//while(!quit cout.printf( "Thank you and Bye!\n"); return 0; } // play( //function to check if the user want to play again int askPlayAgain(){ /* return 1 if he/she said YES */ String tmp="no"; //static char tmp[99]; try { tmp = cin.readLine( ); // readLine(tmp); }catch(Exception e) { } if(tmp.length( ) == 0) return 1; // 只按 ENTER if(tmp.charAt(0) == 'Y' || tmp.charAt(0) == 'y') return 1; if(tmp.charAt(0) == 'N' || tmp.charAt(0) == 'n') return 0; // if(toupper(tmp[0]) == 'N') return 0; cout.printf( "Please type N or Y: "); return askPlayAgain(); /* recursively call myself */ } /// function prepareAnswers(); int prepareAnswers(){ //想辦法 (用四層 for loop ) 把所有可能答案 放在 possible[5040] //共有 5040 個, 最小 0123, 最大 9876 //(因每位不能重複) //int * x = possible; /* 之後 x[0] 就是 possible[0] */ // 以下是原來給大家的例子 int p=0, i,k,m,n; for(i=0; i<=9; i++){ for(k=0; k<=9; k++){ if(k==i) continue; /* 第二位k與第一位數i相同的都不要 */ for(m=0; m<=9; m++){ if(m==k || m==i) continue; for(n=0; n<=9; n++){ if(n==m || n==k || n==i) continue; possible[p++] = i*1000+k*100+m*10+n; } }//for m }//for k }//for i return p; } /// function reduceAnswers() int reduceAnswers(int myGuess, int nbull, int ncow) { // 刪除不可能之答案, 並將可能之答案放在 array 前端 // // 以下只做刪除不可能之答案 // /// 順便把可能答案往 array 前端移 : 留給大家想 .. int na=0, nb=0, i; int n= 0; /* possible[0] 是第一個可以蓋掉的 */ /* 所以由 possible[1] 開始看看有哪些是有可能的就留下來 */ for(i=1; i< npsible; i++){ //叫用以前的 checkAnswer(要稍微修正) 比較 myGuess 和 possible[i] //以便知道myGuess對於 possible[i]是幾A幾B // //若幾A幾B剛好與 nbull 和 ncow 完全相同表示為可能答案 // //否則都要消去 // int kkk = checkAnswer(myGuess, possible[i], na, nb, i); na = kkk/10; nb = kkk%10; if(na == nbull && nb==ncow){ possible[n] = possible[i]; /*可能的答案移到 array 較前面*/ ++n; /* n 指向下一個可蓋掉的元素 possible[n] */ } }//for i npsible = n; return n; } /*** ***要稍微修正checkAnswer 因以前是比兩個 array 現在是比兩個整數, 要先用 %10 的方法拆成四位數 // *************************************************/ int checkAnswer(int xx, int yy, int na, int nb, int item){ int x[ ]= new int[4], y[ ] = new int[4], i, j; na = nb =0; /* 要先把整數拆解成四位個位數 */ x[0] = xx % 10; y[0] = yy % 10; x[1] = (xx %100) /10; y[1] = (yy %100) /10; x[2] = (xx /100) %10; y[2] = (yy /100) %10; x[3] = xx /1000; y[3] = yy /1000; for(i=0; i<=3; i++){ if(x[i] == y[i]) ++(na); for(j=0; j<=3; j++) if(i!=j && x[i] == y[j]) ++(nb); }//for i return na*10 + nb; } int getInt( ) { String s = "000"; try { s = cin.readLine( ); if(s.length( ) == 0) s = "0"; }catch(Exception e) { } int ans =0; //if(s.length( ) == 0) return 0; try { ans = Integer.parseInt(s); }catch(Exception e) { ans = 0; } return ans; } int getAB( ) { // 問 player 幾 A 幾 B? 若回答 二者, +3000 做區別 int na=0, nb=0; String s = "0"; try { s = cin.readLine( ); if(s.length( ) == 0 ) s= "0"; // 只有按 ENTER 當作 0 }catch(Exception e) { s="0"; } StringTokenizer stk = new StringTokenizer(s, " ,\t"); if( stk.hasMoreTokens( ) ) { try { na = Integer.parseInt( stk.nextToken( ) ); }catch(Exception e) { na = 0; } } if( stk.hasMoreTokens( ) ) { // nb try { nb = Integer.parseInt( stk.nextToken( ) ); }catch(Exception e) { nb = 0; } na = na*10 + nb + 3000; // indicate 2 answers } // return na; // 若 >= 3000 表示有兩個答案 ?A ? B }//getAB( } // class