use colored::*; use pathfinding::prelude::*; use std::path::PathBuf; use structopt::StructOpt; use vroomba::command::Command; use vroomba::consts::*; use vroomba::global_json; use vroomba::state::*; #[derive(StructOpt, Debug)] #[structopt(name = "vroomba-analysis")] struct Opt { /// Find out if there's a shorter path that uses the decelerate move #[structopt(long)] decelerate_experiment: bool, /// Find out if there's a shorter path that uses the nothing move (instead of accelerate) #[structopt(long)] nothing_experiment: bool, /// Path to GlobalState.json path: PathBuf, } fn main() { let opt = Opt::from_args(); let initial_state = global_json::read_initial_state_from_global_json_file(opt.path.to_str().unwrap()).unwrap(); if opt.decelerate_experiment { let shortest_path_with_decelerate = shortest_path(&initial_state, &[]); let shortest_path_without_decelerate = shortest_path(&initial_state, &[Command::Decelerate]); println!("With Decelerate"); log_shortest_path(&initial_state, &shortest_path_with_decelerate); println!("Without Decelerate"); log_shortest_path(&initial_state, &shortest_path_without_decelerate); if shortest_path_with_decelerate.len() < shortest_path_without_decelerate.len() { println!("With decelerate is faster!"); } else { println!("Same length!"); } } if opt.nothing_experiment { let shortest_path_with = shortest_path(&initial_state, &[]); let shortest_path_without = shortest_path(&initial_state, &[Command::Nothing]); println!("With Nothing"); log_shortest_path(&initial_state, &shortest_path_with); println!("Without Nothing"); log_shortest_path(&initial_state, &shortest_path_without); if shortest_path_with.len() < shortest_path_without.len() { println!("With nothing is faster!"); } else { println!("Same length!"); } } else { let shortest_path_actions = shortest_path(&initial_state, &[]); log_shortest_path(&initial_state, &shortest_path_actions); } } fn shortest_path( initial_state: &GameState, blacklist: &[Command], ) -> Vec<(Position, Position, GameState, GameState, Command)> { let shortest_path_states = astar( initial_state, |state| { state .valid_moves(0) .into_iter() .filter(|player_move| { *player_move != Command::UseOil && !blacklist.contains(player_move) }) .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 = &state.players[0]; let player_move = state .valid_moves(0) .into_iter() .filter(|player_move| { *player_move != Command::UseOil && !blacklist.contains(player_move) }) .find(|player_move| { let mut state = state.clone(); state.update([*player_move, Command::Decelerate]); state == *next }) .unwrap(); ( player.position, next.players[0].position, state.clone(), next.clone(), player_move, ) }) .collect() } fn log_shortest_path( initial_state: &GameState, shortest_path_actions: &Vec<(Position, Position, GameState, GameState, Command)>, ) { let chunk_size = 100; for chunk in 0..(WIDTH / chunk_size) { let start_x = chunk * chunk_size; for i in 0..chunk_size / 10 { print!("{:<10}", start_x + i * 10); } println!(); for y in MIN_Y..MAX_Y { for x in start_x..start_x + chunk_size { let pos = Position { y, x }; let c = if initial_state.muds.contains(&pos) { "O" } else if initial_state.powerup_boosts.contains(&pos) { ">" } else if x == WIDTH { "|" } else { "-" }; let player_on_block = shortest_path_actions .iter() .find(|(position, _, _, _, _)| *position == pos); let c_with_background = match player_on_block { None => c.black(), Some((_, _, _, _, Command::Accelerate)) => c.on_red(), Some((_, _, _, _, Command::Decelerate)) => c.on_green(), Some((_, _, _, _, Command::UseBoost)) => c.on_blue(), Some(_) => c.on_yellow(), }; let speed = shortest_path_actions .iter() .find(|(position, next_position, _, _, _)| { position.x < x && x < next_position.x && y == next_position.y }) .map(|(_, _, _, next_state, _)| next_state.players[0].speed); let c_with_foreground = match speed { None => c_with_background.white(), Some(SPEED_0) | Some(SPEED_1) => c_with_background.green(), Some(SPEED_2) => c_with_background.yellow(), Some(SPEED_3) => c_with_background.red(), Some(SPEED_4) => c_with_background.bright_red(), Some(SPEED_BOOST) => c_with_background.blue(), Some(_) => c_with_background.yellow(), }; print!("{}", c_with_foreground); } println!(); } } for (i, (position, _, state, _, player_move)) in shortest_path_actions.iter().enumerate() { let player = &state.players[0]; println!( "{:3}: x: {:4}, y: {:1}, speed: {:2}: {}", i, position.x, position.y, player.speed, player_move ); } println!("{} moves", shortest_path_actions.len()); }