use crate::command::Command; use crate::consts::*; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GameStatus { Continue, PlayerOneWon, PlayerTwoWon, Draw, // Until I add score I guess } #[derive(Debug, Clone, PartialEq, Eq)] pub struct GameState { pub status: GameStatus, pub players: [Player; 2], pub obstacles: Vec, pub powerup_oils: Vec, pub powerup_boosts: Vec, pub finish_lines: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Player { pub position: Position, pub next_position: Position, pub speed: usize, pub boost_remaining: usize, pub oils: usize, pub boosts: usize, pub finished: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Position { pub x: usize, pub y: usize, } impl GameState { pub fn update(&mut self, commands: [Command; 2]) { self.do_command(0, &commands[0]); self.do_command(1, &commands[1]); self.update_player_collisions(); self.update_player_travel(); 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 }; } fn do_command(&mut self, player_index: usize, command: &Command) { use Command::*; self.players[player_index].tick_boost(); // TODO: Command validation assertions match command { Nothing => {} Accelerate => self.players[player_index].accelerate(), Decelerate => self.players[player_index].decelerate(), TurnLeft => self.players[player_index].turn_left(), TurnRight => self.players[player_index].turn_right(), UseBoost => self.players[player_index].boost(), UseOil => { self.players[player_index].oils -= 1; let player_position = self.players[player_index].position; self.obstacles.push(Position { x: player_position.x - 1, y: player_position.y, }); } } self.players[player_index].set_next_position_x(); } fn update_player_collisions(&mut self) { let same_lanes_before = self.players[0].position.y == self.players[1].position.y; let same_lanes_after = self.players[0].next_position.y == self.players[1].next_position.y; let first_passing_second = self.players[0].position.x < self.players[1].position.x && self.players[0].next_position.x >= self.players[1].next_position.x; let second_passing_first = self.players[1].position.x < self.players[0].position.x && self.players[1].next_position.x >= self.players[0].next_position.x; let same_x_after = self.players[0].next_position.x == self.players[1].next_position.x; if same_lanes_before && same_lanes_after && first_passing_second { self.players[0].next_position.x = self.players[1].next_position.x - 1; } else if same_lanes_before && same_lanes_after && second_passing_first { self.players[1].next_position.x = self.players[0].next_position.x - 1; } else if same_lanes_after && same_x_after { self.players[0].bonked_while_changing_lanes(); self.players[1].bonked_while_changing_lanes(); } } fn update_player_travel(&mut self) { for player in &mut self.players { player.move_along( &self.obstacles, &self.powerup_oils, &self.powerup_boosts, &self.finish_lines, ); } } pub fn valid_moves(&self, player_index: usize) -> Vec { let player = &self.players[player_index]; let mut result = Vec::with_capacity(7); result.push(Command::Nothing); if player.speed < SPEED_4 { result.push(Command::Accelerate); } if player.speed > SPEED_0 { result.push(Command::Decelerate); } if player.position.y > 0 { result.push(Command::TurnLeft); } if player.position.y < HEIGHT { 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_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 turn_left(&mut self) { self.next_position.y = self.position.y - 1; } fn turn_right(&mut self) { self.next_position.y = self.position.y + 1; } fn boost(&mut self) { self.speed = SPEED_BOOST; self.boost_remaining = BOOST_DURATION; self.boosts -= 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 set_next_position_x(&mut self) { if self.position.y == self.next_position.y { self.next_position.x = self.position.x + self.speed; } else { self.next_position.x = self.position.x + self.speed - 1; } } fn bonked_while_changing_lanes(&mut self) { self.next_position.y = self.position.y; self.next_position.x = self.position.x + self.speed - 1; } fn move_along( &mut self, obstacles: &[Position], powerup_oils: &[Position], powerup_boosts: &[Position], finish_lines: &[Position], ) { let start_x = if self.position.y == self.next_position.y { self.position.x + 1 } else { self.position.x }; for x in start_x..=self.next_position.x { let position = Position { x, y: self.next_position.y, }; if obstacles.contains(&position) { self.decelerate_from_obstacle(); } if powerup_oils.contains(&position) { self.oils += 1; } if powerup_boosts.contains(&position) { self.boosts += 1; } if finish_lines.contains(&position) { self.finished = true; } } self.position = self.next_position; } }