diff options
Diffstat (limited to 'src/state.rs')
-rw-r--r-- | src/state.rs | 355 |
1 files changed, 0 insertions, 355 deletions
diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index acbce80..0000000 --- a/src/state.rs +++ /dev/null @@ -1,355 +0,0 @@ -use crate::command::Command; -use crate::consts::*; -use std::collections::BTreeSet; -use std::ops::Bound::{Excluded, Included}; -use std::rc::Rc; - -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub enum GameStatus { - Continue, - PlayerOneWon, - PlayerTwoWon, - Draw, // Until I add score I guess -} - -// TODO: Maintain sorted vecs for these BTrees? Do the range counts -// with binary searches to find indices only. -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct GameState { - pub status: GameStatus, - pub players: [Player; 2], - pub muds: Rc<BTreeSet<Position>>, - pub oil_spills: Rc<BTreeSet<Position>>, - pub powerup_oils: Rc<BTreeSet<Position>>, - pub powerup_boosts: Rc<BTreeSet<Position>>, -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct Player { - pub position: Position, - pub speed: u16, - pub boost_remaining: u8, - pub oils: u16, - pub boosts: u16, -} - -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Position { - pub y: u8, - pub x: u16, -} - -#[derive(Debug, Clone, Default)] -pub struct GameStateUpdateEvents { - pub players: [GameStatePlayerUpdateEvents; 2], -} -#[derive(Debug, Clone, Default)] -pub struct GameStatePlayerUpdateEvents { - pub boosts_used: u16, - pub boosts_collected: u16, - pub boosts_maintained: u8, - pub mud_hit: u16, - pub oil_collected: u16, - pub distance_travelled: u16, -} - -impl GameState { - pub fn update(&mut self, commands: [Command; 2], events: &mut GameStateUpdateEvents) { - if self.status != GameStatus::Continue { - return; - } - - let next_positions = [ - self.do_command(0, &commands[0], &mut events.players[0]), - self.do_command(1, &commands[1], &mut events.players[1]), - ]; - let next_positions = self.update_player_collisions(next_positions, events); - self.update_player_travel(next_positions, events); - - self.status = if self.players[0].finished() && self.players[1].finished() { - if self.players[0].speed > self.players[1].speed { - GameStatus::PlayerOneWon - } else if self.players[0].speed < self.players[1].speed { - GameStatus::PlayerTwoWon - } else { - GameStatus::Draw - } - } else if self.players[0].finished() { - GameStatus::PlayerOneWon - } else if self.players[1].finished() { - GameStatus::PlayerTwoWon - } else { - GameStatus::Continue - }; - } - - pub fn reset_players_to_start(&mut self) { - self.players[0].position = Position { x: 1, y: 1 }; - self.players[1].position = Position { x: 1, y: 4 }; - for player in &mut self.players { - player.speed = 5; - player.boost_remaining = 0; - player.oils = 0; - player.boosts = 0; - } - } - - fn do_command( - &mut self, - player_index: usize, - command: &Command, - events: &mut GameStatePlayerUpdateEvents, - ) -> Position { - use Command::*; - self.players[player_index].tick_boost(); - let mut next_y = self.players[player_index].position.y; - - match command { - Nothing => {} - Accelerate => self.players[player_index].accelerate(), - Decelerate => self.players[player_index].decelerate(), - TurnLeft => next_y = next_y.saturating_sub(1).max(MIN_Y), - TurnRight => next_y = next_y.saturating_add(1).min(MAX_Y), - UseBoost => { - events.boosts_used += 1; - self.players[player_index].boost(); - } - UseOil => { - debug_assert!(self.players[player_index].oils > 0); - self.players[player_index].oils = self.players[player_index].oils.saturating_sub(1); - let player_position = self.players[player_index].position; - let mut oil_spills = (*self.oil_spills).clone(); - oil_spills.insert(Position { - x: player_position.x.saturating_sub(1), - y: player_position.y, - }); - self.oil_spills = Rc::new(oil_spills); - } - } - - let turning = match command { - TurnLeft | TurnRight => true, - _ => false, - }; - - let next_x = self.players[player_index].next_position_x(turning); - Position { - x: next_x, - y: next_y, - } - } - - fn update_player_collisions( - &mut self, - next_positions: [Position; 2], - _events: &mut GameStateUpdateEvents, - ) -> [Position; 2] { - let same_lanes_before = self.players[0].position.y == self.players[1].position.y; - let same_lanes_after = next_positions[0].y == next_positions[1].y; - let first_passing_second = self.players[0].position.x < self.players[1].position.x - && next_positions[0].x >= next_positions[1].x; - let second_passing_first = self.players[1].position.x < self.players[0].position.x - && next_positions[1].x >= next_positions[0].x; - let same_x_after = next_positions[0].x == next_positions[1].x; - - if same_lanes_before && same_lanes_after && first_passing_second { - [ - Position { - y: next_positions[0].y, - x: next_positions[1].x.saturating_sub(1), - }, - next_positions[1], - ] - } else if same_lanes_before && same_lanes_after && second_passing_first { - [ - next_positions[0], - Position { - y: next_positions[1].y, - x: next_positions[0].x.saturating_sub(1), - }, - ] - } else if same_lanes_after && same_x_after { - [ - Position { - y: self.players[0].position.y, - x: self.players[0].next_position_x(true), - }, - Position { - y: self.players[1].position.y, - x: self.players[1].next_position_x(true), - }, - ] - } else { - next_positions - } - } - - fn update_player_travel( - &mut self, - next_positions: [Position; 2], - events: &mut GameStateUpdateEvents, - ) { - for (i, (player, next_position)) in self - .players - .iter_mut() - .zip(next_positions.iter()) - .enumerate() - { - player.move_along( - *next_position, - &self.muds, - &self.oil_spills, - &self.powerup_oils, - &self.powerup_boosts, - &mut events.players[i], - ); - } - } - - pub fn valid_moves(&self, player_index: usize) -> Vec<Command> { - let player = &self.players[player_index]; - let mut result = Vec::with_capacity(7); - result.push(Command::Nothing); - result.push(Command::Accelerate); - if player.speed > SPEED_0 { - result.push(Command::Decelerate); - } - if player.position.y > MIN_Y { - result.push(Command::TurnLeft); - } - if player.position.y < MAX_Y - 1 { - result.push(Command::TurnRight); - } - if player.boosts > 0 { - result.push(Command::UseBoost); - } - if player.oils > 0 { - result.push(Command::UseOil); - } - result - } - - pub fn good_moves(&self, player_index: usize) -> Vec<Command> { - let player = &self.players[player_index]; - let mut result = Vec::with_capacity(5); - result.push(Command::Accelerate); - if player.position.y > MIN_Y { - result.push(Command::TurnLeft); - } - if player.position.y < MAX_Y - 1 { - result.push(Command::TurnRight); - } - if player.boosts > 0 { - result.push(Command::UseBoost); - } - if player.oils > 0 { - result.push(Command::UseOil); - } - result - } -} - -impl Player { - fn accelerate(&mut self) { - self.speed = match self.speed { - i if i < SPEED_1 => SPEED_1, - i if i < SPEED_2 => SPEED_2, - i if i < SPEED_3 => SPEED_3, - SPEED_BOOST => SPEED_BOOST, - _ => SPEED_4, - }; - } - - fn decelerate(&mut self) { - self.speed = match self.speed { - i if i <= SPEED_1 => SPEED_0, - i if i <= SPEED_2 => SPEED_1, - i if i <= SPEED_3 => SPEED_2, - i if i <= SPEED_4 => SPEED_3, - _ => SPEED_4, - }; - self.boost_remaining = 0; - } - - fn decelerate_from_obstacle(&mut self) { - self.speed = match self.speed { - i if i <= SPEED_2 => SPEED_1, - i if i <= SPEED_3 => SPEED_2, - i if i <= SPEED_4 => SPEED_3, - _ => SPEED_4, - }; - self.boost_remaining = 0; - } - - fn boost(&mut self) { - debug_assert!(self.boosts > 0); - self.speed = SPEED_BOOST; - self.boost_remaining = BOOST_DURATION; - self.boosts = self.boosts.saturating_sub(1); - } - - fn tick_boost(&mut self) { - self.boost_remaining = self.boost_remaining.saturating_sub(1); - if self.boost_remaining == 0 && self.speed == SPEED_BOOST { - self.speed = SPEED_4; - } - } - - fn next_position_x(&mut self, turning: bool) -> u16 { - if turning { - self.position.x.saturating_add(self.speed.saturating_sub(1)) - } else { - self.position.x.saturating_add(self.speed) - } - } - - fn move_along( - &mut self, - next_position: Position, - muds: &BTreeSet<Position>, - oil_spills: &BTreeSet<Position>, - powerup_oils: &BTreeSet<Position>, - powerup_boosts: &BTreeSet<Position>, - events: &mut GameStatePlayerUpdateEvents, - ) { - let range = ( - Included(Position { - y: next_position.y, - x: self.position.x.saturating_add(1), - }), - Excluded(Position { - y: next_position.y, - x: next_position.x.saturating_add(1), - }), - ); - - let mud_hit = muds - .range(range) - .count() - .saturating_add(oil_spills.range(range).count()); - for _ in 0..mud_hit { - self.decelerate_from_obstacle(); - } - - let oil_collected = powerup_oils.range(range).count() as u16; - self.oils = self.oils.saturating_add(oil_collected); - let boosts_collected = powerup_boosts.range(range).count() as u16; - self.boosts = self.boosts.saturating_add(boosts_collected); - - events.mud_hit = events.mud_hit.saturating_add(mud_hit as u16); - events.boosts_collected = events.boosts_collected.saturating_add(boosts_collected); - events.oil_collected = events.oil_collected.saturating_add(oil_collected); - events.distance_travelled = events - .distance_travelled - .saturating_add(next_position.x.saturating_sub(self.position.x)); - if self.speed == SPEED_BOOST { - events.boosts_maintained = events.boosts_maintained.saturating_add(1); - } - - self.position = next_position; - } - - fn finished(&self) -> bool { - self.position.x >= WIDTH - } -} |