AoC2022/d24/src/a1.rs
2022-12-24 14:53:46 +01:00

300 lines
7.8 KiB
Rust

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<String>) -> (Pos, Pos, Vec<Blizzard>, 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<Blizzard>, 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<Blizzard>) -> i32 {
// positions that need to be evaluated in this round
let mut positions :Vec<Pos> = 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<Pos> = 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<Blizzard>, 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<String>) {
// 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);
}