/*** 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<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include <sys/types.h>
#include <sys/time.h>
       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 
}
