use std::{collections::HashSet, ops::Sub}; #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] struct Pos(usize, usize); #[derive(Debug)] enum Direction { UP, DOWN, LEFT, RIGHT, } struct Blizzard { pos :Pos, future_pos :Pos, dir :Direction, } impl Blizzard { fn from(x :usize, y :usize, dir :Direction) -> Self { Blizzard { pos: Pos(x, y), future_pos: Pos(0, 0), dir: dir } } } struct Player { pos :Pos, future_pos :Pos, } impl Player { fn from(x: usize, y: usize) -> Self { Player { pos: Pos(x, y), future_pos: Pos(0, 0) } } } fn parse_inp(inp :&Vec) -> (Pos, Pos, Vec, Player, Pos) { // find Walls let mut min = Pos(usize::MAX, usize::MAX); let mut max = Pos(usize::MIN, usize::MIN); for line in inp.iter().enumerate() { for c in line.1.char_indices() { if c.1 == '#' { if c.0 < min.0 { min.0 = c.0; } if c.0 > max.0 { max.0 = c.0; } if line.0 < min.1 { min.1 = line.0; } if line.0 > max.1 { max.1 = line.0; } } } } // find blizards let mut blizzards = vec![]; for line in inp.iter().enumerate() { for c in line.1.char_indices() { match c.1 { '<' => { blizzards.push(Blizzard::from(c.0, line.0, Direction::LEFT)) } '>' => { blizzards.push(Blizzard::from(c.0, line.0, Direction::RIGHT)) } 'v' => { blizzards.push(Blizzard::from(c.0, line.0, Direction::DOWN)) } '^' => { blizzards.push(Blizzard::from(c.0, line.0, Direction::UP)) } _ => {} } } } let player_x = inp.first().unwrap().chars().position(|c| c == '.').unwrap(); let player = Player::from(player_x, 0); let goal_x = inp.last().unwrap().chars().position(|c| c == '.').unwrap(); let goal = Pos(goal_x, inp.len() - 1); (min, max, blizzards, player, goal) } fn advance_blizzards(blizzards :&mut Vec, min :&Pos, max :&Pos) { for blizzard in blizzards { let future_pos = { match blizzard.dir { Direction::DOWN => { let mut p = blizzard.pos; p.1 += 1; if p.1 >= max.1 { p.1 = min.1 + 1; } p } Direction::UP => { let mut p = blizzard.pos; match p.1.checked_sub(1) { Some(i) => { if i <= min.1 { p.1 = max.1 - 1; } else { p.1 = i; } } None => { p.1 = max.1 - 1; } } p } Direction::RIGHT => { let mut p = blizzard.pos; p.0 += 1; if p.0 >= max.0 { p.0 = min.0 + 1; } p } Direction::LEFT => { let mut p = blizzard.pos; match p.0.checked_sub(1) { Some(i) => { if i <= min.0 { p.0 = max.0 - 1; } else { p.0 = i; } } None => { p.0 = max.0 - 1; } } p } } }; //println!("blizzard {:?} with dir {:?} moved to position {:?}", blizzard.pos, blizzard.dir, future_pos); blizzard.pos = future_pos; } } fn find_shortest_path(start :&Pos, goal :&Pos, min :&Pos, max :&Pos, blizzards :&mut Vec) -> i32 { // positions that need to be evaluated in this round let mut positions :Vec = vec![ *start ]; let mut elapsed_time = 0; loop { elapsed_time += 1; // find future positions advance_blizzards(blizzards, &min, &max); // look from the current position to all 4 possible directions AND the current Tile // if no blizzard future_position is there then add it to positions let mut position_candidates :HashSet = HashSet::new(); for p in &positions { position_candidates.insert(*p); // stand still if p.1 > min.1 && p.1 < max.1 { if p.0 + 1 < max.0 { position_candidates.insert(Pos(p.0 + 1, p.1)); // go right } if p.0 > min.0 + 1 { position_candidates.insert(Pos(p.0 - 1, p.1)); // go left } } if p.0 > min.0 && p.0 < max.0 { if (p.1 + 1 < max.1) || (p.0 == goal.0 && (p.1+1) == goal.1) { position_candidates.insert(Pos(p.0, p.1 + 1)); // go down } if (p.1 > min.1 + 1) || (p.0 == goal.0 && (p.1-1) == goal.1) { position_candidates.insert(Pos(p.0, p.1 - 1)); // go up } } } blizzards.iter().for_each(|blizzard| { //if position_candidates.contains(&blizzard.pos) { position_candidates.remove(&blizzard.pos); //println!("{}: Removed {:?}", elapsed_time, blizzard.pos); //} }); // add all valid future positions to the future position vector with its time positions.clear(); for p in position_candidates { if p == *goal { return elapsed_time; } positions.push(p); } //println!("{}: ", elapsed_time); //print_board(min, max, blizzards, &player.pos); //println!("{}: {:?}", elapsed_time, positions); //if elapsed_time > 20 { return 0; } } } fn print_board(min :&Pos, max :&Pos, blizzards :&Vec, player :&Pos) { let mut map = vec![vec!['.'; max.0 + 1]; max.1 + 1]; for i in 0..=max.1 { map[i][0] = '#'; map[i][max.0] = '#'; } for i2 in 0..=max.0 { map[0][i2] = '#'; map[max.1][i2] = '#'; } for b in blizzards { if map[b.pos.1][b.pos.0] != '.' { map[b.pos.1][b.pos.0] = '2'; continue; } match b.dir { Direction::DOWN => { map[b.pos.1][b.pos.0] = 'v'; } Direction::UP => { map[b.pos.1][b.pos.0] = '^'; } Direction::RIGHT => { map[b.pos.1][b.pos.0] = '>'; } Direction::LEFT => { map[b.pos.1][b.pos.0] = '<'; } } } if map[player.1][player.0] != '.' { println!("ERROR!!!!!!!!!!!!"); } map[player.1][player.0] = 'E'; for l in map { for c in l { print!("{}", c); } println!(); } println!(); } pub fn run(inp :Vec) { // parse input let (min, max, mut blizzards, player, goal) = parse_inp(&inp); print_board(&min, &max, &blizzards, &player.pos); let time = find_shortest_path(&player.pos, &goal, &min, &max, &mut blizzards); println!("a1: {}", time); }