Initial commit

This commit is contained in:
2025-08-28 04:58:26 +00:00
commit b38519b081
15 changed files with 523 additions and 0 deletions

61
ttt/game.py Normal file
View File

@@ -0,0 +1,61 @@
class TTTGame:
"Models a tic-tac-toe game."
def get_initial_state(self):
"Returns the game's initial state."
return {
"board": ['-', '-', '-', '-', '-', '-', '-', '-', '-'],
"player_x": True,
}
def get_next_state(self, state, action):
"""Given a state and an action, returns the resulting state.
In the resulting state, the current player's symbol has been placed
in an empty board space, and it is the opposite player's turn.
"""
new_board = state["board"].copy()
new_board[action] = 'X' if state["player_x"] else 'O'
return {
"board": new_board,
"player_x": not state["player_x"],
}
def get_actions(self, state):
"Returns a list of the indices of empty spaces"
return [index for index in range(9) if state["board"][index] == '-']
def is_over(self, state):
"Checks whether the game is over."
return self.board_is_full(state) or self.check_winner(state, 'X') or self.check_winner(state, 'O')
def get_reward(self, state):
"""Determines the reward associated with reaching this state.
For tic-tac-toe, the two opponents each want a different game outcome. So
we set the reward for X winning to 1 and the reward for O winning to -1.
All other states (unfinished games and games which ended in a draw) are worth 0.
"""
if self.check_winner(state, 'X'):
return 1
elif self.check_winner(state, 'O'):
return -1
else:
return 0
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 tic tac toe, the players
want opposite things, so we set X's objective to the built-in function `max`
(which chooses the largest number), and we set O's objective to the built-in function `min`.
"""
return max if state["player_x"] else min
def board_is_full(self, state):
"Checks whether all the spaces in the board are occupied."
for space in state["board"]:
if space == '-':
return False
return True
def check_winner(self, state, symbol):
"Checks whether the player with `symbol` has won the game."
return False

33
ttt/player.py Normal file
View File

@@ -0,0 +1,33 @@
from click import Choice, prompt
from strategy.random_strategy import RandomStrategy
from ttt.game import TTTGame
import random
class TTTHumanPlayer:
"A human tic tac toe player."
def __init__(self, name):
"Sets up the player."
self.name = name
self.game = TTTGame()
def choose_action(self, state):
"Chooses an action by prompting the player for a choice."
actions = self.game.get_actions(state)
choices = Choice([str(action) for action in actions])
action = int(prompt("> ", type=choices, show_choices=False))
return action
class TTTComputerPlayer:
"A computer tic tac toe player"
def __init__(self, name):
"Sets up the player."
self.name = name
self.strategy = RandomStrategy(TTTGame())
def choose_action(self, state):
"Chooses a random move from the moves available."
action = self.strategy.choose_action(state)
print(f"{self.name} chooses {action}.")
return action

75
ttt/view.py Normal file
View File

@@ -0,0 +1,75 @@
from ttt.game import TTTGame
import click
class TTTView:
"Handles user interaction with a tic-tac-toe game."
greeting = "Welcome to tic-tac-toe"
goodbye = "Well, that's a wrap."
divider = "---+---+---"
x_color = "red"
o_color = "blue"
option_color = "bright_black"
def __init__(self, playerX, playerO):
self.game = TTTGame()
self.players = {
"X": playerX,
"O": playerO,
}
def greet(self):
"Starts a new game by greeting the players."
x_name = self.players['X'].name
o_name = self.players['O'].name
print(self.greeting)
print(f"{x_name} will play as X.")
print(f"{o_name} will play as O.")
def get_action(self, state):
"Shows the board and asks the current player for their choice of action."
self.print_board(state)
current_player_symbol = 'X' if state["player_x"] else 'O'
player = self.players[current_player_symbol]
print(f"{player.name}, it's your move.")
return player.choose_action(state)
def print_board(self, state):
"Prints the current board, showing indices of available spaces"
print(self.format_row(state, [0, 1, 2]))
print(self.divider)
print(self.format_row(state, [3, 4, 5]))
print(self.divider)
print(self.format_row(state, [6, 7, 8]))
def format_row(self, state, indices):
"Returns a string for one row in the board, like ' X | O | X '"
spaces = [self.format_value(state, i) for i in indices]
return f" {spaces[0]} | {spaces[1]} | {spaces[2]} "
def format_value(self, state, index):
"""Formats the value for a single space on the board.
If the game board already has a symbol in that space, formats that value for the Terminal.
If the space is empty, instead formats the index of the space.
"""
if state["board"][index] == 'X':
return click.style('X', fg=self.x_color)
elif state["board"][index] == 'O':
return click.style('O', fg=self.o_color)
else:
return click.style(index, fg=self.option_color)
def conclude(self, state):
"""Says goodbye.
"""
self.print_board(state)
if self.game.check_winner(state, 'X'):
winner = self.players['X']
elif self.game.check_winner(state, 'O'):
winner = self.players['O']
else:
winner = None
print(self.goodbye)
if winner:
print(f"Congratulations to {winner.name}.")
else:
print("Nobody won this game.")