/*** cowbull.c -- 電腦猜人想的 Bull & Cow game * @CopyLeft by tsaiwn@csie.nctu.edu.tw ****************************************/ //#define DEBUG /// #if defined(sun) || defined(__SVR4) || defined(__MINGW32__) // no fpurge() fuction for Sun OS #define fpurge fflush #endif /**********************/ #include #include #include #include #include #include int possible[5040]; // global variable int npsible; // global variable 記著有幾個可能答案 // int reduceAnswers(int myGuess, int nbull, int ncow); int readLine(char*); void checkAnswer(int xx, int yy, int *na, int *nb, int item); void hello( ) { fprintf(stderr, "公牛Bull = 位置對數字也對, \n\r"); fprintf(stderr, "母牛 Cow = 數字對但位置不對!\n\r"); } int main(){ int n, tmp, nguess, nbull, ncow; int yn, myGuess; long now= time(0); srand( now &0xffff); /* randomize, see "man srand" */ hello( ); again: npsible = prepareAnswers(); // 放在 possible[5040] nbull = ncow = nguess = 0; while(nbull != 4) { #ifdef DEBUG fprintf(stderr, "DEBUG: npsible = %d\n", npsible); #endif // 由所有可能答案中隨便挑一個當 myGuess, 並印出說猜該數 // n = rand() % npsible; /* 0.. npsible -1 */ tmp = possible[0]; possible[0] = possible[n]; possible[n] = tmp; myGuess = possible[0]; /* 永遠猜第 0 個比較好寫 */ fprintf(stderr, "I guess your number" " is %4.4d, 幾A 幾B? ", myGuess); ++nguess; // 猜的次數 // 由 user 取得幾A放 nbull, 幾 B 放 ncow getAnswer(&nbull, &ncow); if(nbull == 4){ if(nguess<5)fprintf(stderr, "才 %d 次", nguess); fprintf(stderr, "喔 Yeah 我猜到了!\n"); if(nguess>6)fprintf(stderr, "這麼衰..猜了 %d 次!\n", nguess); break; } /* 還沒猜對, 把不可能的答案刪除並修正 npsible */ npsible = reduceAnswers(myGuess, nbull, ncow); if(npsible <= 0){ fprintf(stderr, "You 欺騙我!\n"); break; } } fprintf(stderr, "\n要不要繼續玩下一攤(yes, no)? "); yn = askPlayAgain(); if(yn) goto again; fprintf(stderr, "Thank you and Bye!\nHit RETURN key ..."); getchar( ); return 0; } //function to check if the user want to play again int askPlayAgain(){ /* return 1 if he/she said YES */ static char tmp[99]; readLine(tmp); if(tmp[0] == 'Y' || *tmp == 'y') return 1; if(toupper(tmp[0]) == 'N') return 0; fprintf(stderr, "Please type N or Y: "); return askPlayAgain(); /* recursively call myself */ } // function to get how many A + how many B from player int getAnswer(int *nbull, int *ncow){ static char tmp[99], i; char *pa, *pb; /* used to make the program more intelegent */ readLine(tmp); pa = strchr(tmp, 'A'); if(pa==0) pa=strchr(tmp, 'a'); pb = strchr(tmp, 'B'); if(pb==0) pb=strchr(tmp, 'b'); if(pa==0 && pb!=0) pa = pb+3; if(pb==0 && pa!=0) pb = pa+3; for(i=0; i< strlen(tmp); i++){ if(tmp[i] == 'A' || tmp[i] == 'a') tmp[i] = ' '; if(tmp[i] == 'B' || tmp[i] == 'b') tmp[i] = ' '; if(tmp[i] == ',' || tmp[i] == ';') tmp[i] = ' '; } *nbull = *ncow = 0; i = sscanf(tmp, "%d %d", nbull, ncow); /* 注意 nbull, ncow */ if(i!=2) i = sscanf(tmp, "%d, %d", nbull, ncow); // try ??,?? if(pa>pb) { i = *nbull; *nbull = *ncow; *ncow = i; } /*swap A, B*/ #ifdef DEBUG fprintf(stderr, "===%d A, %d B\n", *nbull, *ncow); #endif return *nbull; } /// 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; x[p++] = i*1000+k*100+m*10+n; }// for n } // for m }//for k }// for i return p; } /// function reduceAnswers() int reduceAnswers(int myGuess, int nbull, int ncow){ // 刪除不可能之答案, 並將可能之答案放在 array 前端 // // 以下只做刪除不可能之答案 // /// 順便把可能答案往 array 前端移 : 留給大家想 .. int na, nb, 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 完全相同表示為可能答案 // //否則都要消去 // #ifdef DEBUG if(i==1 || i==npsible-1 || i%100 == 0) fprintf(stderr, "DEBUG: %4d) ", i); #endif checkAnswer(myGuess, possible[i], &na, &nb, i); if(na == nbull && nb==ncow){ possible[n] = possible[i]; /*可能的答案移到 array 較前面*/ ++n; /* n 指向下一個可蓋掉的元素 possible[n] */ } } npsible = n; return n; } /*** /// 要稍微修正checkAnswer 因以前是比兩個 array 現在是比兩個整數, 要先用 %10 的方法拆成四位數 // *************************************************/ void checkAnswer(int xx, int yy, int *na, int *nb, int item){ int x[4], y[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); } #ifdef DEBUG if(item ==1 || item ==npsible-1 || item %100 == 0) fprintf(stderr, " x= %d %d %d %d, y= %d %d %d %d :" " %d A %d B\n", x[3],x[2],x[1],x[0], y[3],y[2],y[1],y[0], *na, *nb); #endif return; } void chop(char*p) { // 會去掉字串 p 最後的 newLine if(*p ==0) return; //empty string while(p[0] != 0) ++p; // 移到字串尾巴 --p; // 倒退回去一格 if(*p == '\n') *p=0; // chop it out! } // chop( //function to read one Line from fp int readLine(char buf[]) { buf[0] = 0; fgets(buf, 99, stdin); // 注意傳過來的 buf 要夠大 ! 須 >= 99 if(ferror(stdin)) { clearerr(stdin); fpurge(stdin); fflush(stdin); // 都 try 看看 :-) return -1; /* tell caller that error occurs */ } chop(buf); // 把最後的 '\n' 去掉 return strlen(buf); // 共讀入多少 char }