From 1f673b4ebab54e887319c2087568f1932e41c04b Mon Sep 17 00:00:00 2001 From: eros Date: Wed, 24 Jun 2026 10:46:12 -0700 Subject: [PATCH] refactor(backend): encapsulate throw lifecycle and clean dead code in game.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Game::process_throw to centralize throw→simulation→scoring→state flow. Remove dead room_tx field, simplify add_player, and rename end_ends_or_continue. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- backend/src/game.rs | 102 +++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 43 deletions(-) diff --git a/backend/src/game.rs b/backend/src/game.rs index a9ea466..84516a0 100644 --- a/backend/src/game.rs +++ b/backend/src/game.rs @@ -24,7 +24,13 @@ pub struct Game { stones_red: u8, stones_yellow: u8, last_end_scored: Option<(u8, i32, Option)>, - pub room_tx: Option>, +} + +pub struct ThrowOutcome { + pub trajectory: Vec<(f32, f32, f32)>, + pub end_scored: Option, + pub state_message: ServerMessage, + pub game_over: Option, } impl Game { @@ -42,43 +48,31 @@ impl Game { stones_red: STONES_PER_TEAM, stones_yellow: STONES_PER_TEAM, last_end_scored: None, - room_tx: None, } } pub fn add_player(&mut self, id: String, preferred: Option) -> Option { - let team = match preferred { - Some(Team::Red) if self.red.is_none() => Some(Team::Red), - Some(Team::Yellow) if self.yellow.is_none() => Some(Team::Yellow), - Some(_) => { - // Preferred is taken; fall back to the other free team. - if self.red.is_none() { - Some(Team::Red) - } else if self.yellow.is_none() { - Some(Team::Yellow) - } else { - None - } - } - None => { - // Legacy first-free logic. - if self.red.is_none() { - Some(Team::Red) - } else if self.yellow.is_none() { - Some(Team::Yellow) - } else { - None - } - } - }; + let team = preferred + .filter(|t| self.slot_for(t).is_none()) + .or_else(|| self.first_open_team())?; - if let Some(t) = team { - let player = Player { id, team: t, connected: true }; - match t { - Team::Red => self.red = Some(player), - Team::Yellow => self.yellow = Some(player), - } - Some(t) + let player = Player { id, team, connected: true }; + *self.slot_for(&team) = Some(player); + Some(team) + } + + fn slot_for(&mut self, team: &Team) -> &mut Option { + match team { + Team::Red => &mut self.red, + Team::Yellow => &mut self.yellow, + } + } + + fn first_open_team(&self) -> Option { + if self.red.is_none() { + Some(Team::Red) + } else if self.yellow.is_none() { + Some(Team::Yellow) } else { None } @@ -171,6 +165,34 @@ impl Game { Ok(path) } + pub fn process_throw( + &mut self, + player_id: &str, + broom_x: f32, + broom_y: f32, + weight: u8, + curl: i8, + friction: f32, + ) -> Result { + let trajectory = self.handle_throw(player_id, broom_x, broom_y, weight, curl, friction)?; + self.finish_simulation(); + + let end_scored = self.take_last_end_scored(); + let state_message = self.game_state_message(); + let game_over = if self.phase == GamePhase::GameComplete { + Some(self.game_over_message()) + } else { + None + }; + + Ok(ThrowOutcome { + trajectory, + end_scored, + state_message, + game_over, + }) + } + pub fn finish_simulation(&mut self) { if self.phase != GamePhase::Simulating { return; @@ -229,10 +251,10 @@ impl Game { self.last_end_scored = Some((self.end, points, scoring_team)); self.phase = GamePhase::EndComplete; - self.end_ends_or_continue(points, scoring_team); + self.advance_end_or_finish(); } - fn end_ends_or_continue(&mut self, _points: i32, _scoring_team: Option) { + fn advance_end_or_finish(&mut self) { let tied = self.scores[0] == self.scores[1]; let after_regulation = self.end >= ENDS; @@ -241,10 +263,6 @@ impl Game { return; } - if after_regulation && tied { - // Extra end - } - self.end += 1; self.stones_red = STONES_PER_TEAM; self.stones_yellow = STONES_PER_TEAM; @@ -311,13 +329,11 @@ pub struct Room { impl Room { pub fn new(id: &str) -> Self { let (tx, _) = tokio::sync::broadcast::channel(256); - let mut room = Self { + Self { id: id.to_string(), game: Game::new(), tx, - }; - room.game.room_tx = Some(room.tx.clone()); - room + } } }