start on connect 4
This commit is contained in:
parent
4afcf8aa94
commit
db21ec3ce1
74
connect4.py
Normal file
74
connect4.py
Normal file
@ -0,0 +1,74 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as tp
|
||||
from dataclasses import dataclass
|
||||
from functools import cache
|
||||
|
||||
Player = tp.Literal["X", "O"]
|
||||
Winner = Player | tp.Literal["draw"]
|
||||
Space = Player | tp.Literal[" "]
|
||||
Board = tuple[Space, ...]
|
||||
Score = int
|
||||
|
||||
C4_COLS = 7
|
||||
C4_ROWS = 6
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class C4GameState:
|
||||
player: Player
|
||||
board: Board
|
||||
|
||||
def __str__(self) -> str:
|
||||
b = self.board
|
||||
return f"───┼───┼───┼───┼───┼───┼───\n".join(
|
||||
f" {b[i+0]} │ {b[i+1]} │ {b[i+2]} │ {b[i+3]} │ {b[i+4]} │ {b[i+5]} │ {b[i+6]}\n"
|
||||
for i in range(0, C4_COLS * C4_ROWS, C4_COLS)
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Move:
|
||||
col: int
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.col + 1)
|
||||
|
||||
|
||||
@cache
|
||||
def get_valid_moves(state: C4GameState) -> tp.Iterable[Move]:
|
||||
# NOTE: a slot must not be full if its top row is empty
|
||||
return tuple(Move(col) for col in range(C4_COLS) if state.board[col] == " ")
|
||||
|
||||
|
||||
@cache
|
||||
def apply_move(state: C4GameState, move: Move) -> C4GameState:
|
||||
new_player = "X" if state.player == "O" else "O"
|
||||
|
||||
# B)
|
||||
for i in range(1, C4_ROWS):
|
||||
idx_above = (i - 1) * C4_COLS + move.col
|
||||
idx_below = (i - 0) * C4_COLS + move.col
|
||||
if state.board[idx_below] != " ":
|
||||
idx = idx_above
|
||||
break
|
||||
else:
|
||||
# bottom
|
||||
idx = (C4_ROWS - 1) * C4_COLS + move.col
|
||||
|
||||
new_board = list(state.board)
|
||||
new_board[idx] = state.player
|
||||
new_board = tuple(new_board)
|
||||
|
||||
return C4GameState(player=new_player, board=new_board)
|
||||
|
||||
@cache
|
||||
def get_winner(state: C4GameState) -> Winner | None:
|
||||
pass
|
||||
|
||||
START = C4GameState(player="X", board=(" ",) * C4_COLS * C4_ROWS)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(START)
|
||||
|
||||
print(apply_move(START, Move(col=6)))
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
pdbp
|
18
tictactoe.py
18
tictactoe.py
@ -33,7 +33,7 @@ class TTTGameState:
|
||||
player: Player
|
||||
board: Board
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
b = self.board
|
||||
return (
|
||||
f" {b[0]} │ {b[1]} │ {b[2]}\n"
|
||||
@ -52,14 +52,14 @@ class Move:
|
||||
return str(self.position + 1)
|
||||
|
||||
|
||||
# @cache
|
||||
@cache
|
||||
def get_valid_moves(state: TTTGameState) -> tp.Iterable[Move]:
|
||||
return tuple(
|
||||
Move(position=i) for i, square in enumerate(state.board) if square == " "
|
||||
)
|
||||
|
||||
|
||||
# @cache
|
||||
@cache
|
||||
def apply_move(state: TTTGameState, move: Move) -> TTTGameState:
|
||||
new_player = "X" if state.player == "O" else "O"
|
||||
|
||||
@ -70,7 +70,7 @@ def apply_move(state: TTTGameState, move: Move) -> TTTGameState:
|
||||
return TTTGameState(player=new_player, board=new_board)
|
||||
|
||||
|
||||
# @cache
|
||||
@cache
|
||||
def get_winner(state: TTTGameState) -> Winner | None:
|
||||
# abc
|
||||
# def
|
||||
@ -103,18 +103,18 @@ def get_winner(state: TTTGameState) -> Winner | None:
|
||||
if not any(square == " " for square in state.board):
|
||||
return "draw"
|
||||
|
||||
# no winner
|
||||
# no winner yet
|
||||
return None
|
||||
|
||||
|
||||
# @cache
|
||||
@cache
|
||||
def get_next_states(state: TTTGameState) -> tuple[tuple[Move, TTTGameState], ...]:
|
||||
assert get_winner(state) is None, "should not be called if game ended"
|
||||
return tuple((move, apply_move(state, move)) for move in get_valid_moves(state))
|
||||
|
||||
|
||||
# @cache
|
||||
@util.count_calls
|
||||
@cache
|
||||
# @util.count_calls
|
||||
def get_score(target: Player, state: TTTGameState) -> Score:
|
||||
winner = get_winner(state)
|
||||
if winner == target:
|
||||
@ -132,7 +132,7 @@ def get_score(target: Player, state: TTTGameState) -> Score:
|
||||
return score
|
||||
|
||||
|
||||
# @cache
|
||||
@cache
|
||||
def get_scored_moves(
|
||||
target: Player, state: TTTGameState
|
||||
) -> tuple[tuple[Score, Move], ...]:
|
||||
|
Loading…
Reference in New Issue
Block a user