//nim5ok.java
import java.io.*;
import java.util.*;

class nim5ok {
     public static void main(String xxx[ ] ) {
         new nim5ok( );
     }
 nim5ok(BufferedReader gg, PrintStream yy) {
    cin = gg;
    cout = yy;
    mainNIM( ); // original main( ) in C
 }

 nim5ok(  ) {
    prepareIO( );
    mainNIM( ); // original main( ) in C
 }
 BufferedReader cin;
 PrintStream cout;

void prepareIO( ) {
   try {
       cin = new BufferedReader(
              new InputStreamReader( System.in ) );
       cout = new PrintStream( System.out );
   }catch(Exception e) {  }
} // IO ok

/*********************************************************

//nim5OK.c  ---  NIM GAME, by tsaiwn@csie.nctu.edu.tw
//prototype by tsaiwn@csie.nctu.edu.tw
//******
// ..這版本 就把 nim3.c  加上 computer 可贏就贏的策略
//  C++ 本來就認識 boolean, false, true; C則可用 typedef enum...boolean;
/**********
  請仔細研讀前一版 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 boolean;    // 與 #define boolean int  同效果, 但要加分號;
// typedef enum {false=0, true } boolean;   // 更好 :-) 跟 C++ 同樣!
  // C++/Java 已經認識 false, true; 但 boolean 在 Java 是 booleanean
//#endif

//#define MAX_PILE 7
//#define MAX_STONE 31
//#define MIN_STONE 5

  public static final int  MAX_PILE  = 7;

  public static final int  MAX_STONE = 31;

  public static final int  MIN_STONE = 5;


/// =================> use Global variables 比較方便但要小心處理!

int nPile;  /* number of Pile */

//int nStone[MAX_PILE + 1];   // 第 0 堆不要用, 用 1 到 7  !!
         // 注意不用第 0 堆是因這樣跟人的習慣比較像 :-)
int nStone[ ] = new int[MAX_PILE + 1];  

int pTake, nTake;   // take from which pile, how many stones
boolean quit;  // indicate that user want to Stop playing

boolean lastToWin;  // 1 == 拿到最後一個贏,  0 == 拿到最後一個贏輸 
boolean userFirst;   // user want to Go first
boolean gameBegin;  // used in printStatus( )
//char msgWin[ ][33] = { "拿到最後一顆輸Lose!", "拿到最後一顆贏Win!" };

 String msgWin[ ] = { "拿到最後一顆輸Lose!", "拿到最後一顆贏Win!" };


/*** ******** Java 不用宣告 :-)   *********
boolean 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( ) {
    cout.printf("NIM 是從many多石頭堆中輪流拿, \r\n");
    // 有些compiler下不能寫 "許" ? compile時會出問題! 故寫 many 多 :-)
    cout.printf(" ..NIM 每次只能從一堆拿, 至少拿一個, 至多把該堆拿光,\r\n");
    cout.printf(" .. 可規定拿到最後一顆贏, 或拿到最後一顆輸!\r\n\n");
}

boolean askYN( ){
    boolean ans;   // 1 == true == Yes, 0 == false == No
    String buf=null;   // static char buf[333];
    //fgets(buf, sizeof(buf), stdin);
    try {
       buf = cin.readLine( );
    }catch(Exception e) { buf = "N"; }
    if(buf.charAt(0) == 'N' || buf.charAt(0) =='n') return false;
    if(buf.length( ) == 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 mainNIM( ) {  
   boolean 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( );
      cout.printf("Want to play again (Y, N)? ");
      playAgain = askYN( );
   }
   cout.printf("\r\nThank you for your play!\nCome to play again!\n");
   return 0;
} // main

int rand( ) {
    return (int) (32767* Math.random( ) );
}
///////////////////////////////////////////////////////////
void prepareGame( ) {
    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;
    cout.printf("There are 共有 %d pile堆石頭:\r\n", nPile);
    cout.printf("   P_%d: %d", 1, nStone[1]);  // Note that 沒第 0 堆
    for(i=2; i <= nPile; ++i) {  // then pile-2, pile-3, ...999999
       cout.printf(",  P_%d: %d", i, nStone[i]);
    }
    cout.printf("\r\n");
    int kkk = 0;
    if(lastToWin) kkk = 1;
    if(gameBegin) {   // 只有剛開始才印這句
       cout.printf("!!! 注意 %s! %s!!\r\n",  
               msgWin[kkk], msgWin[kkk]);
       gameBegin = false; //So that won't print again
    }
} // printStatus

boolean 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 userTurn( ) {
    boolean goodData;   // Note that "quit" is a Global variable
    String buf; //static char buf[88];
 //again:
    while( true ) {  // not got good data yet
       quit = false;  // assume continue to play !
       cout.printf("Take from which Pile? ");
       //fgets(buf, sizeof(buf), stdin);
       //pTake = (int)atol(buf);
       try {
           buf = cin.readLine( );
       }catch(Exception e) { buf = "-1"; }
       if (buf == null ) buf = "-1";
       try {
          pTake = Integer.parseInt( buf );
       }catch(Exception e) { pTake = 0; }

       // 防呆處理: 要確保輸入資料正確, 不可讓 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 ) {
           cout.printf(" Sorry, there is no pile#0\r\n");
           continue;  // goto again;
       }
       if(pTake > nPile ) {
           cout.printf(" ? Which pile? "); // print 沒那麼多堆喔 !
           continue;   // goes back to while( )  (again:)
       }
      //again22:
       while( true )  {  // Loop forever
          cout.printf(" from Pile# %d, take how many stones (1..%d)? ",
              pTake, nStone[pTake]);
          //fgets(buf, sizeof(buf), stdin);
          //nTake = (int)atol(buf);

          try {
              buf = cin.readLine( );
          }catch(Exception e) { buf = "-1"; }
          if (buf == null ) buf = "-1";
          try {
             nTake = Integer.parseInt( buf );
          }catch(Exception e) { nTake = 0; }

          // 防呆處理: 要確保輸入資料正確, 不可拿超過
          ///*** 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]) {
              cout.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) {
        cout.printf( " You chicken!!! \r\n");
        return;  // user 不玩了 
    }
    nStone[pTake] -= nTake;
    printStatus( );
} // userTurn

void playGame( ) {
   boolean userWin;  // int userWin;  // Local variable is enough
   gameBegin = true;// 每次game開始, 要印出拿最後一個會贏還是會輸
   quit = false;   // indicate continue to play
   printStatus( );  // 印出目前各堆石頭, 若第一次印要印上列說的
   cout.printf("\r\n ..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) {
      cout.printf(" Congratulations -- You Win!\r\n");
   }else{
      cout.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?")
((((((((((((((((((((((((((()))))))))))))))))))))))))))))
********************************************************/

// NIM Game 電腦必贏的密技 ---
//#######################
   // #define MAX_PILE 7
   // int nPile = 3 + rand( )%(MAX_PILE -3 + 1);  // 3..7
   // int nStone[MAX_PILE + 1];    // 第 0 堆不用, 所以要多一堆
   // assume that the global variable nPile is the #of pile in this game.
   // assume that  nStone[1] .. nStone[nPile]  are 各堆石頭數
   // 注意第0堆不用, 浪費一個元素沒關係! 這會方便許多, why ?
//////

/*** *** Java 不用宣告 !
int findPile(int sumXOR); // function to find the available pile
int atMostOne(int p, int n);  // to check if we reach to the critical step
    // this function is ONLY useful for the one takes last object to lose
    // i.e., check to 看看若從第 p 堆拿走 n 個後是否造成各堆最多一個?
*********/

void computerTurn( ) {     // 把 array 多留一個元素, 第0個不要用
   int pp=0, nn=0, remain=0, i;   //  注意不是 0..n-1,  我們用 1..n 堆!
   int sum = 0;
   for(i=1; i <= nPile; ++i)  sum ^= nStone[i];    // 不用第 0 堆!
   if(sum == 0) {
        nn = 1; // 因 XOR 起來是0, 表示必輸的, 隨便拿一個等對方拿錯
          // ..從任一有石頭的堆(pp) 拿走一個 (nn = 1);
         // ..  (用 while 找 pp,  很簡單, 留給 同學們 想 :-)
        // ..或從最多石頭那堆拿一個更好!
   } else {   // 會贏, 但是要找出如何拿..
      pp = findPile(sum);  // find which pile that we can win in normal NIM   
           //  找出該拿哪一堆才會贏, 先用拿最後一個會贏的策略
      remain = nStone[pp] ^ sum;  // 要拿成剩下 remain 這麼多 
      nn = nStone[pp] - remain;    // 算出該拿 nn 這麼多才會使 sum 是 0
      //special process for last object to lose, 若拿最後是輸, 要特別處理
      if( ( ! lastToWin) && atMostOne(pp, nn) ) {    
          if(remain==1) ++nn; //還有, 拿光它,把剩下的那個也拿走;例 1,1,1,6
          if(remain==0) --nn; //放回一個!Why?例 1,1,5 會誤判要拿成 1, 1, 0
      } // if lastToWin== false 表示拿到最後的是輸
   }//if 
   nStone[pp] -= nn;  // 從 pp 堆拿走 nn 個, 阿就該堆減去 nn 啦 
   cout.printf( "..I just took %d stone(s) from pile#%d\n",nn, pp);
   printStatus( );  // tell user 目前各堆的狀況..
}
int findPile(int sum) {  // 找 #of objects 與 sum 做 xor 後會變少的堆
   int i=1;
   for(i=1; i<=nPile; ++i)    // 從第 1 堆找到第  nPile 堆
       if( (sum ^ nStone[i] ) < nStone[i]) return i; // XOR 後變小表示可
   // impossible to here --- 因不可能找不到任何一堆! wikipedia.org 有證明
   cout.printf( " ? 有問題喔! impossible to here!\n");
   return -1; // 故意回傳 -1
} // findPile
boolean atMostOne(int p, int n) {   // 看看從第 p 堆拿走 n 個會不會每堆最多一個
       // 若從第 p 堆若拿走 n 個後, 沒有一堆超過一個石頭, 則傳回 1 表示 yes
   int i;
   boolean ans = true;  // assume yes;  也可像我之前範例使用 true/false
   nStone[p] -= n ;   // 注意是 減等於 (-=) ,  先假裝第 p 堆拿走 n 個
   for(i=1; i<=nPile; ++i)  // check all piles (heaps) 1..nPile
        if(nStone[i] > 1) ans = false;  // at least one pile has stones > 1  
   nStone[p] += n ;  // 把 n 個放回第 p 堆! 因我們只是檢查 
   return ans;    // 若要用 tru/false, 函數開始須  boolean ans = true;
} // atMostOne in each pile (heap)
///////////////////////////////////////////////

} // class
