diff --git a/crm_5_implementation.py b/crm_5_implementation.py index 36d5866..73205b0 100644 --- a/crm_5_implementation.py +++ b/crm_5_implementation.py @@ -1,364 +1,302 @@ """ Tic Tac Toe Game Implementation -This module provides a complete implementation of a Tic Tac Toe game -with HTML, CSS, and JavaScript components. The game supports player -turns, win detection, and game state persistence. - -Classes: - TicTacToe: Main game class handling game logic and state management - GameBoard: Represents the game board and its state - Player: Represents a player in the game - -Functions: - main: Entry point for running the game +This module provides a complete implementation of the Tic Tac Toe game +with HTML, CSS, and JavaScript components. The game follows MVC architecture +and includes proper state management, win condition checking, and player turn handling. """ -import json -import os -from typing import List, Optional, Tuple, Dict, Any from enum import Enum -from dataclasses import dataclass +from typing import List, Optional, Tuple, Dict, Any +import json -class PlayerSymbol(Enum): - """Enumeration for player symbols.""" +class Player(Enum): + """Enumeration for game players.""" X = "X" O = "O" + EMPTY = " " class GameStatus(Enum): - """Enumeration for game status states.""" + """Enumeration for game statuses.""" PLAYING = "playing" - X_WINS = "x_wins" - O_WINS = "o_wins" + X_WON = "x_won" + O_WON = "o_won" DRAW = "draw" -@dataclass -class Player: - """Represents a player in the Tic Tac Toe game.""" - symbol: PlayerSymbol - name: str - - -class GameBoard: - """Represents the game board and its state.""" +class TicTacToeGame: + """ + Main game controller for Tic Tac Toe implementation. - def __init__(self, size: int = 3): + This class manages the game state, player turns, win condition checking, + and maintains the game board. + """ + + def __init__(self) -> None: """ - Initialize the game board. + Initialize the Tic Tac Toe game. - Args: - size: The size of the board (default 3 for 3x3) + Sets up the game board, initializes player turns, and prepares + the game state for play. """ - self.size = size - self.board: List[List[Optional[PlayerSymbol]]] = [ - [None for _ in range(size)] for _ in range(size) + self.board: List[List[Player]] = [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] ] - - def make_move(self, row: int, col: int, symbol: PlayerSymbol) -> bool: + self.current_player: Player = Player.X + self.game_status: GameStatus = GameStatus.PLAYING + self.move_history: List[Tuple[int, int]] = [] + + def make_move(self, row: int, col: int) -> bool: """ - Make a move on the board. + Make a move on the game board at specified position. Args: - row: Row index (0-based) - col: Column index (0-based) - symbol: Player symbol to place + row (int): The row index (0-2) where the move should be made + col (int): The column index (0-2) where the move should be made Returns: - True if move was successful, False otherwise + bool: True if the move was successful, False otherwise Raises: - IndexError: If row or column is out of bounds + ValueError: If row or col are outside valid range (0-2) """ - if not (0 <= row < self.size and 0 <= col < self.size): - raise IndexError("Move coordinates are out of bounds") + if not (0 <= row <= 2 and 0 <= col <= 2): + raise ValueError("Row and column must be between 0 and 2") - if self.board[row][col] is not None: + if self.board[row][col] != Player.EMPTY: return False - self.board[row][col] = symbol - return True + # Make the move + self.board[row][col] = self.current_player + self.move_history.append((row, col)) - def get_cell(self, row: int, col: int) -> Optional[PlayerSymbol]: - """ - Get the symbol at a specific cell. + # Check for win or draw + self._check_game_status() - Args: - row: Row index (0-based) - col: Column index (0-based) + # Switch player if game is still playing + if self.game_status == GameStatus.PLAYING: + self.current_player = Player.O if self.current_player == Player.X else Player.X - Returns: - The symbol at the cell or None if empty - """ - return self.board[row][col] - - def is_full(self) -> bool: + return True + + def _check_game_status(self) -> None: """ - Check if the board is completely filled. + Check the current game status for win conditions or draw. - Returns: - True if board is full, False otherwise + This method evaluates the board state to determine if there is a winner + or if the game has ended in a draw. """ + # Check rows for row in self.board: - for cell in row: - if cell is None: - return False - return True + if row[0] == row[1] == row[2] != Player.EMPTY: + self.game_status = GameStatus.X_WON if row[0] == Player.X else GameStatus.O_WON + return + + # Check columns + for col in range(3): + if self.board[0][col] == self.board[1][col] == self.board[2][col] != Player.EMPTY: + self.game_status = GameStatus.X_WON if self.board[0][col] == Player.X else GameStatus.O_WON + return + + # Check diagonals + if self.board[0][0] == self.board[1][1] == self.board[2][2] != Player.EMPTY: + self.game_status = GameStatus.X_WON if self.board[0][0] == Player.X else GameStatus.O_WON + return + + if self.board[0][2] == self.board[1][1] == self.board[2][0] != Player.EMPTY: + self.game_status = GameStatus.X_WON if self.board[0][2] == Player.X else GameStatus.O_WON + return + + # Check for draw + is_board_full = all( + self.board[row][col] != Player.EMPTY + for row in range(3) + for col in range(3) + ) - def get_empty_cells(self) -> List[Tuple[int, int]]: + if is_board_full: + self.game_status = GameStatus.DRAW + + def reset_game(self) -> None: """ - Get list of all empty cells on the board. + Reset the game to initial state. - Returns: - List of tuples containing (row, col) of empty cells + Clears the board, resets player turns, and sets game status back to playing. """ - empty_cells = [] - for row in range(self.size): - for col in range(self.size): - if self.board[row][col] is None: - empty_cells.append((row, col)) - return empty_cells - - def reset(self) -> None: - """Reset the board to initial state.""" self.board = [ - [None for _ in range(self.size)] for _ in range(self.size) + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] ] + self.current_player = Player.X + self.game_status = GameStatus.PLAYING + self.move_history.clear() - -class TicTacToe: - """Main game class handling game logic and state management.""" - - def __init__(self, player1: Player, player2: Player, board_size: int = 3): - """ - Initialize the Tic Tac Toe game. - - Args: - player1: First player - player2: Second player - board_size: Size of the game board (default 3) - """ - self.player1 = player1 - self.player2 = player2 - self.current_player = player1 - self.board = GameBoard(board_size) - self.status = GameStatus.PLAYING - self.winner: Optional[Player] = None - self.move_history: List[Tuple[int, int, PlayerSymbol]] = [] - - def make_move(self, row: int, col: int) -> bool: + def get_board_state(self) -> List[List[Player]]: """ - Make a move for the current player. + Get the current state of the game board. - Args: - row: Row index (0-based) - col: Column index (0-based) - Returns: - True if move was successful, False otherwise - - Raises: - ValueError: If the game has already ended + List[List[Player]]: A copy of the current board state """ - if self.status != GameStatus.PLAYING: - raise ValueError("Game has already ended") - - success = self.board.make_move(row, col, self.current_player.symbol) - - if success: - # Record the move - self.move_history.append((row, col, self.current_player.symbol)) - - # Check win condition - if self._check_win(row, col): - self.status = GameStatus.X_WINS if self.current_player.symbol == PlayerSymbol.X else GameStatus.O_WINS - self.winner = self.current_player - elif self.board.is_full(): - self.status = GameStatus.DRAW - - # Switch player - self.current_player = self.player2 if self.current_player == self.player1 else self.player1 - - return success - - def _check_win(self, row: int, col: int) -> bool: + return [row[:] for row in self.board] + + def get_current_player(self) -> Player: """ - Check if the last move resulted in a win. + Get the current player making a move. - Args: - row: Last move row - col: Last move column - Returns: - True if the move resulted in a win, False otherwise + Player: The current player (X or O) """ - symbol = self.board.get_cell(row, col) - - # Check row - if all(self.board.get_cell(row, c) == symbol for c in range(self.board.size)): - return True - - # Check column - if all(self.board.get_cell(r, col) == symbol for r in range(self.board.size)): - return True - - # Check diagonals - if row == col: - # Main diagonal - if all(self.board.get_cell(i, i) == symbol for i in range(self.board.size)): - return True - - if row + col == self.board.size - 1: - # Anti-diagonal - if all(self.board.get_cell(i, self.board.size - 1 - i) == symbol for i in range(self.board.size)): - return True - - return False - - def get_game_state(self) -> Dict[str, Any]: + return self.current_player + + def get_game_status(self) -> GameStatus: """ - Get the current game state. + Get the current game status. Returns: - Dictionary containing game state information + GameStatus: The current status of the game (playing, won, draw) """ - return { - "status": self.status.value, - "current_player_symbol": self.current_player.symbol.value, - "board": [ - [cell.value if cell else None for cell in row] - for row in self.board.board - ], - "winner": self.winner.symbol.value if self.winner else None, - "move_history": self.move_history - } - - def reset(self) -> None: - """Reset the game to initial state.""" - self.board.reset() - self.status = GameStatus.PLAYING - self.winner = None - self.current_player = self.player1 - self.move_history.clear() - - def get_winner(self) -> Optional[Player]: + return self.game_status + + def get_move_history(self) -> List[Tuple[int, int]]: """ - Get the winner of the game. + Get the move history of the current game. Returns: - The winning player or None if no winner + List[Tuple[int, int]]: List of moves made (row, col) """ - return self.winner - - def get_current_player(self) -> Player: + return self.move_history.copy() + + def get_winner(self) -> Optional[Player]: """ - Get the current player. + Get the winner of the game if there is one. Returns: - The current player + Optional[Player]: The winning player or None if no winner """ - return self.current_player - - def get_board(self) -> GameBoard: + if self.game_status == GameStatus.X_WON: + return Player.X + elif self.game_status == GameStatus.O_WON: + return Player.O + else: + return None + + def get_game_state(self) -> Dict[str, Any]: """ - Get the game board. + Get the complete game state as a dictionary. Returns: - The game board instance + Dict[str, Any]: Dictionary containing all game state information """ - return self.board + return { + "board": self.get_board_state(), + "current_player": self.current_player.value, + "game_status": self.game_status.value, + "winner": self.get_winner().value if self.get_winner() else None, + "move_history": self.move_history + } -def create_html_template(game_state: Dict[str, Any], game_id: str) -> str: +class GameRenderer: """ - Create an HTML template for the Tic Tac Toe game. + Handles rendering of the Tic Tac Toe game to HTML. - Args: - game_state: Current game state dictionary - game_id: Unique identifier for the game instance - - Returns: - HTML string representing the game interface + This class generates the HTML and CSS for displaying the game board + and managing user interaction. """ - # Create basic HTML structure with embedded CSS and JavaScript - html_content = f""" + + def __init__(self) -> None: + """ + Initialize the game renderer. + + Sets up the basic HTML structure and CSS styling for the game. + """ + self.game_state = None + + def render_html(self, game: TicTacToeGame) -> str: + """ + Render the complete HTML for the Tic Tac Toe game. + + Args: + game (TicTacToeGame): The current game instance + + Returns: + str: Complete HTML string for the game display + """ + self.game_state = game + + html_content = f""" - Tic Tac Toe Game + Tic Tac Toe
-

Tic Tac Toe

-
Current Player: {game_state['current_player_symbol']}
-
- {"".join([ - f'
' - for r in range(3) for c in range(3) - ])} +

Tic Tac Toe

+
Current Player: {game.get_current_player().value}
+
+ {self._render_board(game)}
-
-""" - - return html_content - - -def save_game_state(game: TicTacToe, file_path: str) -> None: - """ - Save the current game state to a JSON file. - - Args: - game: The TicTacToe game instance to save - file_path: Path to the file where game state should be saved - """ - try: - game_state = game.get_game_state() + """ - # Ensure directory exists - os.makedirs(os.path.dirname(file_path), exist_ok=True) + return html_content + + def _render_board(self, game: TicTacToeGame) -> str: + """ + Render the game board as HTML cells. - with open(file_path, 'w') as f: - json.dump(game_state, f, indent=2) + Args: + game (TicTacToeGame): The current game instance - except Exception as e: - raise IOError(f"Failed to save game state: {str(e)}") + Returns: + str: HTML string representing the board cells + """ + board_html = "" + + for i in range(3): + for j in range(3): + player = game.get_board_state()[i][j] + cell_value = player.value if player != Player.EMPTY else "" + + # Determine if the game is over to disable further moves + is_game_over = game.get_game_status() != GameStatus.PLAYING + disabled_class = "disabled" if is_game_over and player == Player.EMPTY else "" + + board_html += f''' +
+ {cell_value} +
+ ''' + + return board_html -def load_game_state(file_path: str) -> Dict[str, Any]: +class GameEngine: """ - Load game state from a JSON file. + Main engine that coordinates the Tic Tac Toe game. - Args: - file_path: Path to the file containing game state - - Returns: - Dictionary containing loaded game state - - Raises: - FileNotFoundError: If the file does not exist - json.JSONDecodeError: If the file is not valid JSON + This class acts as the central coordinator, managing the game lifecycle, + user interaction, and state updates. """ - try: - with open(file_path, 'r') as f: - return json.load(f) - except FileNotFoundError: - raise FileNotFoundError(f"Game state file not found: {file_path}") - except json.JSONDecodeError: - raise json.JSONDecodeError("Invalid JSON in game state file", "", 0) - - -def main() -> None: - """Main function to demonstrate the Tic Tac Toe game.""" - # Create players - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - # Create game instance - game = TicTacToe(player1, player2) - - # Display initial state - print("Initial Game State:") - state = game.get_game_state() - print(json.dumps(state, indent=2)) - - # Example moves - try: - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(0, 1) # X plays at (0,1) - game.make_move(1, 0) # O plays at (1,0) - game.make_move(0, 2) # X plays at (0,2) - wins - - print("\nAfter example moves:") - state = game.get_game_state() - print(json.dumps(state, indent=2)) - - # Save the game state - save_game_state(game, "games/tic_tac_toe_game.json") - print("\nGame state saved successfully.") - - except Exception as e: - print(f"Error during game execution: {e}") - - -if __name__ == "__main__": - main() -Tic Tac Toe Game Implementation (Clean Version) - -Python backend game engine with optional HTML/CSS/JS generation. -""" - -from typing import List, Optional, Dict, Any -import json - - -# ----------------------------- -# Player -# ----------------------------- -class Player: - """Represents a Tic Tac Toe player.""" - - def __init__(self, symbol: str, name: str): - if symbol not in ("X", "O"): - raise ValueError("Symbol must be 'X' or 'O'") - self.symbol = symbol - self.name = name - - -# ----------------------------- -# Game Board -# ----------------------------- -class GameBoard: - """3x3 Tic Tac Toe board.""" - - def __init__(self): - self.size = 3 - self.board = [["" for _ in range(3)] for _ in range(3)] - - def get_cell(self, row: int, col: int) -> str: - self._validate_position(row, col) - return self.board[row][col] - - def set_cell(self, row: int, col: int, symbol: str) -> None: - self._validate_position(row, col) - - if symbol not in ("X", "O"): - raise ValueError("Invalid symbol") - - if self.board[row][col] != "": - raise ValueError("Cell already occupied") - - self.board[row][col] = symbol - - def clear_cell(self, row: int, col: int) -> None: - self._validate_position(row, col) - self.board[row][col] = "" - - def is_full(self) -> bool: - return all(cell != "" for row in self.board for cell in row) - - def reset(self) -> None: - self.board = [["" for _ in range(3)] for _ in range(3)] - - def to_dict(self) -> List[List[str]]: - return [row[:] for row in self.board] - - def _validate_position(self, row: int, col: int) -> None: - if not (0 <= row < 3 and 0 <= col < 3): - raise IndexError("Row and column must be between 0 and 2") - - -# ----------------------------- -# Win Checker -# ----------------------------- -class WinChecker: - """Utility class for win checking.""" - - @staticmethod - def check_winner(board: GameBoard) -> Optional[str]: - b = board.board - - lines = ( - # rows - b[0], b[1], b[2], - # columns - [b[0][0], b[1][0], b[2][0]], - [b[0][1], b[1][1], b[2][1]], - [b[0][2], b[1][2], b[2][2]], - # diagonals - [b[0][0], b[1][1], b[2][2]], - [b[0][2], b[1][1], b[2][0]], - ) - - for line in lines: - if line[0] and line.count(line[0]) == 3: - return line[0] - - return None - - -# ----------------------------- -# Game Engine -# ----------------------------- -class GameEngine: - """Main Tic Tac Toe game engine.""" - def __init__(self): - self.players = [ - Player("X", "Player X"), - Player("O", "Player O"), - ] - self.current_player_index = 0 - self.board = GameBoard() - self.winner: Optional[str] = None - self.game_over = False - self.move_history: List[Dict[str, Any]] = [] - - def current_player(self) -> Player: - return self.players[self.current_player_index] - - def make_move(self, row: int, col: int) -> Dict[str, Any]: - if self.game_over: - return {"success": False, "error": "Game is already over"} + def __init__(self) -> None: + """ + Initialize the game engine. + + Creates instances of the game controller and renderer. + """ + self.game = TicTacToeGame() + self.renderer = GameRenderer() + def play_move(self, row: int, col: int) -> Dict[str, Any]: + """ + Process a player move and return updated game state. + + Args: + row (int): The row index where the move is made + col (int): The column index where the move is made + + Returns: + Dict[str, Any]: Updated game state after the move + """ try: - symbol = self.current_player().symbol - self.board.set_cell(row, col, symbol) - - self.move_history.append( - {"row": row, "col": col, "symbol": symbol} - ) - - winner = WinChecker.check_winner(self.board) - if winner: - self.winner = winner - self.game_over = True - return self._response(f"{winner} wins!") - - if self.board.is_full(): - self.game_over = True - return self._response("Game ended in a draw") - - self._switch_player() - return self._response("Move successful") - - except Exception as e: - return {"success": False, "error": str(e)} - - def undo_move(self) -> bool: - if not self.move_history or self.game_over: - return False - - last = self.move_history.pop() - self.board.clear_cell(last["row"], last["col"]) - self._switch_player() - return True - - def reset_game(self) -> None: - self.board.reset() - self.current_player_index = 0 - self.winner = None - self.game_over = False - self.move_history.clear() - - def get_game_state(self) -> Dict[str, Any]: + success = self.game.make_move(row, col) + + if not success: + return { + "success": False, + "error": "Invalid move - cell already occupied", + "state": self.game.get_game_state() + } + + return { + "success": True, + "state": self.game.get_game_state() + } + + except ValueError as e: + return { + "success": False, + "error": str(e), + "state": self.game.get_game_state() + } + + def reset(self) -> Dict[str, Any]: + """ + Reset the game to initial state. + + Returns: + Dict[str, Any]: Game state after reset + """ + self.game.reset_game() return { - "board": self.board.to_dict(), - "current_player": self.current_player().name, - "winner": self.winner, - "game_over": self.game_over, - "moves": self.move_history, + "success": True, + "state": self.game.get_game_state() } - def _switch_player(self) -> None: - self.current_player_index = (self.current_player_index + 1) % 2 + def get_html(self) -> str: + """ + Generate complete HTML for the game. + + Returns: + str: Complete HTML page for the Tic Tac Toe game + """ + return self.renderer.render_html(self.game) - def _response(self, message: str) -> Dict[str, Any]: - return { - "success": True, - "message": message, - "state": self.get_game_state(), - } + def get_game_state(self) -> Dict[str, Any]: + """ + Get the current game state. + + Returns: + Dict[str, Any]: Current game state as dictionary + """ + return self.game.get_game_state() -# ----------------------------- -# Demo -# ----------------------------- -def main() -> None: - game = GameEngine() - print("Tic Tac Toe started\n") +# Example usage and testing functions +def create_sample_game() -> GameEngine: + """ + Create a sample game instance for demonstration. + + Returns: + GameEngine: A configured game engine instance + """ + return GameEngine() - print(json.dumps(game.make_move(0, 0), indent=2)) - print(json.dumps(game.make_move(1, 1), indent=2)) - print(json.dumps(game.make_move(0, 1), indent=2)) - print(json.dumps(game.make_move(2, 2), indent=2)) - print(json.dumps(game.make_move(0, 2), indent=2)) # X wins + +def test_game_logic() -> None: + """ + Test the core game logic with sample moves. + + This function demonstrates basic functionality of the Tic Tac Toe engine. + """ + game_engine = create_sample_game() + + # Test basic moves + result1 = game_engine.play_move(0, 0) + print(f"Move (0,0): {result1['success']}") + + result2 = game_engine.play_move(1, 1) + print(f"Move (1,1): {result2['success']}") + + # Test invalid move + result3 = game_engine.play_move(0, 0) + print(f"Move (0,0) again: {result3['success']}") + + # Test reset + reset_result = game_engine.reset() + print(f"Reset successful: {reset_result['success']}") + + # Print final state + state = game_engine.get_game_state() + print(f"Final game status: {state['game_status']}") if __name__ == "__main__": - main() + # Run basic tests + test_game_logic() + + # Create and display game HTML + engine = create_sample_game() + html_output = engine.get_html() + + # Save to file for demonstration + with open("tic_tac_toe.html", "w") as f: + f.write(html_output) + + print("Tic Tac Toe HTML generated successfully as 'tic_tac_toe.html'") diff --git a/test_crm_5_implementation.py b/test_crm_5_implementation.py index 97d1bc5..0a8b4f1 100644 --- a/test_crm_5_implementation.py +++ b/test_crm_5_implementation.py @@ -1,769 +1,326 @@ import pytest -from unittest.mock import patch, MagicMock -import os -import json - +from unittest.mock import patch from crm_5_implementation import ( - PlayerSymbol, - GameStatus, Player, - GameBoard, - TicTacToe, - create_html_template, - save_game_state, - load_game_state + GameStatus, + TicTacToeGame, + GameRenderer, + GameEngine ) -class TestPlayerSymbol: - """Test PlayerSymbol enumeration.""" - - def test_player_symbol_values(self): - """Test that PlayerSymbol has correct values.""" - assert PlayerSymbol.X.value == "X" - assert PlayerSymbol.O.value == "O" +@pytest.fixture +def tic_tac_toe_game(): + """Fixture to create a fresh TicTacToeGame instance.""" + return TicTacToeGame() -class TestPlayer: - """Test Player class.""" - - def test_player_creation(self): - """Test Player creation with valid parameters.""" - player = Player(PlayerSymbol.X, "Test Player") - assert player.symbol == PlayerSymbol.X - assert player.name == "Test Player" - - def test_player_equality(self): - """Test Player equality comparison.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.X, "Player X") - player3 = Player(PlayerSymbol.O, "Player O") - - assert player1 == player2 - assert player1 != player3 +@pytest.fixture +def game_renderer(): + """Fixture to create a fresh GameRenderer instance.""" + return GameRenderer() + +@pytest.fixture +def game_engine(): + """Fixture to create a fresh GameEngine instance.""" + return GameEngine() -class TestGameBoard: - """Test GameBoard class.""" + +class TestPlayerEnum: + """Test Player enumeration values.""" - def test_board_initialization(self): - """Test board initialization with default size.""" - board = GameBoard() - assert board.size == 3 - assert len(board.board) == 3 - assert all(len(row) == 3 for row in board.board) - assert all(cell is None for row in board.board for cell in row) + def test_player_enum_values(self): + """Test that Player enum has correct values.""" + assert Player.X.value == "X" + assert Player.O.value == "O" + assert Player.EMPTY.value == " " + + +class TestGameStatusEnum: + """Test GameStatus enumeration values.""" - def test_board_initialization_custom_size(self): - """Test board initialization with custom size.""" - board = GameBoard(5) - assert board.size == 5 - assert len(board.board) == 5 - assert all(len(row) == 5 for row in board.board) + def test_game_status_enum_values(self): + """Test that GameStatus enum has correct values.""" + assert GameStatus.PLAYING.value == "playing" + assert GameStatus.X_WON.value == "x_won" + assert GameStatus.O_WON.value == "o_won" + assert GameStatus.DRAW.value == "draw" + + +class TestTicTacToeGame: + """Test TicTacToeGame class functionality.""" - def test_make_move_valid(self): + def test_initialization(self, tic_tac_toe_game): + """Test game initialization.""" + assert tic_tac_toe_game.board == [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ] + assert tic_tac_toe_game.current_player == Player.X + assert tic_tac_toe_game.game_status == GameStatus.PLAYING + assert tic_tac_toe_game.move_history == [] + + def test_make_move_valid(self, tic_tac_toe_game): """Test making a valid move.""" - board = GameBoard() - result = board.make_move(0, 0, PlayerSymbol.X) - + result = tic_tac_toe_game.make_move(0, 0) assert result is True - assert board.get_cell(0, 0) == PlayerSymbol.X + assert tic_tac_toe_game.board[0][0] == Player.X + assert tic_tac_toe_game.current_player == Player.O + assert tic_tac_toe_game.move_history == [(0, 0)] - def test_make_move_invalid_coordinates(self): + def test_make_move_invalid_coordinates(self, tic_tac_toe_game): """Test making a move with invalid coordinates.""" - board = GameBoard() + with pytest.raises(ValueError): + tic_tac_toe_game.make_move(-1, 0) - with pytest.raises(IndexError): - board.make_move(5, 5, PlayerSymbol.X) + with pytest.raises(ValueError): + tic_tac_toe_game.make_move(0, 3) - def test_make_move_occupied_cell(self): + def test_make_move_occupied_cell(self, tic_tac_toe_game): """Test making a move on an occupied cell.""" - board = GameBoard() - board.make_move(0, 0, PlayerSymbol.X) - result = board.make_move(0, 0, PlayerSymbol.O) + # Make first move + tic_tac_toe_game.make_move(0, 0) + # Try to make move on same cell + result = tic_tac_toe_game.make_move(0, 0) assert result is False - assert board.get_cell(0, 0) == PlayerSymbol.X - - def test_get_cell(self): - """Test getting cell value.""" - board = GameBoard() - board.make_move(1, 1, PlayerSymbol.O) + assert tic_tac_toe_game.board[0][0] == Player.X + + def test_check_game_status_x_wins_row(self, tic_tac_toe_game): + """Test checking game status when X wins by row.""" + # Make moves for X to win in first row + tic_tac_toe_game.make_move(0, 0) # X + tic_tac_toe_game.make_move(1, 0) # O + tic_tac_toe_game.make_move(0, 1) # X + tic_tac_toe_game.make_move(1, 1) # O + tic_tac_toe_game.make_move(0, 2) # X + + assert tic_tac_toe_game.game_status == GameStatus.X_WON + + def test_check_game_status_o_wins_column(self, tic_tac_toe_game): + """Test checking game status when O wins by column.""" + # Make moves for O to win in first column + tic_tac_toe_game.make_move(0, 0) # X + tic_tac_toe_game.make_move(1, 0) # O + tic_tac_toe_game.make_move(0, 1) # X + tic_tac_toe_game.make_move(2, 0) # O + tic_tac_toe_game.make_move(0, 2) # X + tic_tac_toe_game.make_move(1, 1) # O + + assert tic_tac_toe_game.game_status == GameStatus.O_WON + + def test_check_game_status_x_wins_diagonal(self, tic_tac_toe_game): + """Test checking game status when X wins by diagonal.""" + # Make moves for X to win in main diagonal + tic_tac_toe_game.make_move(0, 0) # X + tic_tac_toe_game.make_move(1, 0) # O + tic_tac_toe_game.make_move(1, 1) # X + tic_tac_toe_game.make_move(0, 2) # O + tic_tac_toe_game.make_move(2, 2) # X + + assert tic_tac_toe_game.game_status == GameStatus.X_WON + + def test_check_game_status_draw(self, tic_tac_toe_game): + """Test checking game status when game is a draw.""" + # Fill board with alternating moves to create draw + moves = [ + (0, 0), (1, 1), (0, 1), (1, 0), (0, 2), + (2, 0), (2, 1), (2, 2), (1, 2) + ] - assert board.get_cell(0, 0) is None - assert board.get_cell(1, 1) == PlayerSymbol.O - - def test_is_full_empty_board(self): - """Test is_full on empty board.""" - board = GameBoard() - assert board.is_full() is False - - def test_is_full_full_board(self): - """Test is_full on full board.""" - board = GameBoard() - for i in range(3): - for j in range(3): - board.make_move(i, j, PlayerSymbol.X) - - assert board.is_full() is True - - def test_get_empty_cells(self): - """Test getting empty cells.""" - board = GameBoard() - board.make_move(0, 0, PlayerSymbol.X) - - empty_cells = board.get_empty_cells() - assert len(empty_cells) == 8 - assert (0, 0) not in empty_cells - - def test_reset_board(self): - """Test resetting the board.""" - board = GameBoard() - board.make_move(0, 0, PlayerSymbol.X) - board.reset() - - assert board.is_full() is False - assert all(cell is None for row in board.board for cell in row) - - -class TestTicTacToe: - """Test TicTacToe class.""" - - def test_game_initialization(self): - """Test game initialization.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) + for i, move in enumerate(moves): + if i % 2 == 0: + tic_tac_toe_game.make_move(move[0], move[1]) # X + else: + tic_tac_toe_game.make_move(move[0], move[1]) # O - assert game.player1 == player1 - assert game.player2 == player2 - assert game.current_player == player1 - assert game.status == GameStatus.PLAYING - assert game.winner is None - assert len(game.move_history) == 0 + assert tic_tac_toe_game.game_status == GameStatus.DRAW - def test_game_initialization_custom_size(self): - """Test game initialization with custom board size.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2, 5) + def test_reset_game(self, tic_tac_toe_game): + """Test resetting the game.""" + # Make some moves + tic_tac_toe_game.make_move(0, 0) + tic_tac_toe_game.make_move(1, 1) + + # Reset game + tic_tac_toe_game.reset_game() + + assert tic_tac_toe_game.board == [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ] + assert tic_tac_toe_game.current_player == Player.X + assert tic_tac_toe_game.game_status == GameStatus.PLAYING + assert tic_tac_toe_game.move_history == [] + + def test_get_board_state(self, tic_tac_toe_game): + """Test getting board state.""" + # Make a move + tic_tac_toe_game.make_move(0, 0) - assert game.board.size == 5 + board_state = tic_tac_toe_game.get_board_state() + assert board_state[0][0] == Player.X + assert board_state[1][0] == Player.EMPTY - def test_make_move_valid(self): - """Test making a valid move.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) + def test_get_current_player(self, tic_tac_toe_game): + """Test getting current player.""" + assert tic_tac_toe_game.get_current_player() == Player.X - result = game.make_move(0, 0) - - assert result is True - assert game.current_player == player2 - assert len(game.move_history) == 1 - assert game.move_history[0] == (0, 0, PlayerSymbol.X) + # Make a move + tic_tac_toe_game.make_move(0, 0) + assert tic_tac_toe_game.get_current_player() == Player.O - def test_make_move_invalid_game_ended(self): - """Test making a move when game has ended.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) + def test_get_game_status(self, tic_tac_toe_game): + """Test getting game status.""" + assert tic_tac_toe_game.get_game_status() == GameStatus.PLAYING - # Make moves to end the game - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(0, 1) # X plays at (0,1) - game.make_move(1, 0) # O plays at (1,0) - game.make_move(0, 2) # X plays at (0,2) - wins + # Make moves to win + tic_tac_toe_game.make_move(0, 0) + tic_tac_toe_game.make_move(1, 0) + tic_tac_toe_game.make_move(0, 1) + tic_tac_toe_game.make_move(1, 1) + tic_tac_toe_game.make_move(0, 2) - with pytest.raises(Exception): # Should raise an exception or handle properly - game.make_move(2, 2) + assert tic_tac_toe_game.get_game_status() == GameStatus.X_WON - def test_check_win_row(self): - """Test win condition checking for row.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) + def test_get_move_history(self, tic_tac_toe_game): + """Test getting move history.""" + # Make some moves + tic_tac_toe_game.make_move(0, 0) + tic_tac_toe_game.make_move(1, 1) - # Make moves to create a winning row - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(0, 1) # X plays at (0,1) - game.make_move(1, 0) # O plays at (1,0) - result = game.make_move(0, 2) # X plays at (0,2) - wins - - assert result is True - assert game.status == GameStatus.PLAYING # Should still be playing until next move + history = tic_tac_toe_game.get_move_history() + assert history == [(0, 0), (1, 1)] - def test_check_win_column(self): - """Test win condition checking for column.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) + def test_get_winner(self, tic_tac_toe_game): + """Test getting winner.""" + assert tic_tac_toe_game.get_winner() is None - # Make moves to create a winning column - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(1, 0) # X plays at (1,0) - game.make_move(2, 1) # O plays at (2,1) - result = game.make_move(2, 0) # X plays at (2,0) - wins + # Make moves to win + tic_tac_toe_game.make_move(0, 0) + tic_tac_toe_game.make_move(1, 0) + tic_tac_toe_game.make_move(0, 1) + tic_tac_toe_game.make_move(1, 1) + tic_tac_toe_game.make_move(0, 2) - assert result is True + assert tic_tac_toe_game.get_winner() == Player.X - def test_check_win_diagonal(self): - """Test win condition checking for diagonal.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) + def test_get_game_state(self, tic_tac_toe_game): + """Test getting complete game state.""" + state = tic_tac_toe_game.get_game_state() - # Make moves to create a winning diagonal - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(1, 0) # X plays at (1,0) - game.make_move(2, 2) # O plays at (2,2) - result = game.make_move(2, 0) # X plays at (2,0) - wins - - assert result is True + assert state["board"] == [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ] + assert state["current_player"] == "X" + assert state["game_status"] == "playing" + assert state["winner"] is None + assert state["move_history"] == [] + + +class TestGameRenderer: + """Test GameRenderer class functionality.""" - def test_get_game_state(self): - """Test getting game state.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - state = game.get_game_state() - - assert isinstance(state, dict) - assert "current_player_symbol" in state - assert "board" in state - assert "status" in state - assert "winner" in state + def test_initialization(self, game_renderer): + """Test renderer initialization.""" + assert game_renderer.game_state is None - def test_reset_game(self): - """Test resetting the game.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - game.make_move(0, 0) # X plays at (0,0) + def test_render_html(self, game_renderer, tic_tac_toe_game): + """Test rendering HTML.""" + html_content = game_renderer.render_html(tic_tac_toe_game) - # Reset the game - # Note: There's no explicit reset method in the original code, - # but we can simulate by creating a new game instance - - game2 = TicTacToe(player1, player2) - - assert game2.current_player == player1 - assert game2.status == GameStatus.PLAYING - assert len(game2.move_history) == 0 + assert "Tic Tac Toe" in html_content + assert "Current Player: X" in html_content + assert '
' in html_content - assert 'Tic Tac Toe' in html_content - assert '
' in html_content - - def test_generate_css(self): - """Test CSS generation.""" - ui = TicTacToeUI() - css_content = ui.generate_css() - assert 'body {' in css_content - assert '.cell {' in css_content - assert '@media' in css_content - - def test_generate_javascript(self): - """Test JavaScript generation.""" - ui = TicTacToeUI() - js_content = ui.generate_javascript() - assert 'class TicTacToeGame' in js_content - assert 'makeMove' in js_content - assert 'updateUI' in js_content - - -def test_create_tic_tac_toe_game(): - """Test creating complete game package.""" - result = create_tic_tac_toe_game() - assert isinstance(result, dict) - assert 'html' in result - assert 'css' in result - assert 'js' in result - assert len(result['html']) > 0 - assert len(result['css']) > 0 - assert len(result['js']) > 0 - - -def test_main_function(): - """Test main function runs without errors.""" - # This is mainly for code coverage and to ensure the function can run - # The actual content is tested in other tests - pass - - -class TestEdgeCases: - """Test edge cases for the game.""" - - def test_multiple_wins_same_row(self): - """Test that win is detected correctly even with multiple moves.""" - game = TicTacToeGame() - # Create a winning condition - game.make_move(0) # X - game.make_move(3) # O - game.make_move(1) # X - game.make_move(4) # O - result = game.make_move(2) # X - should win - assert result is True - assert game.game_over is True - assert game.winner == 'X' - - def test_full_board_draw(self): - """Test draw with specific board arrangement.""" - game = TicTacToeGame() - # Create a full board that should end in draw - moves = [0, 1, 2, 4, 3, 5, 7, 6, 8] # X, O, X, O, X, O, X, O, X - for i, move in enumerate(moves): - game.make_move(move) - assert game.game_over is True - assert game.winner == 'Draw' - - def test_empty_board(self): - """Test that initial board is empty.""" - game = TicTacToeGame() - assert all(cell == '' for cell in game.board) - - def test_invalid_move_after_win(self): - """Test making move after win.""" - game = TicTacToeGame() - # Create a win condition - game.make_move(0) # X - game.make_move(3) # O - game.make_move(1) # X - game.make_move(4) # O - game.make_move(2) # X - win - # Try to make another move after win - result = game.make_move(5) - assert result is False - - def test_multiple_resets(self): - """Test resetting multiple times.""" - game = TicTacToeGame() - game.make_move(0) - game.reset_game() - game.reset_game() # Second reset - assert game.board == [''] * 9 - assert game.current_player == 'X' - - def test_win_with_different_patterns(self): - """Test win detection with different patterns.""" - game = TicTacToeGame() - - # Test column win - game.make_move(0) # X - game.make_move(1) # O - game.make_move(3) # X - game.make_move(4) # O - result = game.make_move(6) # X - win column 0 - assert result is True - assert game.game_over is True - assert game.winner == 'X' - - # Reset and test diagonal win - game.reset_game() - game.make_move(0) # X - game.make_move(1) # O - game.make_move(4) # X - game.make_move(2) # O - result = game.make_move(8) # X - win diagonal - assert result is True - assert game.game_over is True - assert game.winner == 'X' + pytest.fail("test_game_logic function should not raise an exception")