summaryrefslogtreecommitdiff
path: root/2020-overdrive/vroomba-analysis/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to '2020-overdrive/vroomba-analysis/src/main.rs')
-rw-r--r--2020-overdrive/vroomba-analysis/src/main.rs182
1 files changed, 182 insertions, 0 deletions
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::<Vec<_>>()
+ },
+ |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());
+}