Last active
September 14, 2023 01:07
-
-
Save Diapolo10/0af07b0f4c3036ad8353fbc41e1c31b1 to your computer and use it in GitHub Desktop.
[Python] Flexible Tic Tac Toe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Tic-Tac-Toe implementation""" | |
from typing import Literal, get_args | |
# Custom type annotations | |
PlayerSymbol = Literal['X', 'O'] | |
BoardSymbol = Literal[' ', PlayerSymbol] | |
Board = list[list[BoardSymbol]] | |
BOARD_WIDTH = 3 | |
BOARD_HEIGHT = 3 | |
VALID_PLAYER_SYMBOLS: tuple[PlayerSymbol] = get_args(PlayerSymbol) | |
def choose_side() -> tuple[PlayerSymbol, ...]: | |
player_symbol = None | |
while player_symbol not in VALID_PLAYER_SYMBOLS: | |
player_symbol = input(f"Player, please choose your side (one of: {', '.join(VALID_PLAYER_SYMBOLS)}): ").strip().upper()[:1] | |
if player_symbol == VALID_PLAYER_SYMBOLS[0]: | |
return VALID_PLAYER_SYMBOLS | |
return tuple(reversed(VALID_PLAYER_SYMBOLS)) | |
class TicTacToe: | |
def __init__(self, players: tuple[PlayerSymbol]) -> None: | |
self.players = players | |
self.board = [ | |
[' ' for _ in range(BOARD_WIDTH)] | |
for _ in range(BOARD_HEIGHT) | |
] | |
self.winner = None | |
self.game_loop() | |
def game_loop(self): | |
while self.winner is None and self.board_has_space(): | |
for player_num, player in enumerate(self.players, 1): | |
self.render_board() | |
player_choice = self.ask_input(player_num) | |
self.update_board(player_choice, player) | |
winner = self.check_winner() | |
if winner or not self.board_has_space(): | |
break | |
self.render_board() | |
if winner is None: | |
print("The game is finished! It is a tie.") | |
for player_num, player in enumerate(self.players, 1): | |
if player == self.winner: | |
print(f"Player {player_num} has won the game!") | |
def check_winner(self) -> PlayerSymbol | None: | |
"""Check which player has met the winning conditions.""" | |
for player in self.players: | |
# Check rows | |
for row in self.board: | |
if all(cell == player for cell in row): | |
return player | |
# Check columns | |
for col in zip(*self.board): | |
if all(cell == player for cell in col): | |
return player | |
# Check crosses (assumes 3x3 board) | |
if all(cell == player | |
for cell in self.board[::4]): | |
return player | |
if all(cell == player | |
for cell in self.board[2:-1:2]): | |
return player | |
def board_has_space(self) -> bool: | |
"""Return True if there is space left on the board.""" | |
return any( | |
cell == ' ' | |
for row in self.board | |
for cell in row | |
) | |
def render_board(self): | |
"""Print out the current state of the board.""" | |
board_text = '-+-+-'.join( | |
'|'.join(cols) | |
for row in self.board | |
for cols in row | |
) | |
print(board_text) | |
def update_board(self, coordinates: tuple[int, int], symbol: PlayerSymbol): | |
"""Place a new symbol on the board.""" | |
x, y = coordinates | |
current_symbol = self.board[x][y] | |
if current_symbol not in VALID_PLAYER_SYMBOLS: | |
self.board[x][y] = symbol | |
else: | |
print(f"{current_symbol} is already in the location {coordinates}. Skipping this turn.") | |
def ask_input(self, player_number: int, is_ai: bool = False) -> tuple[int, int]: | |
"""Ask a player to enter their desired coordinates to place a new symbol.""" | |
text_coordinates = input( | |
f"Player {player_number}, please make your choice: " | |
).strip().split() | |
coordinates = ( | |
int(text_coordinates[0]), | |
int(text_coordinates[1]), | |
) | |
return coordinates | |
def main(): | |
print("Welcome to the Tic-Tac-Toe Game!") | |
players = choose_side() | |
game = TicTacToe(players) | |
game.game_loop() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment