"""Connect4 game state module.This module defines the core state representation for Connect4 games,including board representation, move tracking, and game status.Example: >>> from haive.games.connect4.state import Connect4State >>> from haive.games.connect4.models import Connect4Move >>> >>> # Initialize a new game >>> state = Connect4State.initialize() >>> state.board_string # Get string representation >>> >>> # Check game properties >>> state.is_column_full(3) # Check if column is full >>> state.get_next_row(3) # Get next available row in column"""fromtypingimportLiteralfrompydanticimportField,field_validatorfromhaive.games.connect4.modelsimportConnect4Movefromhaive.games.framework.base.stateimportGameState
[docs]classConnect4State(GameState):"""State representation for a Connect4 game. This class represents the complete state of a Connect4 game, including: - Board representation (6x7 grid) - Current player's turn - Game status and winner - Move history - Position analysis for both players The board is represented as a 6x7 grid of cells, where each cell can be: - None: Empty cell - "red": Red player's piece - "yellow": Yellow player's piece Attributes: board (list[list[str | None]]): 6x7 board representation (rows x columns) turn (Literal["red", "yellow"]): Current player's turn game_status (Literal["ongoing", "red_win", "yellow_win", "draw"]): Game status move_history (list[Connect4Move]): History of moves made in the game red_analysis (list[dict]): Analysis history for the red player yellow_analysis (list[dict]): Analysis history for the yellow player winner (Optional[str]): Winner of the game, if any error_message (Optional[str]): Error message from the last operation Examples: >>> state = Connect4State.initialize() >>> state.turn 'red' >>> state.is_column_full(3) False """board:list[list[str|None]]=Field(...,description="6x7 board representation (rows x columns)")turn:Literal["red","yellow"]=Field(...,description="Current player's turn")game_status:Literal["ongoing","red_win","yellow_win","draw"]=Field(default="ongoing",description="Status of the game")move_history:list[Connect4Move]=Field(default_factory=list,description="History of moves")red_analysis:list[dict]=Field(default_factory=list,description="Analysis history for red player")yellow_analysis:list[dict]=Field(default_factory=list,description="Analysis history for yellow player")winner:str|None=Field(default=None,description="Winner of the game, if any")error_message:str|None=Field(default=None,description="Error message if any")@propertydefboard_string(self)->str:"""Get a string representation of the board. Returns a formatted string representation of the current board state, with column and row indices, cell contents, and borders. Returns: str: String representation of the board Example: >>> state = Connect4State.initialize() >>> # state.board_string displays: 0 1 2 3 4 5 6 ------------- 0| | | | | | | | 1| | | | | | | | 2| | | | | | | | 3| | | | | | | | 4| | | | | | | | 5| | | | | | | | ------------- 0 1 2 3 4 5 6 """result=[]# Column headersresult.append(" 0 1 2 3 4 5 6")result.append(" -------------")# Board rows (top to bottom)fori,rowinenumerate(self.board):row_str=f"{i}|"forcellinrow:ifcellisNone:row_str+=" |"elifcell=="red":row_str+="R|"else:# yellowrow_str+="Y|"result.append(row_str)result.append(" -------------")result.append(" 0 1 2 3 4 5 6")return"\n".join(result)
[docs]defis_column_full(self,column:int)->bool:"""Check if a column is full. Args: column: Column index to check (0-6) Returns: bool: True if the column is full, False otherwise Example: >>> state = Connect4State.initialize() >>> state.is_column_full(3) False """returnself.board[0][column]isnotNone
[docs]defget_next_row(self,column:int)->int|None:"""Get the next available row in a column. Returns the row index where a piece would land if dropped in the specified column, or None if the column is full. Args: column: Column index (0-6) Returns: Optional[int]: Row index for the next piece, or None if column is full Example: >>> state = Connect4State.initialize() >>> state.get_next_row(3) 5 # Bottom row (gravity effect) """forrowinrange(len(self.board)-1,-1,-1):ifself.board[row][column]isNone:returnrowreturnNone
[docs]@field_validator("board")@classmethoddefvalidate_board_dimensions(cls,board):"""Validate board dimensions are 6x7. Args: board: Board to validate Returns: list[list[str | None]]: Validated board Raises: ValueError: If board dimensions are invalid """iflen(board)!=6:raiseValueError("Board must have 6 rows")ifany(len(row)!=7forrowinboard):raiseValueError("Each row must have 7 columns")returnboard
[docs]@classmethoddefinitialize(cls):"""Initialize a new Connect4 game. Creates a new Connect4 game state with an empty board, red player starting, and game status set to ongoing. Returns: Connect4State: A new game state Example: >>> state = Connect4State.initialize() >>> state.turn 'red' >>> state.game_status 'ongoing' """board=[[Nonefor_inrange(7)]for_inrange(6)]returncls(board=board,turn="red",game_status="ongoing",move_history=[])