From af79c47691fa51fc5b53eeede1f4f2793904b1dc Mon Sep 17 00:00:00 2001 From: Chris Proctor Date: Wed, 11 May 2022 16:46:00 -0400 Subject: [PATCH] Add nim stub to main repo --- nim/game_stub.py | 34 ++++++++++++++++++++++++++++++++++ nim/player.py | 35 +++++++++++++++++++++++++++++++++++ nim/view.py | 32 ++++++++++++++++++++++++++++++++ play_nim.py | 15 +++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 nim/game_stub.py create mode 100644 nim/player.py create mode 100644 nim/view.py create mode 100644 play_nim.py diff --git a/nim/game_stub.py b/nim/game_stub.py new file mode 100644 index 0000000..b8a7d36 --- /dev/null +++ b/nim/game_stub.py @@ -0,0 +1,34 @@ +class NimGameStub: + """A stub is a minimal version of a class which stands in for the + real class, which hasn't yet been written. The stub has all the correct + methods, and their inputs and outputs are the right kind of thing, + but it doesn't really do anything. + """ + def get_initial_state(self): + return { + "board": [1, 3, 5, 7], + "first_player": True + } + + def get_next_state(self, state, action): + next_state = { + "board": state["board"].copy(), + "first_player": not state["first_player"], + } + return next_state + + def get_actions(self, state): + return [ + (0, 0), + (1, 0), (1, 1), + (2, 0), (2, 1), (2, 2), (3, 0), (3, 1), (3, 2), (3, 3), + ] + + def get_reward(self, state): + return 0 + + def is_over(self, state): + return False + + def get_objective(self, state): + return max if state["first_player"] else min diff --git a/nim/player.py b/nim/player.py new file mode 100644 index 0000000..32fd9fc --- /dev/null +++ b/nim/player.py @@ -0,0 +1,35 @@ +from nim.game_stub import NimGameStub +from strategy.lookahead_strategy import LookaheadStrategy + +class HumanNimPlayer: + def __init__(self, name): + self.name = name + self.game = NimGameStub() + + 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}.") + choice = self.get_int(len(actions)) + return actions[choice] + + def get_int(self, maximum): + while True: + response = input("> ") + if response.isdigit(): + value = int(response) + if value < maximum: + return value + print("Invalid input.") + +class ComputerNimPlayer: + def __init__(self, name): + self.name = name + self.strategy = LookaheadStrategy(NimGameStub(), max_depth=3, 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}") + return action diff --git a/nim/view.py b/nim/view.py new file mode 100644 index 0000000..16e3fdf --- /dev/null +++ b/nim/view.py @@ -0,0 +1,32 @@ +from nim.game_stub import NimGameStub + +class NimView: + def __init__(self, player0, player1): + self.players = [player0, player1] + self.game = NimGameStub() + + def greet(self): + print(f"{self.players[0].name} and {self.players[1].name}, welcome to Nim.") + + def show_board(self, state): + for lines_in_row in state["board"]: + print("| " * lines_in_row) + + def get_action(self, state): + self.show_board(state) + player = self.get_current_player(state) + return player.choose_action(state) + + def get_current_player(self, state): + if state["first_player"]: + return self.players[0] + else: + return self.players[1] + + def conclude(self, state): + self.show_board(state) + if self.game.get_reward(state) > 0: + winner = self.players[0] + else: + winner = self.players[1] + print(f"Congratulations, {winner.name}!") diff --git a/play_nim.py b/play_nim.py new file mode 100644 index 0000000..61f5cf2 --- /dev/null +++ b/play_nim.py @@ -0,0 +1,15 @@ +from nim.game_stub import NimGameStub +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() + +view.greet() +state = game.get_initial_state() +while not game.is_over(state): + action = view.get_action(state) + state = game.get_next_state(state, action) +view.conclude(state)