day 10
This commit is contained in:
192
day10/src/main.rs
Normal file
192
day10/src/main.rs
Normal file
@@ -0,0 +1,192 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
fn main() {
|
||||
let input_str = read_to_string("input.txt").unwrap();
|
||||
// the layout as in the file
|
||||
let layout = input_str.lines().map(|str| str.as_bytes()).collect::<Vec<_>>();
|
||||
let layout_width = layout[0].len();
|
||||
let layout_height = layout.len();
|
||||
// Double the sizes
|
||||
let layout_width2 = layout_width * 2;
|
||||
let layout_height2 = layout_height * 2;
|
||||
// layout at double the resolution with spaces between each segment for task 2
|
||||
// E - enclosed, M - main loop, O - outside
|
||||
let mut layout_enclosing = vec![vec![b'E'; layout_width2]; layout_height2];
|
||||
// Step counter for task 1
|
||||
let mut steps = 1;
|
||||
unsafe {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Direction {
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
// Pipe walker, a structure that describes an abstract entity walking around the pipes
|
||||
#[derive(Clone, Copy)]
|
||||
struct PipeWalker {
|
||||
x: usize,
|
||||
y: usize,
|
||||
dir: Direction,
|
||||
}
|
||||
let layout_enclosing_cell = UnsafeCell::new(layout_enclosing);
|
||||
// Functions that move the walker around the pipes
|
||||
// also leaves a trail of M's in the layout_enclosing
|
||||
// They use an unsafe cell because otherwise the closures would have a borrow conflict
|
||||
let move_left = |walker: &mut PipeWalker| {
|
||||
let layout_enclosing_mut = &mut *layout_enclosing_cell.get();
|
||||
layout_enclosing_mut[walker.y * 2][walker.x * 2 - 1] = b'M';
|
||||
layout_enclosing_mut[walker.y * 2][walker.x * 2 - 2] = b'M';
|
||||
walker.x -= 1;
|
||||
walker.dir = Direction::Left;
|
||||
};
|
||||
let move_right = |walker: &mut PipeWalker| {
|
||||
let layout_enclosing_mut = &mut *layout_enclosing_cell.get();
|
||||
layout_enclosing_mut[walker.y * 2][walker.x * 2 + 1] = b'M';
|
||||
layout_enclosing_mut[walker.y * 2][walker.x * 2 + 2] = b'M';
|
||||
walker.x += 1;
|
||||
walker.dir = Direction::Right;
|
||||
};
|
||||
let move_up = |walker: &mut PipeWalker| {
|
||||
let layout_enclosing_mut = &mut *layout_enclosing_cell.get();
|
||||
layout_enclosing_mut[walker.y * 2 - 1][walker.x * 2] = b'M';
|
||||
layout_enclosing_mut[walker.y * 2 - 2][walker.x * 2] = b'M';
|
||||
walker.y -= 1;
|
||||
walker.dir = Direction::Up;
|
||||
};
|
||||
let move_down = |walker: &mut PipeWalker| {
|
||||
let layout_enclosing_mut = &mut *layout_enclosing_cell.get();
|
||||
layout_enclosing_mut[walker.y * 2 + 1][walker.x * 2] = b'M';
|
||||
layout_enclosing_mut[walker.y * 2 + 2][walker.x * 2] = b'M';
|
||||
walker.y += 1;
|
||||
walker.dir = Direction::Down;
|
||||
};
|
||||
let mut walkers: Vec<PipeWalker> = vec![];
|
||||
walkers.reserve(2);
|
||||
// Find the starting position and starting walkers
|
||||
for (j, &row) in layout.iter().enumerate() {
|
||||
for (i, &elem) in row.iter().enumerate() {
|
||||
if elem == b'S' {
|
||||
(&mut *layout_enclosing_cell.get())[j * 2][i * 2] = b'M';
|
||||
if i > 0 {
|
||||
let elem2 = layout[j][i - 1];
|
||||
if elem2 == b'-' || elem2 == b'L' || elem2 == b'F' {
|
||||
walkers.push(PipeWalker { x: i, y: j, dir: Direction::Left });
|
||||
move_left(walkers.last_mut().unwrap());
|
||||
}
|
||||
}
|
||||
if i < layout_width - 1 {
|
||||
let elem2 = layout[j][i + 1];
|
||||
if elem2 == b'-' || elem2 == b'J' || elem2 == b'7' {
|
||||
walkers.push(PipeWalker { x: i, y: j, dir: Direction::Left });
|
||||
move_right(walkers.last_mut().unwrap());
|
||||
}
|
||||
}
|
||||
if j > 0 {
|
||||
let elem2 = layout[j - 1][i];
|
||||
if elem2 == b'|' || elem2 == b'F' || elem2 == b'F' {
|
||||
walkers.push(PipeWalker { x: i, y: j, dir: Direction::Left });
|
||||
move_up(walkers.last_mut().unwrap());
|
||||
}
|
||||
}
|
||||
if j < layout_height - 1 {
|
||||
let elem2 = layout[j + 1][i];
|
||||
if elem2 == b'|' || elem2 == b'J' || elem2 == b'L' {
|
||||
walkers.push(PipeWalker { x: i, y: j, dir: Direction::Left });
|
||||
move_down(walkers.last_mut().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Walk around the main pipe loop, count steps for task 1
|
||||
loop {
|
||||
let advance_walker = |walker: &mut PipeWalker| {
|
||||
match walker.dir {
|
||||
Direction::Left => {
|
||||
match layout[walker.y][walker.x] {
|
||||
b'-' => move_left(walker),
|
||||
b'L' => move_up(walker),
|
||||
b'F' => move_down(walker),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Direction::Right => {
|
||||
match layout[walker.y][walker.x] {
|
||||
b'-' => move_right(walker),
|
||||
b'J' => move_up(walker),
|
||||
b'7' => move_down(walker),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Direction::Up => {
|
||||
match layout[walker.y][walker.x] {
|
||||
b'|' => move_up(walker),
|
||||
b'7' => move_left(walker),
|
||||
b'F' => move_right(walker),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Direction::Down => {
|
||||
match layout[walker.y][walker.x] {
|
||||
b'|' => move_down(walker),
|
||||
b'J' => move_left(walker),
|
||||
b'L' => move_right(walker),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
steps += 1;
|
||||
advance_walker(&mut walkers[0]);
|
||||
if walkers[0].x == walkers[1].x && walkers[0].y == walkers[1].y {
|
||||
break;
|
||||
}
|
||||
advance_walker(&mut walkers[1]);
|
||||
if walkers[0].x == walkers[1].x && walkers[0].y == walkers[1].y {
|
||||
break;
|
||||
}
|
||||
}
|
||||
layout_enclosing = layout_enclosing_cell.into_inner();
|
||||
}
|
||||
// Flood the layout from edges to determine what is enclosed
|
||||
let mut to_flood: Vec<(u16, u16)> = vec![];
|
||||
to_flood.extend((0..layout_width2).map(|x| (x as u16, 0u16)));
|
||||
to_flood.extend((0..layout_width2).map(|x| (x as u16, (layout_height2 - 1) as u16)));
|
||||
to_flood.extend((1..layout_height2 - 1).map(|x| (0u16, x as u16)));
|
||||
to_flood.extend((1..layout_height2 - 1).map(|x| ((layout_width2 - 1) as u16, x as u16)));
|
||||
loop {
|
||||
if to_flood.is_empty() {
|
||||
break;
|
||||
}
|
||||
let &(x, y) = to_flood.last().unwrap();
|
||||
to_flood.pop();
|
||||
// If an unflooded E (enclosed) is found, convert it to O (outside) and flood neighbours
|
||||
if layout_enclosing[y as usize][x as usize] == b'E' {
|
||||
layout_enclosing[y as usize][x as usize] = b'O';
|
||||
if x > 0 {
|
||||
to_flood.push((x - 1, y));
|
||||
}
|
||||
if x < layout_width2 as u16 - 1 {
|
||||
to_flood.push((x + 1, y));
|
||||
}
|
||||
if y > 0 {
|
||||
to_flood.push((x, y - 1));
|
||||
}
|
||||
if y < layout_height2 as u16 - 1 {
|
||||
to_flood.push((x, y + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Count enclosed space for task 2
|
||||
let count_enclosed = layout_enclosing.iter()
|
||||
.step_by(2)
|
||||
.fold(0, |acc, row| {
|
||||
acc + row.iter()
|
||||
.step_by(2)
|
||||
.fold(0, |acc, &elem| acc + if elem == b'E' { 1 } else { 0 })
|
||||
});
|
||||
println!("Steps: {}", steps);
|
||||
println!("Enclosed: {}", count_enclosed);
|
||||
}
|
Reference in New Issue
Block a user