Skip to content

Instantly share code, notes, and snippets.

@cwshevlin
Last active December 9, 2021 21:49
Show Gist options
  • Save cwshevlin/327ba45e575c9c7b04f318aee89eb52e to your computer and use it in GitHub Desktop.
Save cwshevlin/327ba45e575c9c7b04f318aee89eb52e to your computer and use it in GitHub Desktop.
A command line tic tac toe game
from typing import Optional
import argparse
def _validate_number_of_players(number_of_players: int):
if number_of_players != 2:
raise ValueError('Tic Tac Toe is a 2 player game for now.')
def _validate_player_mark(player_mark: str):
if len(player_mark) != 1:
raise ValueError('Please keep your tic tac toe marks to one character.')
def _validate_row_and_column(row: str, column: str):
try:
int(row)
int(column)
except ValueError:
raise ValueError('Please provide numbers for the rows and columns.')
class Game:
def __init__(self):
self.current_player_index: int = 0
self.board: Board = Board(3, 3)
self.players: list[Player] = []
def initialize_players(self):
"""
Initialize the players and get names and marks as input from the player.
"""
print('Welcome to Tic Tac Toe! How many players are playing?')
number_of_players = int(input())
_validate_number_of_players(int(number_of_players))
for player_number in range(number_of_players):
print(f'Player {player_number + 1}, what is your name?')
player_name = input()
print(f'Hi {player_name}! What would you like to use to mark your moves?')
player_mark = input()
_validate_player_mark(player_mark)
self.players.append(Player(player_name, player_mark))
def get_current_player(self):
return self.players[self.current_player_index]
def complete_turn(self):
"""
Marks the board with the player's input, checks if the board is winning, and increments the player index.
"""
print(f'{self.get_current_player().name}, your turn! Where would you like to play?')
row = input('Row:')
column = input('Column:')
try:
self.board.mark_turn(self.get_current_player(), int(row), int(column))
except ValueError as e:
print(f'\n{e}\n')
self.complete_turn()
return
if self.board.is_board_winning():
print(f'\n\n{self.get_current_player().name} wins!\n\n')
print(self.board.print_current_board())
elif self.board.is_board_full():
print(f'\n\nCat\'s game!\n\n')
print(self.board.print_current_board())
self._increment_player_index()
def _increment_player_index(self):
self.current_player_index += 1
if self.current_player_index >= len(self.players):
self.current_player_index = 0
class Player:
def __init__(self, name: Optional[str] = None, mark: Optional[str] = None):
self.mark = mark
self.name = name
class Board:
def __init__(self, number_of_rows: int = 3, number_of_columns: int = 3):
self.board: list[list[str]] = [[''] * number_of_columns for i in range(number_of_rows)]
self.number_of_rows = number_of_rows
self.number_of_columns = number_of_columns
self.past_moves: list[Move] = []
def mark_turn(self, player: Player, row: int, column: int):
"""
Receives the row and column from the player, records a move, and updates the board.
"""
row_index = row - 1
column_index = column - 1
self._validate_row_and_column(row_index, column_index)
move = Move(player, player.mark, row, column)
self.past_moves.append(move)
self.board[row_index][column_index] = player.mark
self.print_current_board()
def print_current_board(self):
board_string = '-------------\n\n'
for i in range(self.number_of_rows):
board_string += '|'
for j in range(self.number_of_columns):
mark = self.board[i][j]
if not mark:
mark = ' '
board_string += f' {mark} |'
board_string += '\n\n-------------\n\n'
print(f'\n\nLet\'s take a look at the board: \n\n{board_string}')
def is_board_winning(self) -> bool:
"""
Check rows, columns, and diagonals for the same character
"""
return self._are_diagonals_winning() or self._are_rows_winning() or self._are_columns_winning()
def is_board_full(self) -> bool:
"""
Returns True if the board is full of marks, False if there is at least one blank position
"""
full = True
for i in range(self.number_of_rows):
for j in range(self.number_of_columns):
if not self.board[i][j]:
full = False
return full
def _are_diagonals_winning(self) -> bool:
"""
Check diagonals for winning characters
"""
first_diagonal_wins = self.board[0][0] and (self.board[0][0] == self.board[1][1] == self.board[2][2])
second_diagonal_wins = self.board[0][2] and (self.board[0][2] == self.board[1][1] == self.board[2][0])
return first_diagonal_wins or second_diagonal_wins
def _are_rows_winning(self) -> bool:
"""
Check rows for winning characters
"""
first_row_wins = self.board[0][0] and (self.board[0][0] == self.board[0][1] == self.board[0][2])
second_row_wins = self.board[1][0] and (self.board[1][0] == self.board[1][1] == self.board[1][2])
third_row_wins = self.board[2][0] and (self.board[2][0] == self.board[2][1] == self.board[2][2])
return first_row_wins or second_row_wins or third_row_wins
def _are_columns_winning(self) -> bool:
"""
Check columns for winning characters
"""
first_column_wins = self.board[0][0] and (self.board[0][0] == self.board[1][0] == self.board[2][0])
second_column_wins = self.board[0][1] and (self.board[0][1] == self.board[1][1] == self.board[2][1])
third_column_wins = self.board[0][2] and (self.board[0][2] == self.board[1][2] == self.board[2][2])
return first_column_wins or second_column_wins or third_column_wins
def _validate_row_and_column(self, row, column):
if not (row < self.number_of_rows and column < self.number_of_columns):
raise ValueError('Moves must be within the bounds of the board')
if not self.board[row][column] == '':
raise ValueError('There is already a mark at that position, choose another one')
class Move:
def __init__(self, player: Player, mark: str, row: int, column: int):
self.player = player
self.mark = mark
self.row = row
self.column = column
def game_loop():
game = Game()
game.initialize_players()
while not game.board.is_board_winning() and not game.board.is_board_full():
game.complete_turn()
print('\n\nThanks for playing!')
parser = argparse.ArgumentParser(description='Play a tic tac toe game')
parser.add_argument('command', metavar='command', type=str,
help='"new_game" to start a new game or "rules" to learn the rules.')
args = parser.parse_args()
if args.command == 'new_game':
print('Starting a new game.\n')
game_loop()
elif args.command == 'rules':
print(f'Via Wikipedia: \n\n '
f'"Tic-tac-toe is played on a three-by-three grid by two players, '
f'who alternately place the marks X and O in one of the nine '
f'spaces in the grid." \n\n'
f'To win the game, get three of your marks in a row before your opponent.')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment