introduced internal chess engine, fixed bugs
This commit is contained in:
+77
-31
@@ -6,7 +6,13 @@ from datetime import datetime
|
||||
from threading import Lock
|
||||
from typing import Optional
|
||||
|
||||
from chess_sim.game_board import ChessBoard, Outcome, Color
|
||||
from app.chess_sim.game_board import (
|
||||
ChessBoard,
|
||||
Outcome,
|
||||
Color,
|
||||
BoardMove,
|
||||
PieceType,
|
||||
)
|
||||
from flask_login import current_user
|
||||
from flask import request
|
||||
from flask_socketio import emit, join_room, leave_room
|
||||
@@ -27,7 +33,7 @@ class GameRoom:
|
||||
p2_user_id: Optional[int] = None
|
||||
p2_name: Optional[str] = None
|
||||
ready: dict[str, bool] = field(default_factory=dict)
|
||||
board: ChessBoard = ChessBoard.init_default()
|
||||
board: ChessBoard = field(default_factory=ChessBoard.init_default)
|
||||
color_by_sid: dict[str, str] = field(default_factory=dict)
|
||||
initial_ms: int = 600000
|
||||
increment_ms: int = 0
|
||||
@@ -160,15 +166,18 @@ def _emit_game_over(room: GameRoom, reason: str) -> None:
|
||||
p1_result = "draw"
|
||||
p2_result = "draw"
|
||||
|
||||
if outcome == Outcome.WHITE_WIN:
|
||||
winner_color = "w"
|
||||
else:
|
||||
winner_color = "b"
|
||||
if room.color_by_sid.get(room.p1_sid) == winner_color:
|
||||
p1_result = "win"
|
||||
p2_result = "loss"
|
||||
else:
|
||||
p1_result = "loss"
|
||||
p2_result = "win"
|
||||
|
||||
|
||||
if outcome and outcome.winner is not None:
|
||||
winner_color = "w" if outcome.winner == chess.WHITE else "b"
|
||||
if room.color_by_sid.get(room.p1_sid) == winner_color:
|
||||
p1_result = "win"
|
||||
p2_result = "loss"
|
||||
else:
|
||||
p1_result = "loss"
|
||||
p2_result = "win"
|
||||
|
||||
emit("game_over", {"result": p1_result, "reason": reason}, to=room.p1_sid)
|
||||
if room.p2_sid:
|
||||
@@ -296,8 +305,7 @@ def on_disconnect():
|
||||
room.p2_name = None
|
||||
room.ready.pop(sid, None)
|
||||
room.color_by_sid.pop(sid, None)
|
||||
# initialize a new chessboard???
|
||||
room.board = chess.Board()
|
||||
room.board = ChessBoard.init_default()
|
||||
room.ready[room.p1_sid] = False
|
||||
room.game_active = False
|
||||
room.completed = False
|
||||
@@ -307,6 +315,18 @@ def on_disconnect():
|
||||
emit("p2_connected", {"p2_name": None, "ready": False}, to=other_sid)
|
||||
|
||||
|
||||
def _make_room(sid: str, play_as: str, time_mode: str) -> GameRoom:
|
||||
return GameRoom(
|
||||
code="", # caller will fill in the actual code
|
||||
p1_sid=sid,
|
||||
p1_user_id=current_user.id,
|
||||
p1_name=current_user.username,
|
||||
p1_pref=play_as,
|
||||
time_mode=time_mode,
|
||||
ready={sid: False},
|
||||
)
|
||||
|
||||
|
||||
@sIO.on("create_code_game")
|
||||
def on_create_code_game(payload):
|
||||
ok, err = validate_client_event("create_code_game", payload)
|
||||
@@ -324,14 +344,9 @@ def on_create_code_game(payload):
|
||||
_cleanup_room(existing.code)
|
||||
|
||||
code = _new_code()
|
||||
room = GameRoom(
|
||||
code=code,
|
||||
p1_sid=sid,
|
||||
p1_name=current_user.username,
|
||||
p1_pref=payload["play_as"],
|
||||
time_mode=payload["time_mode"],
|
||||
ready={sid: False},
|
||||
)
|
||||
room = _make_room(sid, payload["play_as"], payload["time_mode"])
|
||||
room.code = code # assign after generation so constructor stays pure
|
||||
|
||||
games_by_code[code] = room
|
||||
code_by_sid[sid] = code
|
||||
|
||||
@@ -410,6 +425,31 @@ def on_user_ready(payload):
|
||||
_start_game_if_ready(room)
|
||||
|
||||
|
||||
# helpers for converting client payloads to engine moves and back
|
||||
|
||||
def _payload_to_move(payload: dict) -> BoardMove:
|
||||
from_sq = ChessBoard.algebraic_to_pos(payload["from_square"])
|
||||
to_sq = ChessBoard.algebraic_to_pos(payload["to_square"])
|
||||
promotion = payload.get("promotion")
|
||||
prom_piece: Optional[PieceType] = None
|
||||
if promotion:
|
||||
mapping = {
|
||||
"q": PieceType.QUEEN,
|
||||
"r": PieceType.ROOK,
|
||||
"b": PieceType.BISHOP,
|
||||
"n": PieceType.KNIGHT,
|
||||
}
|
||||
prom_piece = mapping.get(promotion.lower())
|
||||
if prom_piece is None:
|
||||
raise ValueError("invalid promotion piece")
|
||||
return BoardMove(from_sq, to_sq, promotion_piece=prom_piece)
|
||||
|
||||
|
||||
def _move_to_san(move: BoardMove) -> str:
|
||||
# todo can be improved, very simple
|
||||
return f"{ChessBoard.pos_to_algebraic(move.m_from)}{ChessBoard.pos_to_algebraic(move.m_to)}"
|
||||
|
||||
|
||||
@sIO.on("move_request")
|
||||
def on_move_request(payload):
|
||||
ok, err = validate_client_event("move_request", payload)
|
||||
@@ -440,24 +480,28 @@ def on_move_request(payload):
|
||||
emit("move_reject", {"reason": "not your turn"})
|
||||
return
|
||||
|
||||
uci = (
|
||||
f"{payload['from_square'].lower()}{payload['to_square'].lower()}"
|
||||
f"{payload.get('promotion', '')}"
|
||||
)
|
||||
|
||||
try:
|
||||
move = chess.Move.from_uci(uci)
|
||||
move = _payload_to_move(payload)
|
||||
except ValueError:
|
||||
print("rejecting move due to invalid move format")
|
||||
emit("move_reject", {"reason": "invalid move format"}, to=sid)
|
||||
return
|
||||
|
||||
if move not in room.board.legal_moves:
|
||||
current_color = room.board.turn
|
||||
legal = room.board.generate_moves(current_color)
|
||||
if move not in legal:
|
||||
print(f"current_color: {current_color}")
|
||||
print("rejecting move due to illegal move")
|
||||
print(f"move: {move}")
|
||||
room.board.print_legal_moves(current_color)
|
||||
emit("move_reject", {"reason": "illegal move"}, to=sid)
|
||||
return
|
||||
|
||||
mover_color = room.color_by_sid.get(sid)
|
||||
san = room.board.san(move)
|
||||
room.board.push(move)
|
||||
san = _move_to_san(move)
|
||||
# perform the move on our own engine
|
||||
room.board.make_move(move, current_color)
|
||||
room.move_history.append(san)
|
||||
|
||||
if mover_color == "w":
|
||||
room.white_ms += room.increment_ms
|
||||
@@ -472,7 +516,7 @@ def on_move_request(payload):
|
||||
"promotion": payload.get("promotion"),
|
||||
"san": san,
|
||||
"time_left_ms": room.white_ms if mover_color == "w" else room.black_ms,
|
||||
"fen": room.board.fen(),
|
||||
"fen": room.board.to_fen(),
|
||||
**_clock_payload(room),
|
||||
}
|
||||
|
||||
@@ -481,7 +525,9 @@ def on_move_request(payload):
|
||||
if other_sid:
|
||||
emit("user_move", move_payload, to=other_sid)
|
||||
|
||||
if room.board.is_game_over(claim_draw=True):
|
||||
|
||||
if room.board.outcome() != Outcome.NOT_FINISHED:
|
||||
print(f"ending a game due to outcome: { room.board.outcome()}")
|
||||
_emit_game_over(room, _game_over_reason(room.board))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user