I completed converting game_stub to game for Nim.

My strategy was to use the game.py from Tic Tac Toe and try to compare
what was happening in Nim to TTT.
This commit is contained in:
root 2024-12-15 00:38:37 -05:00
parent 70fdcaac79
commit cd99fae57d
4 changed files with 78 additions and 7 deletions

69
nim/game.py Normal file
View File

@ -0,0 +1,69 @@
class NimGame:
"Models a Nim game."
def get_initial_state(self):
"Returns the game's initial state."
return {
"board": [1, 3, 5, 7],
"first_player": True
}
def get_next_state(self, state, action):
"""Given a state and an action, returns the resulting state.
In the resulting state, the lines have been removed from last
turn, and it is the opposite player's turn.
"""
next_state = {
"board": state["board"].copy(),
"first_player": not state["first_player"],
}
row, lines_to_remove = action
next_state["board"][row] -= lines_to_remove
return next_state
def get_actions(self, state):
"Returns a list of possible moves."
actions = []
for row, lines in enumerate(state["board"]):
for lines_to_remove in range(1, 4):
if lines >= lines_to_remove:
actions.append((row, lines_to_remove))
return actions
def get_reward(self, state):
"""Determines the reward associated with reaching this state.
For Nim, the two opponents each want a different game outcome.
If the game is over when it is first_player's turn, they lose, so reward is -1
and the reward for the game being over on Computer's turn as 1.
All other states (unfinished games) are worth 0.
"""
if self.is_over(state):
if state["first_player"]:
return -1
elif not state["first_player"]:
return 1
else:
return 0
def is_over(self, state):
"Checks whether the game is over."
return self.board_is_empty(state)
def board_is_empty(self, state):
"Checks whether all the lines in the board are gone."
for lines in state["board"]:
if lines != 0:
return False
return True
def get_objective(self, state):
"""Returns a player's objective, or a function describing what a player wants.
This function should choose the best value from a list. In Nim, the players
want opposite things, so we set first_player's objective to the built-in function `max`
(which chooses the largest number), and we set Computer's objective to the built-in function `min`.
"""
return max if state["first_player"] else min

View File

@ -1,10 +1,12 @@
from nim.game_stub import NimGameStub
from nim.game import NimGame
from strategy.lookahead_strategy import LookaheadStrategy
from strategy.random_strategy import RandomStrategy
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 +28,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)

View File

@ -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.")

View File

@ -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()