Initial version of lab

This commit is contained in:
Chris Proctor 2022-04-28 15:03:52 -04:00
commit 900c4622ad
8 changed files with 214 additions and 0 deletions

9
.commit_template Normal file
View File

@ -0,0 +1,9 @@
(Commit summary. Replace this with a one-line description of this commit.)
What I changed
(Replace this with a description of what you changed in this commit. This should be 1-2 sentences.)
Why I changed it
(Describe why you made these changes. Were you working toward a goal? Did you reorganize your code? This should be 1-2 sentences.)
Estimate for remaining time to finish assignment: [REPLACE WITH TIME ESTIMATE]

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__/*

14
play.py Normal file
View File

@ -0,0 +1,14 @@
from ttt_game import TTTGame
from ttt_view import TTTView
from ttt_player import TTTHumanPlayer
player0 = TTTHumanPlayer("Player 1")
player1 = TTTHumanPlayer("Player 2")
game = TTTGame(player0, player1)
view = TTTView()
view.greet(game)
while not game.is_over():
move = view.get_move(game)
game.play_move(move)
view.conclude(game)

33
poetry.lock generated Normal file
View File

@ -0,0 +1,33 @@
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "b1fe87cf4a78305c8810dfd14d6b6354185272e19826bd925ee2d6c7d1a8c763"
[metadata.files]
click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]

16
pyproject.toml Normal file
View File

@ -0,0 +1,16 @@
[tool.poetry]
name = "mwc-pedprog-unit02-lab02"
version = "0.1.0"
description = ""
authors = ["Chris Proctor <github.com@accounts.chrisproctor.net>"]
license = "MIT"
[tool.poetry.dependencies]
python = "^3.9"
click = "^8.1.3"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

45
ttt_game.py Normal file
View File

@ -0,0 +1,45 @@
class TTTGame:
"""Models a tic-tac-toe game.
"""
def __init__(self, playerX, playerO):
self.board = [None] * 9
self.turn_index = 0
self.players = {
'X': playerX,
'O': playerO,
}
def play_move(self, move):
if not self.is_valid_move(move):
raise ValueError(f"Illegal move {move} with board {self.board}.")
self.board[move] = self.get_current_player_symbol()
self.turn_index += 1
def get_valid_moves(self):
"Returns a list of the indices of empty spaces"
return [index for index in range(9) if self.board[index] is None]
def is_valid_move(self, move):
"Checks whether a move is valid."
return move in self.get_valid_moves()
def get_current_player_symbol(self):
"Returns the symbol of the current player"
if self.turn_index % 2 == 0:
return 'X'
else:
return 'O'
def get_current_player(self):
"Returns the symbol of the current player and the current player"
return self.players[self.get_current_player_symbol()]
def is_over(self):
"Checks whether the game is over."
return self.check_winner('X') or self.check_winner('O')
def check_winner(self, symbol):
"Checks whether the player with `symbol` has won the game."
return False

35
ttt_player.py Normal file
View File

@ -0,0 +1,35 @@
from click import Choice, prompt
import random
class TTTPlayer:
"A tic tac toe player."
def __init__(self, name):
"Sets up the player."
self.name = name
def get_symbol(self, game):
"Returns this player's symbol in the game."
if game.players['X'] == self:
return 'X'
elif game.players['O'] == self:
return 'O'
else:
raise ValueError(f"Player {self.name} isn't in this game!")
class TTTHumanPlayer(TTTPlayer):
"A human tic tac toe player."
def choose_move(self, game):
"Chooses a move by prompting the player for a choice."
choices = Choice([str(i) for i in game.get_valid_moves()])
move = prompt("> ", type=choices, show_choices=False)
return int(move)
class TTTComputerPlayer(TTTPlayer):
"A computer tic tac toe player"
def choose_move(self, game):
"Chooses a random move from the moves available."
return random.choice(game.get_valid_moves())

61
ttt_view.py Normal file
View File

@ -0,0 +1,61 @@
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 greet(self, game):
"Starts a new game by greeting the players."
print(self.greeting)
print(f"{game.players['X']} will play as X.")
print(f"{game.players['O']} will play as O.")
def get_move(self, game):
"Shows the board and asks the current player for their choice of move."
self.print_board_with_options(game)
player = game.get_current_player()
print(f"{player.name}, it's your move.")
return player.choose_move(game)
def print_board_with_options(self, game):
"Prints the current board, showing indices of available spaces"
print(self.format_row(game, [0, 1, 2]))
print(self.divider)
print(self.format_row(game, [3, 4, 6]))
print(self.divider)
print(self.format_row(game, [6, 7, 8]))
def format_row(self, game, indices):
"Returns a string for one row in the board, like ' X | O | X '"
spaces = [self.format_value(game, i) for i in indices]
return f" {spaces[0]} | {spaces[1]} | {spaces[2]} "
def format_value(self, game, 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 game.board[index] == 'X':
return click.style('X', fg=self.x_color)
elif game.board[index] == 'O':
return click.style('O', fg=self.o_color)
else:
return click.style(index, fg=self.option_color)
def conclude(self, game):
"""Says goodbye.
"""
if game.check_winner('X'):
winner = game.players['X']
elif game.check_winner('X'):
winner = game.players['X']
else:
raise Exception("Tried to conclude a game which wasn't over!")
print(self.goodbye)
print(f"Congratulations to {winner.name}.")