jasonlooked #1

Merged
eros merged 21 commits from jasonlooked into main 2026-07-11 10:13:09 -07:00
Showing only changes of commit 1f673b4eba - Show all commits

View File

@ -24,7 +24,13 @@ pub struct Game {
stones_red: u8, stones_red: u8,
Review

don't specify stone color here, just team1 or team2, allow generalizability to choose colors. for now we will default to red and yellow

don't specify stone color here, just team1 or team2, allow generalizability to choose colors. for now we will default to red and yellow
Review

instead of a score. call it scoreboard. for each end, specify team with hammer, score for team1, score for team2. show this scoreboard on the frontend. this allows the hammer and turn_team variables to go away.

instead of a score. call it scoreboard. for each end, specify team with hammer, score for team1, score for team2. show this scoreboard on the frontend. this allows the hammer and turn_team variables to go away.
stones_yellow: u8, stones_yellow: u8,
last_end_scored: Option<(u8, i32, Option<Team>)>, last_end_scored: Option<(u8, i32, Option<Team>)>,
pub room_tx: Option<tokio::sync::broadcast::Sender<ServerMessage>>, }
pub struct ThrowOutcome {
pub trajectory: Vec<(f32, f32, f32)>,
pub end_scored: Option<ServerMessage>,
pub state_message: ServerMessage,
pub game_over: Option<ServerMessage>,
} }
impl Game { impl Game {
@ -42,43 +48,31 @@ impl Game {
stones_red: STONES_PER_TEAM, stones_red: STONES_PER_TEAM,
Review

on the frontend, use a skeumorphic HUD to display how many stones are left. for instance, at the start of the game, show 2 rows of 8 stones. a stone should be removed from the hud when it is thrown.

on the frontend, use a skeumorphic HUD to display how many stones are left. for instance, at the start of the game, show 2 rows of 8 stones. a stone should be removed from the hud when it is thrown.
stones_yellow: STONES_PER_TEAM, stones_yellow: STONES_PER_TEAM,
last_end_scored: None, last_end_scored: None,
room_tx: None,
} }
} }
pub fn add_player(&mut self, id: String, preferred: Option<Team>) -> Option<Team> { pub fn add_player(&mut self, id: String, preferred: Option<Team>) -> Option<Team> {
let team = match preferred { let team = preferred
Some(Team::Red) if self.red.is_none() => Some(Team::Red), .filter(|t| self.slot_for(t).is_none())
Some(Team::Yellow) if self.yellow.is_none() => Some(Team::Yellow), .or_else(|| self.first_open_team())?;
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
}
}
};
if let Some(t) = team { let player = Player { id, team, connected: true };
let player = Player { id, team: t, connected: true }; *self.slot_for(&team) = Some(player);
match t { Some(team)
Team::Red => self.red = Some(player),
Team::Yellow => self.yellow = Some(player),
} }
Some(t)
fn slot_for(&mut self, team: &Team) -> &mut Option<Player> {
match team {
Team::Red => &mut self.red,
Team::Yellow => &mut self.yellow,
}
}
fn first_open_team(&self) -> Option<Team> {
if self.red.is_none() {
Some(Team::Red)
} else if self.yellow.is_none() {
Some(Team::Yellow)
} else { } else {
None None
} }
@ -171,6 +165,34 @@ impl Game {
Ok(path) Ok(path)
} }
pub fn process_throw(
&mut self,
player_id: &str,
broom_x: f32,
broom_y: f32,
weight: u8,
curl: i8,
friction: f32,
) -> Result<ThrowOutcome, String> {
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,
Review

at the completion of an end, show a modal with the points scored, who has hammer next, and the updated scoreboard

at the completion of an end, show a modal with the points scored, who has hammer next, and the updated scoreboard
end_scored,
state_message,
game_over,
})
}
pub fn finish_simulation(&mut self) { pub fn finish_simulation(&mut self) {
if self.phase != GamePhase::Simulating { if self.phase != GamePhase::Simulating {
return; return;
@ -229,10 +251,10 @@ impl Game {
self.last_end_scored = Some((self.end, points, scoring_team)); self.last_end_scored = Some((self.end, points, scoring_team));
self.phase = GamePhase::EndComplete; 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<Team>) { fn advance_end_or_finish(&mut self) {
let tied = self.scores[0] == self.scores[1]; let tied = self.scores[0] == self.scores[1];
let after_regulation = self.end >= ENDS; let after_regulation = self.end >= ENDS;
@ -241,10 +263,6 @@ impl Game {
return; return;
} }
if after_regulation && tied {
// Extra end
}
self.end += 1; self.end += 1;
self.stones_red = STONES_PER_TEAM; self.stones_red = STONES_PER_TEAM;
self.stones_yellow = STONES_PER_TEAM; self.stones_yellow = STONES_PER_TEAM;
@ -311,13 +329,11 @@ pub struct Room {
impl Room { impl Room {
pub fn new(id: &str) -> Self { pub fn new(id: &str) -> Self {
let (tx, _) = tokio::sync::broadcast::channel(256); let (tx, _) = tokio::sync::broadcast::channel(256);
let mut room = Self { Self {
id: id.to_string(), id: id.to_string(),
game: Game::new(), game: Game::new(),
tx, tx,
}; }
room.game.room_tx = Some(room.tx.clone());
room
} }
} }