Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions custom_components/beatify/game/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ def __init__(self, time_fn: Callable[[], float] | None = None) -> None:
self._intro_splash_deferred_song: dict | None = None
self._intro_splash_hass: HomeAssistant | None = None

def current_time(self) -> float:
"""Return the current timestamp from the injected clock."""
return self._now()

# ------------------------------------------------------------------
# Player registry delegation (keep public interface identical)
# ------------------------------------------------------------------
Expand Down Expand Up @@ -996,6 +1000,11 @@ async def _trigger_early_reveal(self) -> None:
await self._end_round_unlocked()
_LOGGER.info("Early reveal complete - phase now %s", self.phase.value)

async def trigger_early_reveal_if_complete(self) -> None:
"""Trigger early reveal if the round is playing and all guesses are in."""
if self.phase == GamePhase.PLAYING and self.check_all_guesses_complete():
await self._trigger_early_reveal()
Comment on lines +1003 to +1006
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The refactored trigger_early_reveal_if_complete method loses the observability previously present in websocket.py. Specifically, the all_complete status is no longer logged, making it harder to debug why an early reveal might not have triggered. Moving the debug logging into this method ensures all call sites (submit, artist guess, movie guess) benefit from consistent observability without duplicating logic.

Suggested change
async def trigger_early_reveal_if_complete(self) -> None:
"""Trigger early reveal if the round is playing and all guesses are in."""
if self.phase == GamePhase.PLAYING and self.check_all_guesses_complete():
await self._trigger_early_reveal()
async def trigger_early_reveal_if_complete(self) -> None:
"""Trigger early reveal if the round is playing and all guesses are in."""
if self.phase != GamePhase.PLAYING:
return
all_complete = self.check_all_guesses_complete()
_LOGGER.debug(
"Early reveal check: phase=%s, all_complete=%s",
self.phase.value,
all_complete,
)
if all_complete:
await self._trigger_early_reveal()


def set_round_end_callback(self, callback: Callable[[], Awaitable[None]]) -> None:
"""
Set callback to invoke when round ends (for broadcasting).
Expand Down
37 changes: 13 additions & 24 deletions custom_components/beatify/server/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -793,8 +793,8 @@ async def _handle_submit(
bet = data.get("bet", False)
player.bet = bool(bet)

# Record submission — Issue #419: use game_state._now() for clock consistency
submission_time = game_state._now()
# Record submission — Issue #419: use game_state.current_time() for clock consistency
submission_time = game_state.current_time()
player.submit_guess(year, submission_time)

# Send acknowledgment
Expand All @@ -809,16 +809,13 @@ async def _handle_submit(
await self.broadcast_state()

# Story 20.9: Check for early reveal when all guesses are complete
# Note: _trigger_early_reveal() calls end_round() which broadcasts via callback
all_complete = game_state.check_all_guesses_complete()
# Note: trigger_early_reveal_if_complete() calls end_round() which broadcasts via callback
_LOGGER.debug(
"Early reveal check: phase=%s, all_complete=%s, artist_challenge=%s",
"Early reveal check: phase=%s, artist_challenge=%s",
game_state.phase.value,
all_complete,
game_state.artist_challenge_enabled,
)
if game_state.phase == GamePhase.PLAYING and all_complete:
await game_state._trigger_early_reveal()
await game_state.trigger_early_reveal_if_complete()
Comment on lines 813 to +818
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This debug log is now redundant and incomplete compared to the original implementation, as it no longer includes the all_complete status. Since the logic has been encapsulated into GameState.trigger_early_reveal_if_complete(), the logging should also be moved there to provide consistent observability across all guess types while keeping the websocket handler clean.

        await game_state.trigger_early_reveal_if_complete()


_LOGGER.info(
"Player %s submitted guess: %d at %.2f", player.name, year, submission_time
Expand Down Expand Up @@ -1163,8 +1160,8 @@ async def _handle_artist_guess(
)
return

# Submit guess — Issue #419: use game_state._now() for clock consistency
guess_time = game_state._now()
# Submit guess — Issue #419: use game_state.current_time() for clock consistency
guess_time = game_state.current_time()
result = game_state.submit_artist_guess(player.name, artist, guess_time)

# Story 20.9: Track that player has made an artist guess
Expand All @@ -1190,12 +1187,8 @@ async def _handle_artist_guess(
await self.broadcast_state()

# Story 20.9: Check for early reveal when all guesses are complete
# Note: _trigger_early_reveal() calls end_round() which broadcasts via callback
if (
game_state.phase == GamePhase.PLAYING
and game_state.check_all_guesses_complete()
):
await game_state._trigger_early_reveal()
# Note: trigger_early_reveal_if_complete() calls end_round() which broadcasts via callback
await game_state.trigger_early_reveal_if_complete()

_LOGGER.debug(
"Artist guess from %s: '%s' -> correct=%s, first=%s",
Expand Down Expand Up @@ -1263,8 +1256,8 @@ async def _handle_movie_guess(
)
return

# Submit guess with server-side timing — Issue #419: use game_state._now()
guess_time = game_state._now()
# Submit guess with server-side timing — Issue #419: use game_state.current_time()
guess_time = game_state.current_time()
result = game_state.submit_movie_guess(player.name, movie, guess_time)

# Issue #28: Track that player has made a movie guess
Expand All @@ -1284,12 +1277,8 @@ async def _handle_movie_guess(
await ws.send_json(response)

# Issue #28: Check for early reveal when all guesses are complete
# Note: _trigger_early_reveal() calls end_round() which broadcasts via callback
if (
game_state.phase == GamePhase.PLAYING
and game_state.check_all_guesses_complete()
):
await game_state._trigger_early_reveal()
# Note: trigger_early_reveal_if_complete() calls end_round() which broadcasts via callback
await game_state.trigger_early_reveal_if_complete()

_LOGGER.debug(
"Movie guess from %s: '%s' -> correct=%s, rank=%s",
Expand Down
Loading