From f8a0e0f7f2f9cd5fb69899b5d7037bc969df4339 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Tue, 19 Apr 2022 21:36:41 +0200 Subject: Refile for merging repos --- 2020-overdrive/vroomba-analysis/src/main.rs | 182 ++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 2020-overdrive/vroomba-analysis/src/main.rs (limited to '2020-overdrive/vroomba-analysis/src/main.rs') diff --git a/2020-overdrive/vroomba-analysis/src/main.rs b/2020-overdrive/vroomba-analysis/src/main.rs new file mode 100644 index 0000000..994e021 --- /dev/null +++ b/2020-overdrive/vroomba-analysis/src/main.rs @@ -0,0 +1,182 @@ +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()); +} -- cgit v1.2.3