192 lines
5.0 KiB
Rust
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);
|
|
|
|
} |