jasonlooked #1
@ -12,8 +12,6 @@ pub enum GamePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
red: Option<Player>,
|
|
||||||
yellow: Option<Player>,
|
|
||||||
phase: GamePhase,
|
phase: GamePhase,
|
||||||
end: u8,
|
end: u8,
|
||||||
scores: [i32; 2],
|
scores: [i32; 2],
|
||||||
@ -27,7 +25,7 @@ pub struct Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ThrowOutcome {
|
pub struct ThrowOutcome {
|
||||||
pub trajectory: Vec<(f32, f32, f32)>,
|
pub trajectory: Vec<StoneTrajectory>,
|
||||||
pub end_scored: Option<ServerMessage>,
|
pub end_scored: Option<ServerMessage>,
|
||||||
pub state_message: ServerMessage,
|
pub state_message: ServerMessage,
|
||||||
pub game_over: Option<ServerMessage>,
|
pub game_over: Option<ServerMessage>,
|
||||||
@ -36,8 +34,6 @@ pub struct ThrowOutcome {
|
|||||||
impl Game {
|
impl Game {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
red: None,
|
|
||||||
yellow: None,
|
|
||||||
phase: GamePhase::Waiting,
|
phase: GamePhase::Waiting,
|
||||||
end: 1,
|
end: 1,
|
||||||
scores: [0, 0],
|
scores: [0, 0],
|
||||||
@ -51,57 +47,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|||||||
|
|
||||||
pub fn add_player(&mut self, id: String, preferred: Option<Team>) -> Option<Team> {
|
|
||||||
let team = preferred
|
|
||||||
.filter(|t| self.slot_for(t).is_none())
|
|
||||||
.or_else(|| self.first_open_team())?;
|
|
||||||
|
|
||||||
let player = Player { id, team, connected: true };
|
|
||||||
*self.slot_for(&team) = Some(player);
|
|
||||||
Some(team)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_player(&mut self, id: &str) -> Option<Team> {
|
|
||||||
if let Some(ref p) = self.red {
|
|
||||||
if p.id == id {
|
|
||||||
self.red = None;
|
|
||||||
return Some(Team::Red);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(ref p) = self.yellow {
|
|
||||||
if p.id == id {
|
|
||||||
self.yellow = None;
|
|
||||||
return Some(Team::Yellow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn can_start(&self) -> bool {
|
|
||||||
self.red.is_some() && self.yellow.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
if !self.can_start() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.hammer = if rand::random() { Team::Red } else { Team::Yellow };
|
self.hammer = if rand::random() { Team::Red } else { Team::Yellow };
|
||||||
self.turn_team = self.hammer.other();
|
self.turn_team = self.hammer.other();
|
||||||
self.phase = GamePhase::Playing;
|
self.phase = GamePhase::Playing;
|
||||||
@ -114,17 +60,16 @@ impl Game {
|
|||||||
self.active_stones.clear();
|
self.active_stones.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_player_id(&self) -> Option<&str> {
|
pub fn handle_throw(
|
||||||
let p = match self.turn_team {
|
&mut self,
|
||||||
Team::Red => self.red.as_ref()?,
|
team: Team,
|
||||||
Team::Yellow => self.yellow.as_ref()?,
|
broom_x: f32,
|
||||||
};
|
broom_y: f32,
|
||||||
Some(&p.id)
|
weight: u8,
|
||||||
}
|
curl: i8,
|
||||||
|
friction: f32,
|
||||||
pub fn handle_throw(&mut self, player_id: &str, broom_x: f32, broom_y: f32, weight: u8, curl: i8, friction: f32) -> Result<Vec<(f32, f32, f32)>, String> {
|
) -> Result<Vec<StoneTrajectory>, String> {
|
||||||
let current_id = self.current_player_id().ok_or("No current player")?;
|
if self.turn_team != team {
|
||||||
if current_id != player_id {
|
|
||||||
return Err("Not your turn".to_string());
|
return Err("Not your turn".to_string());
|
||||||
}
|
}
|
||||||
if self.phase != GamePhase::Playing {
|
if self.phase != GamePhase::Playing {
|
||||||
@ -138,7 +83,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.active_stones.clear();
|
self.active_stones.clear();
|
||||||
let path = self.physics.throw(self.turn_team, broom_x, broom_y, weight, curl, friction)?;
|
let trajectory = self.physics.throw(self.turn_team, broom_x, broom_y, weight, curl, friction)?;
|
||||||
self.active_stones = self.physics.current_stones();
|
self.active_stones = self.physics.current_stones();
|
||||||
self.phase = GamePhase::Simulating;
|
self.phase = GamePhase::Simulating;
|
||||||
|
|
||||||
@ -147,19 +92,19 @@ impl Game {
|
|||||||
Team::Yellow => self.stones_yellow = self.stones_yellow.saturating_sub(1),
|
Team::Yellow => self.stones_yellow = self.stones_yellow.saturating_sub(1),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(path)
|
Ok(trajectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eros
commented
the game should be playable with a single player who has the capability to switch teams the game should be playable with a single player who has the capability to switch teams
|
|||||||
pub fn process_throw(
|
pub fn process_throw(
|
||||||
&mut self,
|
&mut self,
|
||||||
player_id: &str,
|
team: Team,
|
||||||
broom_x: f32,
|
broom_x: f32,
|
||||||
broom_y: f32,
|
broom_y: f32,
|
||||||
weight: u8,
|
weight: u8,
|
||||||
curl: i8,
|
curl: i8,
|
||||||
friction: f32,
|
friction: f32,
|
||||||
) -> Result<ThrowOutcome, String> {
|
) -> Result<ThrowOutcome, String> {
|
||||||
let trajectory = self.handle_throw(player_id, broom_x, broom_y, weight, curl, friction)?;
|
let trajectory = self.handle_throw(team, broom_x, broom_y, weight, curl, friction)?;
|
||||||
self.finish_simulation();
|
self.finish_simulation();
|
||||||
|
|
||||||
let end_scored = self.take_last_end_scored();
|
let end_scored = self.take_last_end_scored();
|
||||||
@ -223,7 +168,7 @@ impl Game {
|
|||||||
Team::Red => 0,
|
Team::Red => 0,
|
||||||
Team::Yellow => 1,
|
Team::Yellow => 1,
|
||||||
};
|
};
|
||||||
self.scores[team_idx] += points as i32;
|
self.scores[team_idx] += points;
|
||||||
self.hammer = team.other();
|
self.hammer = team.other();
|
||||||
} else {
|
} else {
|
||||||
points = 0;
|
points = 0;
|
||||||
@ -315,29 +260,36 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prefers_red_when_requested_and_free() {
|
fn starts_in_waiting_phase() {
|
||||||
let mut game = Game::new();
|
let game = Game::new();
|
||||||
assert_eq!(game.add_player("p1".into(), Some(Team::Red)), Some(Team::Red));
|
assert!(matches!(game.phase, GamePhase::Waiting));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prefers_yellow_when_requested_and_free() {
|
fn starts_when_called() {
|
||||||
let mut game = Game::new();
|
let mut game = Game::new();
|
||||||
assert_eq!(game.add_player("p1".into(), Some(Team::Yellow)), Some(Team::Yellow));
|
game.start();
|
||||||
|
assert!(matches!(game.phase, GamePhase::Playing));
|
||||||
|
assert_eq!(game.end, 1);
|
||||||
|
assert_eq!(game.scores, [0, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn falls_back_when_preferred_taken() {
|
fn rejects_throw_for_wrong_team() {
|
||||||
let mut game = Game::new();
|
let mut game = Game::new();
|
||||||
assert_eq!(game.add_player("p1".into(), Some(Team::Red)), Some(Team::Red));
|
game.start();
|
||||||
assert_eq!(game.add_player("p2".into(), Some(Team::Red)), Some(Team::Yellow));
|
let turn = game.turn_team;
|
||||||
|
let wrong = turn.other();
|
||||||
|
let result = game.handle_throw(wrong, 0.5, 38.7, 7, 1, 1.0);
|
||||||
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn legacy_order_without_preference() {
|
fn accepts_throw_for_turn_team() {
|
||||||
let mut game = Game::new();
|
let mut game = Game::new();
|
||||||
assert_eq!(game.add_player("p1".into(), None), Some(Team::Red));
|
game.start();
|
||||||
assert_eq!(game.add_player("p2".into(), None), Some(Team::Yellow));
|
let turn = game.turn_team;
|
||||||
assert_eq!(game.add_player("p3".into(), None), None);
|
let result = game.handle_throw(turn, 0.5, 38.7, 7, 1, 1.0);
|
||||||
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user
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.