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")