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) -> 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, current_rock_pos :&Vec, 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::>(); // 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::>(); // 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) { let height = find_highest(positions); if height == -1 { return; } let mut map :Vec> = 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) { let stream = &inp[0]; const NUM_ROCKS :i64 = 1000000000000; const HEIGHT_OFFSET :i32 = 3; let shape_offset :Vec<(Vec, 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 = 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); }