pub mod command; pub mod consts; pub mod global_json; pub mod json; pub mod state; use command::*; use consts::*; use pathfinding::prelude::*; use state::*; use std::cmp::Ordering; pub fn choose_command(state: &GameState) -> Command { // choose_command_with_looking_forward_heuristic(state) choose_command_with_astar(state) } fn choose_command_with_looking_forward_heuristic(state: &GameState) -> Command { let player_moves = state.valid_moves(0); let naive_result = player_moves .into_iter() .map(|player_move| { let mut state = state.clone(); state.update([player_move, Command::Accelerate]); (player_move, state) }) .flat_map(|(player_move, state)| { state.valid_moves(0).into_iter().map(move |second_move| { let mut second_move_state = state.clone(); second_move_state.update([second_move, Command::Accelerate]); (player_move, second_move_state) }) }) .max_by(|(_, a), (_, b)| compare_states(a, b)) .unwrap() .0; naive_result } fn compare_states(a: &GameState, b: &GameState) -> Ordering { if a.status == GameStatus::PlayerOneWon && b.status == GameStatus::PlayerOneWon { a.players[0].speed.cmp(&b.players[0].speed) } else if a.status == GameStatus::PlayerOneWon { Ordering::Greater } else if b.status == GameStatus::PlayerOneWon { Ordering::Less } else { let weighted_position_a = a.players[0].position.x + a.players[0].boosts * 2; let weighted_position_b = b.players[0].position.x + b.players[0].boosts * 2; weighted_position_a .cmp(&weighted_position_b) .then(a.players[0].speed.cmp(&b.players[0].speed)) .then(a.players[0].position.x.cmp(&b.players[0].position.x)) .then(a.players[0].boosts.cmp(&b.players[0].boosts)) } } fn choose_command_with_astar(state: &GameState) -> Command { shortest_path_first_command(state).unwrap_or(Command::Accelerate) } fn shortest_path_first_command(initial_state: &GameState) -> Option { let shortest_path_states = astar( initial_state, |state| { state .good_moves(0) .into_iter() .filter(|player_move| *player_move != Command::UseOil) .map(|player_move| { let mut state = state.clone(); state.update([player_move, Command::Decelerate]); (state, 1) }) .collect::>() }, |state| (WIDTH - state.players[0].position.x) / SPEED_BOOST, |state| state.status != GameStatus::Continue, ) .unwrap(); shortest_path_states .0 .iter() .zip(shortest_path_states.0.iter().skip(1)) .map(|(state, next)| { let player_move = state .valid_moves(0) .into_iter() .filter(|player_move| *player_move != Command::UseOil) .find(|player_move| { let mut state = state.clone(); state.update([*player_move, Command::Decelerate]); state == *next }) .unwrap(); player_move }) .next() }