diff --git a/nim/game.py b/nim/game.py new file mode 100644 index 0000000..6e3644b --- /dev/null +++ b/nim/game.py @@ -0,0 +1,50 @@ +class NimGame: + + def get_initial_state(self): + return { + "board": [1, 3, 5, 7], + "first_player": True + } + + def get_next_state(self, state, action): + board = state["board"].copy() + row = action[0] + number_to_remove = action[1] + board[row] = board[row] - number_to_remove + next_state = { + "board": board, + "first_player": not state["first_player"], + } + return next_state + + def get_actions(self, state): + options = [] + for num in range(4): + row = state["board"][num] + if row >=3: + options.append((num, 1)) + options.append((num, 2)) + options.append((num, 3)) + elif row == 2: + options.append((num, 1)) + options.append((num, 2)) + elif row == 1: + options.append((num, 1)) + return options + + def get_reward(self, state): + if state["first_player"]: + return 1 + elif not state["first_player"]: + return -1 + else: + return 0 + + def is_over(self, state): + for row in state["board"]: + if row != 0: + return False + return True + + def get_objective(self, state): + return max if state["first_player"] else min \ No newline at end of file diff --git a/nim/player.py b/nim/player.py index 32fd9fc..67faf3c 100644 --- a/nim/player.py +++ b/nim/player.py @@ -1,10 +1,10 @@ -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) @@ -26,7 +26,7 @@ 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=3, deterministic=False) def choose_action(self, state): action = self.strategy.choose_action(state) 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/notes.md b/notes.md index 67cc9f6..dd4cabb 100644 --- a/notes.md +++ b/notes.md @@ -6,16 +6,21 @@ Which class is responsible for each of the following behaviors? For each, explain how the behavior is accomplished. ### Checking to see whether the game is over - +The game class is responsible for checking to see if the game is over. The method is_over checks the current +state of the game to see if the board is full or if either X or O has won. If any of these three things are true, +the game is over. ### Determining which actions are available at a particular state - +The game class is responsible for determining which actions are available at a particular state. The get_actions +method returns a list of the indices of open spaces. These are the actions that are available at that state. ### Showing the board - +The view class is responsible for showing the board. The format_row and format_value methods are responsible for formatting, and the print_board method displays the board to the user. The board is actually shown when the get_action method is called. ### Choosing which action to play on a turn - +The player class is responsible for choosing which action to play on a turn. The choose_action method prompts the player to choose the action. ## Checkpoint 2 Notes +For Checkpoint 2, I just programmed the method to check every possible winning combination in the game. + ### TTT Strategy For each of the following board states, if you are playing as X @@ -27,9 +32,15 @@ and it's your turn, which action would you take? Why? ---+---+--- ---+---+--- ---+---+--- ---+---+--- | | | | O | | | | +For the first board I would choose the middle row right column because that would result in a win. +For the second board, I would choose the middle row right column to prevent the other player from winning next turn. +For the third board, I would choose the top row left column because it would guarantee a win on my next turn. +For the last board, I would choose the middle space. That would force my opponent to take the bottom right. Then I could take the bottom left and win on my next turn. + ### Initial game state You can get the inital game state using game.get_initial_state(). What is the current and future reward for this state? What does this mean? +The current and future reward for this state is 0. That means that if both players play optimally, the game will result in a tie. 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() diff --git a/play_ttt.py b/play_ttt.py index ef5530a..92542ba 100644 --- a/play_ttt.py +++ b/play_ttt.py @@ -3,7 +3,7 @@ from ttt.view import TTTView from ttt.player import TTTHumanPlayer, TTTComputerPlayer player0 = TTTHumanPlayer("Player 1") -player1 = TTTHumanPlayer("Player 2") +player1 = TTTComputerPlayer("Robot") game = TTTGame() view = TTTView(player0, player1) diff --git a/ttt/game.py b/ttt/game.py index 2f0e302..e35a8a8 100644 --- a/ttt/game.py +++ b/ttt/game.py @@ -58,4 +58,22 @@ class TTTGame: def check_winner(self, state, symbol): "Checks whether the player with `symbol` has won the game." - return False + board = state["board"] + if board[0] == symbol and board[1] == symbol and board[2] == symbol: + return True + elif board[3] == symbol and board[4] == symbol and board[5] == symbol: + return True + elif board[6] == symbol and board[7] == symbol and board[8] == symbol: + return True + elif board[0] == symbol and board[3] == symbol and board[6] == symbol: + return True + elif board[1] == symbol and board[4] == symbol and board[7] == symbol: + return True + elif board[2] == symbol and board[5] == symbol and board[8] == symbol: + return True + elif board[0] == symbol and board[4] == symbol and board[8] == symbol: + return True + elif board[2] == symbol and board[4] == symbol and board[6] == symbol: + return True + else: + return False diff --git a/ttt/player.py b/ttt/player.py index bfbbe15..0349fdf 100644 --- a/ttt/player.py +++ b/ttt/player.py @@ -1,5 +1,6 @@ from click import Choice, prompt from strategy.random_strategy import RandomStrategy +from strategy.lookahead_strategy import LookaheadStrategy from ttt.game import TTTGame import random @@ -24,7 +25,7 @@ class TTTComputerPlayer: def __init__(self, name): "Sets up the player." self.name = name - self.strategy = RandomStrategy(TTTGame()) + self.strategy = LookaheadStrategy(TTTGame(), deterministic=False) def choose_action(self, state): "Chooses a random move from the moves available."