Fix checkmate outcome and save games
This commit is contained in:
@@ -158,7 +158,7 @@ class ChessBoard:
|
|||||||
|
|
||||||
def outcome(self) -> Outcome:
|
def outcome(self) -> Outcome:
|
||||||
if self.is_checkmate():
|
if self.is_checkmate():
|
||||||
return Outcome.from_color(self.turn)
|
return Outcome.from_color(self.turn.opposite)
|
||||||
if self.is_stalemate():
|
if self.is_stalemate():
|
||||||
return Outcome.DRAW
|
return Outcome.DRAW
|
||||||
if self.is_seventyfive_moves():
|
if self.is_seventyfive_moves():
|
||||||
|
|||||||
+81
-13
@@ -158,6 +158,57 @@ def _game_over_reason(board: ChessBoard) -> str:
|
|||||||
return "game over"
|
return "game over"
|
||||||
|
|
||||||
|
|
||||||
|
_TERMINATION_MAP = {
|
||||||
|
"checkmate": "checkmate",
|
||||||
|
"resignation": "resignation",
|
||||||
|
"timeout": "timeout",
|
||||||
|
"stalemate": "draw",
|
||||||
|
"draw agreed": "draw",
|
||||||
|
"75-move rule": "draw",
|
||||||
|
"fivefold repetition": "draw",
|
||||||
|
"opponent disconnected": "other",
|
||||||
|
"game over": "other",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _save_game(room: GameRoom, winner_color: Optional[str], reason: str = "other") -> Optional[int]:
|
||||||
|
"""Persist the finished game and return the saved game_id (or None on error)."""
|
||||||
|
if room.saved_game_id is not None:
|
||||||
|
return room.saved_game_id
|
||||||
|
try:
|
||||||
|
white_id = next(
|
||||||
|
(uid for sid, uid in [(room.p1_sid, room.p1_user_id), (room.p2_sid, room.p2_user_id)]
|
||||||
|
if room.color_by_sid.get(sid) == "w"),
|
||||||
|
room.p1_user_id,
|
||||||
|
)
|
||||||
|
black_id = next(
|
||||||
|
(uid for sid, uid in [(room.p1_sid, room.p1_user_id), (room.p2_sid, room.p2_user_id)]
|
||||||
|
if room.color_by_sid.get(sid) == "b"),
|
||||||
|
room.p2_user_id,
|
||||||
|
)
|
||||||
|
if black_id is None:
|
||||||
|
return None
|
||||||
|
termination = _TERMINATION_MAP.get(reason, "other")
|
||||||
|
room.ended_at = _timestamp_now()
|
||||||
|
game_id = save_finished_game(
|
||||||
|
white_player_id=white_id,
|
||||||
|
black_player_id=black_id,
|
||||||
|
final_fen=room.board.to_fen(),
|
||||||
|
termination=termination,
|
||||||
|
termination_detail=reason,
|
||||||
|
winner_color=winner_color,
|
||||||
|
move_history=room.move_history,
|
||||||
|
time_mode=room.time_mode,
|
||||||
|
started_at=room.started_at,
|
||||||
|
ended_at=room.ended_at,
|
||||||
|
)
|
||||||
|
room.saved_game_id = game_id
|
||||||
|
return game_id
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"[save_game] failed: {exc}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _emit_game_over(room: GameRoom, reason: str) -> None:
|
def _emit_game_over(room: GameRoom, reason: str) -> None:
|
||||||
room.completed = True
|
room.completed = True
|
||||||
room.game_active = False
|
room.game_active = False
|
||||||
@@ -165,23 +216,30 @@ def _emit_game_over(room: GameRoom, reason: str) -> None:
|
|||||||
outcome = room.board.outcome()
|
outcome = room.board.outcome()
|
||||||
p1_result = "draw"
|
p1_result = "draw"
|
||||||
p2_result = "draw"
|
p2_result = "draw"
|
||||||
|
winner_color_str: Optional[str] = None
|
||||||
|
|
||||||
if outcome == Outcome.WHITE_WIN:
|
if outcome == Outcome.WHITE_WIN:
|
||||||
winner_color = "w"
|
winner_color = "w"
|
||||||
else:
|
winner_color_str = "white"
|
||||||
|
elif outcome == Outcome.BLACK_WIN:
|
||||||
winner_color = "b"
|
winner_color = "b"
|
||||||
if room.color_by_sid.get(room.p1_sid) == winner_color:
|
winner_color_str = "black"
|
||||||
p1_result = "win"
|
|
||||||
p2_result = "loss"
|
|
||||||
else:
|
else:
|
||||||
p1_result = "loss"
|
winner_color = None
|
||||||
p2_result = "win"
|
|
||||||
|
|
||||||
|
if winner_color is not None:
|
||||||
|
if room.color_by_sid.get(room.p1_sid) == winner_color:
|
||||||
|
p1_result = "win"
|
||||||
|
p2_result = "loss"
|
||||||
|
else:
|
||||||
|
p1_result = "loss"
|
||||||
|
p2_result = "win"
|
||||||
|
|
||||||
|
game_id = _save_game(room, winner_color_str, reason)
|
||||||
|
|
||||||
emit("game_over", {"result": p1_result, "reason": reason}, to=room.p1_sid)
|
emit("game_over", {"result": p1_result, "reason": reason, "game_id": game_id}, to=room.p1_sid)
|
||||||
if room.p2_sid:
|
if room.p2_sid:
|
||||||
emit("game_over", {"result": p2_result, "reason": reason}, to=room.p2_sid)
|
emit("game_over", {"result": p2_result, "reason": reason, "game_id": game_id}, to=room.p2_sid)
|
||||||
|
|
||||||
|
|
||||||
def _emit_timeout(room: GameRoom, timed_out_color: str) -> None:
|
def _emit_timeout(room: GameRoom, timed_out_color: str) -> None:
|
||||||
@@ -193,9 +251,12 @@ def _emit_timeout(room: GameRoom, timed_out_color: str) -> None:
|
|||||||
p1_result = "loss" if p1_color == timed_out_color else "win"
|
p1_result = "loss" if p1_color == timed_out_color else "win"
|
||||||
p2_result = "win" if p1_result == "loss" else "loss"
|
p2_result = "win" if p1_result == "loss" else "loss"
|
||||||
|
|
||||||
sIO.emit("game_over", {"result": p1_result, "reason": reason}, to=room.p1_sid)
|
winner_color_str = "white" if timed_out_color == "b" else "black"
|
||||||
|
game_id = _save_game(room, winner_color_str, reason)
|
||||||
|
|
||||||
|
sIO.emit("game_over", {"result": p1_result, "reason": reason, "game_id": game_id}, to=room.p1_sid)
|
||||||
if room.p2_sid:
|
if room.p2_sid:
|
||||||
sIO.emit("game_over", {"result": p2_result, "reason": reason}, to=room.p2_sid)
|
sIO.emit("game_over", {"result": p2_result, "reason": reason, "game_id": game_id}, to=room.p2_sid)
|
||||||
|
|
||||||
|
|
||||||
def _start_game_if_ready(room: GameRoom) -> None:
|
def _start_game_if_ready(room: GameRoom) -> None:
|
||||||
@@ -221,6 +282,7 @@ def _start_game_if_ready(room: GameRoom) -> None:
|
|||||||
room.active_since = time.monotonic()
|
room.active_since = time.monotonic()
|
||||||
room.game_active = True
|
room.game_active = True
|
||||||
room.completed = False
|
room.completed = False
|
||||||
|
room.started_at = _timestamp_now()
|
||||||
|
|
||||||
p1_payload = {
|
p1_payload = {
|
||||||
"play_as": p1_color,
|
"play_as": p1_color,
|
||||||
@@ -385,6 +447,7 @@ def on_join_code_game(payload):
|
|||||||
_cleanup_room(previous.code)
|
_cleanup_room(previous.code)
|
||||||
|
|
||||||
room.p2_sid = sid
|
room.p2_sid = sid
|
||||||
|
room.p2_user_id = current_user.id
|
||||||
room.p2_name = current_user.username
|
room.p2_name = current_user.username
|
||||||
room.ready.setdefault(room.p1_sid, False)
|
room.ready.setdefault(room.p1_sid, False)
|
||||||
room.ready[sid] = False
|
room.ready[sid] = False
|
||||||
@@ -553,9 +616,13 @@ def on_request_resign(payload):
|
|||||||
reason = "resignation"
|
reason = "resignation"
|
||||||
|
|
||||||
other_sid = room.p2_sid if sid == room.p1_sid else room.p1_sid
|
other_sid = room.p2_sid if sid == room.p1_sid else room.p1_sid
|
||||||
emit("game_over", {"result": "loss", "reason": reason}, to=sid)
|
resigning_color = room.color_by_sid.get(sid)
|
||||||
|
winner_color_str = "black" if resigning_color == "w" else "white"
|
||||||
|
game_id = _save_game(room, winner_color_str, reason)
|
||||||
|
|
||||||
|
emit("game_over", {"result": "loss", "reason": reason, "game_id": game_id}, to=sid)
|
||||||
if other_sid:
|
if other_sid:
|
||||||
emit("game_over", {"result": "win", "reason": reason}, to=other_sid)
|
emit("game_over", {"result": "win", "reason": reason, "game_id": game_id}, to=other_sid)
|
||||||
|
|
||||||
|
|
||||||
@sIO.on("request_draw")
|
@sIO.on("request_draw")
|
||||||
@@ -577,7 +644,8 @@ def on_request_draw(payload):
|
|||||||
if payload.get("accepted") is True:
|
if payload.get("accepted") is True:
|
||||||
room.completed = True
|
room.completed = True
|
||||||
room.game_active = False
|
room.game_active = False
|
||||||
emit("game_over", {"result": "draw", "reason": "draw agreed"}, to=room.code)
|
game_id = _save_game(room, None, "draw agreed")
|
||||||
|
sIO.emit("game_over", {"result": "draw", "reason": "draw agreed", "game_id": game_id}, to=room.code)
|
||||||
return
|
return
|
||||||
|
|
||||||
other_sid = room.p2_sid if sid == room.p1_sid else room.p1_sid
|
other_sid = room.p2_sid if sid == room.p1_sid else room.p1_sid
|
||||||
|
|||||||
Reference in New Issue
Block a user