AoC2022/d17/src/a2.rs
2022-12-21 20:04:44 +01:00

192 lines
5.0 KiB
Rust

use std::collections::{HashSet};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Pos (i32, i32);
const WIDTH :i32 = 7;
const FLOOR :i32 = -1;
fn find_highest(map :&HashSet<Pos>) -> i32 {
let mut max = -1;
for pos in map {
if pos.1 > max {
max = pos.1;
}
}
max
}
fn evaluate_stream(mut pos :Pos, stream :char) -> Pos {
match stream {
'>' => { pos.0 += 1 }
'<' => { pos.0 -= 1 }
_ => { }
}
pos
}
fn evaluate_rock_movement(positions :&mut HashSet<Pos>, current_rock_pos :&Vec<Pos>, stream_index :&mut usize, stream :&String) {
let mut moving_rock_pos = current_rock_pos.clone();
loop {
// get current stream
let current_stream = stream.chars().nth(*stream_index).unwrap();
*stream_index = (*stream_index + 1) % stream.len();
// move according to stream
let future_pos = moving_rock_pos.iter().map(|elem| evaluate_stream(*elem, current_stream)).collect::<Vec<Pos>>();
// check for collisions
let mut collision = false;
for pos in &future_pos {
if pos.0 < 0 || pos.0 >= WIDTH || pos.1 < 0 || positions.contains(&pos) {
collision = true;
break;
}
}
// move rock if no collision took place
if !collision {
moving_rock_pos = future_pos;
}
// move down
let future_pos = moving_rock_pos.iter().map(|elem| Pos(elem.0, elem.1 - 1)).collect::<Vec<Pos>>();
// check for collisions
let mut collision = false;
for pos in &future_pos {
if pos.0 < 0 || pos.0 >= WIDTH || pos.1 < 0 || positions.contains(&pos) {
collision = true;
break;
}
}
// move rock if no collision took place
// if a collision took place then add every old position of the rock to the map and return
if !collision {
moving_rock_pos = future_pos;
} else {
for pos in moving_rock_pos {
positions.insert(pos);
}
return;
}
}
}
fn print_board(positions :&HashSet<Pos>) {
let height = find_highest(positions);
if height == -1 {
return;
}
let mut map :Vec<Vec<char>> = vec![vec!['.'; WIDTH as usize]; height as usize + 1];
for pos in positions {
map[pos.1 as usize][pos.0 as usize] = '#';
}
for v in (0..map.len()).rev() {
for c in &map[v] {
print!("{}", c);
}
println!();
}
}
pub fn run(inp :Vec<String>) {
let stream = &inp[0];
const NUM_ROCKS :i64 = 1000000000000;
const HEIGHT_OFFSET :i32 = 3;
let shape_offset :Vec<(Vec<Pos>, i32)> = vec![
(vec![Pos(-1, 0), Pos(0, 0), Pos(1, 0), Pos(2, 0)], 1), // Minus
(vec![Pos(0, 0), Pos(0, 1), Pos(1, 1), Pos(-1, 1), Pos(0, 2)], 3), // Plus
(vec![Pos(1, 0), Pos(1, 1), Pos(1, 2), Pos(0, 2), Pos(-1, 2)], 3), // Reverse L
(vec![Pos(-1, 0), Pos(-1, 1), Pos(-1, 2), Pos(-1, 3)], 4), // Line (Vertical)
(vec![Pos(-1, 0), Pos(0, 0), Pos(0, 1), Pos(-1, 1)], 2), // Square
];
let mut positions :HashSet<Pos> = HashSet::new();
let mut default_spawn = Pos(3, HEIGHT_OFFSET);
let mut stream_index = 0;
let mut shape_index = 0;
// store the height each time a row is completely full and reset the map
let mut current_height = 0;
for rock_num in 0..NUM_ROCKS {
let mut highest_point = find_highest(&positions);
// check if the top row is completely filled
// if so we know that we can reset the board
let mut can_be_reset = true;
for i in 0..WIDTH {
if !positions.contains(&Pos(i, highest_point)) {
can_be_reset = false;
break;
}
}
if can_be_reset {
println!("Reset: stream:{}, block:{}", stream_index, shape_index);
println!("i:{}", rock_num);
println!("h:{}", highest_point);
current_height += highest_point + 1;
positions.clear();
highest_point = -1;
}
// get new rock offset
let mut new_rock = shape_offset[shape_index].clone();
// update spawn position
default_spawn.1 = highest_point + HEIGHT_OFFSET + new_rock.1;
// calculate new rock Position
for offset in &mut new_rock.0 {
offset.0 = default_spawn.0 + offset.0;
offset.1 = default_spawn.1 - offset.1;
}
// evaluate current rock fall
evaluate_rock_movement(&mut positions, &new_rock.0, &mut stream_index, stream);
// go to next rock
shape_index = (shape_index + 1) % shape_offset.len();
}
//print_board(&positions);
let height = find_highest(&positions);
println!("a2: {}", current_height + height + 1);
}