DevilsDozen / src /database /game_state.py
legomaheggo's picture
feat: Add Alien Invasion game mode with Martian Dice mechanics
2e67341
"""
Devil's Dozen - Game State Manager
CRUD operations for the `game_state` table.
"""
from supabase import Client
from src.database.models import GameState
class GameStateManager:
"""Manages active game state in Supabase."""
def __init__(self, client: Client) -> None:
self.client = client
self.table = client.table("game_state")
def create(self, lobby_id: str) -> GameState:
"""Initialize game state for a lobby."""
data = (
self.table
.insert({"lobby_id": lobby_id})
.execute()
)
return GameState.model_validate(data.data[0])
def get(self, lobby_id: str) -> GameState | None:
"""Get the current game state for a lobby."""
data = (
self.table
.select("*")
.eq("lobby_id", lobby_id)
.execute()
)
if data.data:
return GameState.model_validate(data.data[0])
return None
def update(
self,
lobby_id: str,
*,
active_dice: list[int] | None = None,
held_indices: list[int] | None = None,
turn_score: int | None = None,
is_bust: bool | None = None,
roll_count: int | None = None,
tier: int | None = None,
previous_dice: list[int] | None = None,
) -> GameState:
"""Update game state fields. Only provided fields are changed."""
updates: dict = {}
if active_dice is not None:
updates["active_dice"] = active_dice
if held_indices is not None:
updates["held_indices"] = held_indices
if turn_score is not None:
updates["turn_score"] = turn_score
if is_bust is not None:
updates["is_bust"] = is_bust
if roll_count is not None:
updates["roll_count"] = roll_count
if tier is not None:
updates["tier"] = tier
if previous_dice is not None:
updates["previous_dice"] = previous_dice
if not updates:
return self.get(lobby_id) # type: ignore[return-value]
data = (
self.table
.update(updates)
.eq("lobby_id", lobby_id)
.execute()
)
return GameState.model_validate(data.data[0])
def reset_turn(self, lobby_id: str) -> GameState:
"""Reset state for a new turn."""
data = (
self.table
.update({
"active_dice": [],
"held_indices": [],
"turn_score": 0,
"is_bust": False,
"roll_count": 0,
"previous_dice": [],
# Reset Alien Invasion fields
"tanks_count": 0,
"death_rays_count": 0,
"earthlings_count": 0,
"selected_earthling_types": [],
})
.eq("lobby_id", lobby_id)
.execute()
)
return GameState.model_validate(data.data[0])
def update_knucklebones(
self,
lobby_id: str,
**kwargs,
) -> GameState:
"""
Update Knucklebones-specific game state fields.
Args:
lobby_id: The lobby to update
player1_grid: Player 1's 3x3 grid (dict with "columns" key)
player2_grid: Player 2's 3x3 grid (dict with "columns" key)
current_die_value: Currently rolled die value (1-6) or None
Returns:
Updated GameState
"""
updates: dict = {}
# Use kwargs to distinguish "not provided" from "provided as None"
if "player1_grid" in kwargs:
updates["player1_grid"] = kwargs["player1_grid"]
if "player2_grid" in kwargs:
updates["player2_grid"] = kwargs["player2_grid"]
if "current_die_value" in kwargs:
updates["current_die_value"] = kwargs["current_die_value"]
if not updates:
return self.get(lobby_id) # type: ignore[return-value]
data = (
self.table
.update(updates)
.eq("lobby_id", lobby_id)
.execute()
)
return GameState.model_validate(data.data[0])
def reset_knucklebones(self, lobby_id: str) -> GameState:
"""
Reset Knucklebones state for a new game.
Args:
lobby_id: The lobby to reset
Returns:
Reset GameState
"""
data = (
self.table
.update({
"player1_grid": {"columns": [[], [], []]},
"player2_grid": {"columns": [[], [], []]},
"current_die_value": None,
})
.eq("lobby_id", lobby_id)
.execute()
)
return GameState.model_validate(data.data[0])
def update_alien_invasion(
self,
lobby_id: str,
**kwargs,
) -> GameState:
"""
Update Alien Invasion-specific game state fields.
Args:
lobby_id: The lobby to update
tanks_count: Number of tanks collected
death_rays_count: Number of death rays collected
earthlings_count: Number of earthlings collected
selected_earthling_types: List of earthling type names selected
Returns:
Updated GameState
"""
updates: dict = {}
# Use kwargs to distinguish "not provided" from "provided as None"
if "tanks_count" in kwargs:
updates["tanks_count"] = kwargs["tanks_count"]
if "death_rays_count" in kwargs:
updates["death_rays_count"] = kwargs["death_rays_count"]
if "earthlings_count" in kwargs:
updates["earthlings_count"] = kwargs["earthlings_count"]
if "selected_earthling_types" in kwargs:
updates["selected_earthling_types"] = kwargs["selected_earthling_types"]
if not updates:
return self.get(lobby_id) # type: ignore[return-value]
data = (
self.table
.update(updates)
.eq("lobby_id", lobby_id)
.execute()
)
return GameState.model_validate(data.data[0])
def delete(self, lobby_id: str) -> None:
"""Delete game state for a lobby."""
self.table.delete().eq("lobby_id", lobby_id).execute()