This commit is contained in:
Acvaxoort
2023-12-10 16:43:59 +01:00
parent 29812f29f6
commit f1f6f0f79c
4 changed files with 347 additions and 0 deletions

192
day10/src/main.rs Normal file
View 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);
}