300 lines
7.8 KiB
Rust
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);
|
|
|
|
} |