diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..cc6fb7f Binary files /dev/null and b/.DS_Store differ diff --git a/nim/game.py b/nim/game.py new file mode 100644 index 0000000..e740a4f --- /dev/null +++ b/nim/game.py @@ -0,0 +1,49 @@ +class NimGame: + """A Nim game class based off NimGameStub. + """ + def get_initial_state(self): + ''' Constructs the game board and has player 1 start the game. ''' + return { + "board": [1, 3, 5, 7], + "first_player": True + } + + def get_next_state(self, state, action): + ''' Creates a copy of the current board and then makes adjustments + based on the chosen action from a player's turn. ''' + new_board = state["board"].copy() + new_board[action[0]] = new_board[action[0]] - action[1] + return { + "board": new_board, + "first_player": not state["first_player"], + } + + def get_actions(self, state): + ''' Construct the list of actions that can be taken by a player. + Is there a more efficient way of doign this? ''' + actions = [] + new_board = state["board"].copy() + for i in range(4): # check the four rows of the board + if new_board[i] > 0: # if a row is not empty + for j in range(1,4): # check if you can remove 1, 2, or 3 ticks in the row + if j <= new_board[i]: # if so + actions.append((i,j)) # include that as an option + return actions + + def get_reward(self, state): + ''' Reports who wins. ''' + if state["first_player"]: + return 1 # If it is the player's turn and there is nothing on the board, then it was the robot who crossed off the last line. + return 0 # Otherwise, the first player made the last move and lost. + + def is_over(self, state): + ''' Reports true if there are no more lines to cross. ''' + if all(ticks == 0 for ticks in state["board"]): + return True + return False + + def get_objective(self, state): + ''' Reports the desired obejctive to help choose the move that + yields the optimal reward under the lookahead_strategy for + computer players. ''' + return max if state["first_player"] else min diff --git a/nim/player.py b/nim/player.py index 32fd9fc..9060167 100644 --- a/nim/player.py +++ b/nim/player.py @@ -1,16 +1,16 @@ -from nim.game_stub import NimGameStub +from nim.game import NimGame from strategy.lookahead_strategy import LookaheadStrategy class HumanNimPlayer: def __init__(self, name): self.name = name - self.game = NimGameStub() + self.game = NimGame() def choose_action(self, state): actions = self.game.get_actions(state) for i, action in enumerate(actions): row, lines_to_remove = action - print(f"{i}. Remove {lines_to_remove} from row {row}.") + print(f"{i}. Remove {lines_to_remove} from row {row+1}.") choice = self.get_int(len(actions)) return actions[choice] @@ -26,10 +26,10 @@ class HumanNimPlayer: class ComputerNimPlayer: def __init__(self, name): self.name = name - self.strategy = LookaheadStrategy(NimGameStub(), max_depth=3, deterministic=False) + self.strategy = LookaheadStrategy(NimGame(), max_depth=5, deterministic=False) def choose_action(self, state): action = self.strategy.choose_action(state) row, lines_to_remove = action - print(f"{self.name} removes {lines_to_remove} from row {row}") + print(f"{self.name} removes {lines_to_remove} from row {row+1}") return action diff --git a/nim/view.py b/nim/view.py index 16e3fdf..8bd3d88 100644 --- a/nim/view.py +++ b/nim/view.py @@ -1,9 +1,9 @@ -from nim.game_stub import NimGameStub +from nim.game import NimGame class NimView: def __init__(self, player0, player1): self.players = [player0, player1] - self.game = NimGameStub() + self.game = NimGame() def greet(self): print(f"{self.players[0].name} and {self.players[1].name}, welcome to Nim.") diff --git a/play_nim.py b/play_nim.py index 61f5cf2..293b4ef 100644 --- a/play_nim.py +++ b/play_nim.py @@ -1,11 +1,11 @@ -from nim.game_stub import NimGameStub +from nim.game import NimGame from nim.view import NimView from nim.player import HumanNimPlayer, ComputerNimPlayer player0 = HumanNimPlayer(input("What's your name? ")) player1 = ComputerNimPlayer("Robot") view = NimView(player0, player1) -game = NimGameStub() +game = NimGame() view.greet() state = game.get_initial_state()