//nim3.c  ---  NIM GAME, by tsaiwn@csie.nctu.edu.tw
//prototype by tsaiwn@csie.nctu.edu.tw
//******
// ..這版本比 nim2.c 最主要就是 userTurn( ) 內的 goto 改用 while 
//  C++ 本來就認識 bool, false, true; C則可用 typedef enum...bool;
/**********
  請仔細研讀前一版 num2.c 中的說明 與 Lab-11 的說明 
 *******************************************************///////////   
/// 若要用  Turbo C++ 的分割劃面可以參考:
///    ftp://ftp.csie.nctu.edu.tw/pub/CSIE/course/cs2/textwin.cpp
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>

#ifndef __cplusplus
  //typedef int bool;    // 與 #define bool int  同效果, 但要加分號;
 typedef enum {false=0, true } bool;   // 更好 :-) 跟 C++ 同樣!
#endif

#define MAX_PILE 7
#define MAX_STONE 31
#define MIN_STONE 5

/// =================> use Global variables 比較方便但要小心處理!
int nPile;  /* number of Pile */
int nStone[MAX_PILE + 1];   // 第 0 堆不要用, 用 1 到 7  !!
         // 注意不用第 0 堆是因這樣跟人的習慣比較像 :-)
int pTake, nTake;   // take from which pile, how many stones
bool quit;  // indicate that user want to Stop playing

bool lastToWin;  // 1 == 拿到最後一個贏,  0 == 拿到最後一個贏輸 
bool userFirst;   // user want to Go first
bool gameBegin;  // used in printStatus( )
char msgWin[ ][33] = { "拿到最後一顆輸Lose!", "拿到最後一顆贏Win!" };

bool gameOver( );   // indicate somebody took the last stone
void userTurn(void),  computerTurn(void);
void prepareGame(void);  // 把game 用到的整體(global;共用)變數準備好.
void playGame(void);
void printStatus(void);  // 印出各堆石頭狀況.
void hello(void) {
    printf("NIM 是從many多石頭堆中輪流拿, \r\n");
    // 有些compiler下不能寫 "許" ? compile時會出問題! 故寫 many 多 :-)
    printf(" ..NIM 每次只能從一堆拿, 至少拿一個, 至多把該堆拿光,\r\n");
    printf(" .. 可規定拿到最後一顆贏, 或拿到最後一顆輸!\r\n\n");
}

bool askYN( ){
    bool ans;   // 1 == true == Yes, 0 == false == No
    static char buf[333];
    fgets(buf, sizeof(buf), stdin);
    if(buf[0] == 'N' || buf[0] =='n') return false;
    if(buf[0] == 0 ) return false;   //  '\0'  ==  0
    return true;   // We like the user to play :-) So default is YES
}
void clrERR( ) {
   errno = 0; // clear  errno in <errno.h>
}//clrErr

int main( ) {
   bool playAgain;  // Local variable is enough
   hello( );
   srand( time(0) &0xfffff );  // true random, see "man 3 time"
   playAgain = true;  // force to play :-)
   while(playAgain) {
      prepareGame( );
      playGame( );
      printf("Want to play again (Y, N)? ");
      playAgain = askYN( );
   }
   printf("\r\nThank you for your play!\nCome to play again!\n");
   return 0;
} // main

///////////////////////////////////////////////////////////
void prepareGame(void) {
    int i;
    lastToWin = userFirst =  (38==38);  // true
    if(rand( ) % 100 >= 50) lastToWin = false;  // 50%, 就是 %2 == 1
    nPile = 3 + rand( ) % (MAX_PILE-3+1);  // 3..7 
    for(i=1; i <= nPile; ++i) {  /// 注意由第一堆算起
       nStone[i] = MIN_STONE + rand( ) % (MAX_STONE -MIN_STONE +1);
    }
} // prepareGame

void printStatus( ){
    int i;
    printf("There are 共有 %d pile堆石頭:\r\n", nPile);
    printf("   P_%d: %d", 1, nStone[1]);  // Note that 沒第 0 堆
    for(i=2; i <= nPile; ++i) {  // then pile-2, pile-3, ...
       printf(",  P_%d: %d", i, nStone[i]);
    }
    printf("\r\n");
    if(gameBegin) {   // 只有剛開始才印這句
       printf("!!! 注意 %s! %s!!\r\n",  
               msgWin[lastToWin], msgWin[lastToWin]);
       gameBegin = false; //So that won't print again
    }
} // printStatus

bool gameOver( ) {  // Note that Pile# is 1..nPile
   int i, totalStone=0;
   for(i=1; i <= nPile; ++i) totalStone += nStone[i];
   return (totalStone <= 0);  // no more stone == gameOver
}
void computerTurn( ){
    pTake = 1;  // 從第一堆看看; 注意我們一開始就規劃不用第 0 堆
    while(nStone[pTake]==0) ++pTake;   // 目前先找還有石頭的
    nTake = 1;   // 至少要拿一個
    nTake = 1 + rand( ) % nStone[pTake];  // 隨便拿
    //
    // 要確保資料正確, 不可拿超過, 並想辦法能贏就要贏
    // how to win?
    //
    printf(" --> I take %d from Pile %d\r\n", nTake, pTake);
    nStone[pTake] -= nTake;
    printStatus( );
} // computerTurn

void userTurn( ) {
    bool goodData;   // Note that "quit" is a Global variable
    static char buf[88];
 //again:
    while( true ) {  // not got good data yet
       quit = false;  // assume continue to play !
       printf("Take from which Pile? ");
       fgets(buf, sizeof(buf), stdin);
       pTake = (int)atol(buf);
       // 防呆處理: 要確保輸入資料正確, 不可讓 user 亂輸入..
       // ... 若該堆 0 個也不要讓他拿喔!
       if(feof(stdin)) { clrERR( ); pTake = -1;  } // 讀到 CTRL_Z
       if(pTake < 0) {
            // 輸入負數看要如何處理, 可當作錯, 也可表示不玩了 :-) 
            quit = true;  // want to quit from the game
            break;
       }
       if(pTake ==0 ) {
           printf(" Sorry, there is no pile#0\r\n");
           continue;  // goto again;
       }
       if(pTake > nPile ) {
           printf(" ? Which pile? "); // print 沒那麼多堆喔 !
           continue;   // goes back to while( )  (again:)
       }
      //again22:
       while( true )  {  // Loop forever
          printf(" from Pile# %d, take how many stones (1..%d)? ",
              pTake, nStone[pTake]);
          fgets(buf, sizeof(buf), stdin);
          nTake = (int)atol(buf);
          // 防呆處理: 要確保輸入資料正確, 不可拿超過
          ///*** how ? 
          if(feof(stdin)){clrERR( ); nTake = -1;} // 輸入 CTRL_Z
          if(nTake < 0) { 
             // 如何允許他後悔改從別堆 Pile 拿 ?
             goodData = false; // set FLAG for outside while Loop
             break;  // leave this while Loop
          } // if
          if(nTake == 0 || nTake > nStone[pTake]) {
              printf(" 耍白痴?? 不要亂打啦!\r\n");
              continue;  // goto again22;
          }
          goodData = true;   // data is good :-)
          break;  //
       } // while
       if( !goodData ) continue; // goto again;
       break;  // OK, 取得 pTake 堆 與  nTake
    } // while !dataOK
    if(quit) {
        fprintf(stderr, " You chicken!!! \r\n");
        return;  // user 不玩了 
    }
    nStone[pTake] -= nTake;
    printStatus( );
} // userTurn

void playGame(void) {
   int userWin;  // Local variable is enough
   gameBegin = true;// 每次game開始, 要印出拿最後一個會贏還是會輸
   quit = false;   // indicate continue to play
   printStatus( );  // 印出目前各堆石頭, 若第一次印要印上列說的
   printf(" ..Want to go first (Y, N)? ");
   userFirst =  askYN( );
   userWin = false;
   // Game starts ..
   if(userFirst) userTurn( );
   if(gameOver( ) && lastToWin) userWin = true;
   while(!quit && !gameOver( )){ // take turn till no more stone
      computerTurn( );
      if(gameOver( )) {
         if(lastToWin) userWin = false; else userWin = true;
         break;
      }
      userTurn( );
      if(gameOver( )) {
         userWin = false;
         if(lastToWin) userWin = true;  // user took the last
         break;
      }
   } // while
   if(userWin) {
      printf(" Congratulations -- You Win!\r\n");
   }else{
      printf(" SOOORRRY Sorry! -- Ha Ha Ha! -- I Won!\r\n");
   }
} // playGame
/************
-- 
 Proof techniques #1: Proof by Induction.
 Q: 試用歸納法證明:飯永遠吃不飽.
    1. 吃一粒米顯然不會飽
    2. 假設吃了 n 粒米沒有飽
       再吃一粒米顯然也不會飽, 就是說吃 n+1 粒米也不會飽
      由此可推得米飯永遠吃不飽 !
   QED.   (QED translates from the Latin as "So what?")
((((((((((((((((((((((((((()))))))))))))))))))))))))))))
********************************************************/
