Frooster

Game of Nim

May 21st, 2015
456
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.67 KB | None | 0 0
  1. /*
  2. * File: nim.cpp
  3. * -------------
  4. * This program simulates a simple variant of the game of nim.
  5. * In this version, the game starts with a pile of 13 coins
  6. * on a table. Players then take turns removing 1, 2, or 3
  7. * coins from the pile. The player who takes the last coin
  8. * loses. This simulation allows a human player to compete
  9. * against the computer.
  10. */
  11. #include "genlib.h"
  12. #include "simpio.h"
  13. #include <iostream>
  14. /*
  15. * Constants
  16. * ---------
  17. * N_COINS -- Initial number of coins
  18. * MAX_MOVE -- The maximum number of coins a player may take
  19. * NO_GOOD_MOVE -- Sentinel indicating no good move is available
  20. */
  21. const int N_COINS = 13;
  22. const int MAX_MOVE = 3;
  23. const int NO_GOOD_MOVE = -1;
  24. /*
  25. * Type: playerT
  26. * -------------
  27. * This enumeration type distinguishes the turns for the human
  28. * player from those for the computer.
  29. */
  30. enum playerT { Human, Computer };
  31. /* Private function prototypes */
  32. void GiveInstructions();
  33. void AnnounceWinner(int nCoins, playerT whoseTurn);
  34. int GetUserMove(int nCoins);
  35. bool MoveIsLegal(int nTaken, int nCoins);
  36. int ChooseComputerMove(int nCoins);
  37. int FindGoodMove(int nCoins);
  38. bool IsBadPosition(int nCoins);
  39. /*
  40. * Main program
  41. * ------------
  42. * This program plays the game of nim. In this implementation,
  43. * the human player always goes first.
  44. */
  45. int main() {
  46. int nCoins, nTaken;
  47. playerT whoseTurn;
  48. GiveInstructions();
  49. nCoins = N_COINS;
  50. whoseTurn = Human;
  51. while (nCoins > 1) {
  52. cout << "There are " << nCoins << " coins in the pile."<<endl;
  53. switch (whoseTurn) {
  54. case Human:
  55. nTaken = GetUserMove(nCoins);
  56. whoseTurn = Computer;
  57. break;
  58. case Computer:
  59. nTaken = ChooseComputerMove(nCoins);
  60. cout << "I'll take " << nTaken << "." << endl;
  61. whoseTurn = Human;
  62. break;
  63. }
  64. nCoins -= nTaken;
  65. }
  66. AnnounceWinner(nCoins, whoseTurn);
  67. return 0;
  68. }
  69. /*
  70. * Function: GiveInstructions
  71. * Usage: GiveInstructions();
  72. * --------------------------
  73. * This function explains the rules of the game to the user.
  74. */
  75. void GiveInstructions() {
  76. cout << "Hello. Welcome to the game of nim." << endl;
  77. cout << "In this game, we will start with a pile of" << endl;
  78. cout << N_COINS << " coins on the table. " << endl;
  79. cout << "On each turn, you" << endl;
  80. cout << "and I will alternately take between 1 and" << endl;
  81. cout << MAX_MOVE << " coins from the table." << endl;
  82. cout << "The player who" << endl;
  83. cout << "takes the last coin loses." << endl;
  84. cout << endl;
  85. }
  86. /*
  87. * Function: AnnounceWinner
  88. * Usage: AnnounceWinner(nCoins, whoseTurn);
  89. * -----------------------------------------
  90. * This function announces the final result of the game.
  91. */
  92. void AnnounceWinner(int nCoins, playerT whoseTurn) {
  93. if (nCoins == 0) {
  94. cout << "You took the last coin. You lose." << endl;
  95. } else {
  96. cout << "There is only one coin left." << endl;
  97. switch (whoseTurn) {
  98. case Human: cout << "I win." << endl; break;
  99. case Computer: cout << "I lose." << endl; break;
  100. }
  101. }
  102. }
  103. /*
  104. * Function: GetUserMove
  105. * Usage: nTaken = GetUserMove(nCoins);
  106. * ------------------------------------
  107. * This function is responsible for the human player's turn.
  108. * It takes the number of coins left in the pile as an argument,
  109. * and returns the number of coins that the player removes
  110. * from the pile. The function checks the move for legality
  111. * and gives the player repeated chances to enter a legal move.
  112. */
  113. int GetUserMove(int nCoins) {
  114. int nTaken, limit;
  115. while (true) {
  116. cout << "How many would you like? ";
  117. nTaken = GetInteger();
  118. if (MoveIsLegal(nTaken, nCoins)) break;
  119. limit = (nCoins < MAX_MOVE) ? nCoins : MAX_MOVE;
  120. cout << "That's cheating! Please choose a number";
  121. cout << " between 1 and " << limit << endl;
  122. cout << "There are " << nCoins << " coins in the pile."<<endl;
  123. }
  124. return nTaken;
  125. }
  126. /*
  127. * Function: MoveIsLegal
  128. * Usage: if (MoveIsLegal(nTaken, nCoins)) . . .
  129. * ---------------------------------------------
  130. * This predicate function returns true if it is legal to take
  131. * nTaken coins from a pile of nCoins.
  132. */
  133. bool MoveIsLegal(int nTaken, int nCoins) {
  134. return (nTaken > 0 && nTaken <= MAX_MOVE && nTaken <= nCoins);
  135. }
  136. /*
  137. * Function: ChooseComputerMove
  138. * Usage: nTaken = ChooseComputerMove(nCoins);
  139. * -------------------------------------------
  140. * This function figures out what move is best for the computer
  141. * player and returns the number of coins taken. The function
  142. * first calls FindGoodMove to see if a winning move exists.
  143. * If none does, the program takes only one coin to give the
  144. * human player more chances to make a mistake.
  145. */
  146. int ChooseComputerMove(int nCoins) {
  147. int nTaken = FindGoodMove(nCoins);
  148. if (nTaken == NO_GOOD_MOVE) nTaken = 1;
  149. return nTaken;
  150. }
  151. /*
  152. * Function: FindGoodMove
  153. * Usage: nTaken = FindGoodMove(nCoins);
  154. * -------------------------------------
  155. * This function looks for a winning move, given the specified
  156. * number of coins. If there is a winning move in that
  157. * position, the function returns that value; if not, the
  158. * function returns the constant NoWinningMove. This function
  159. * depends on the recursive insight that a good move is one
  160. * that leaves your opponent in a bad position and a bad
  161. * position is one that offers no good moves.
  162. */
  163. int FindGoodMove(int nCoins) {
  164. for (int nTaken = 1; nTaken <= MAX_MOVE; nTaken++) {
  165. if (IsBadPosition(nCoins - nTaken)) return nTaken;
  166. }
  167. return NO_GOOD_MOVE;
  168. }
  169. /*
  170. * Function: IsBadPosition
  171. * Usage: if (IsBadPosition(nCoins)) . . .
  172. * ---------------------------------------
  173. * This function returns true if nCoins is a bad position.
  174. * A bad position is one in which there is no good move.
  175. * Being left with a single coin is clearly a bad position
  176. * and represents the simple case of the recursion.
  177. */
  178. bool IsBadPosition(int nCoins) {
  179. if (nCoins == 1) return true;
  180. return FindGoodMove(nCoins) == NO_GOOD_MOVE;
  181. }
Advertisement
Add Comment
Please, Sign In to add comment