day 10 solution
parent
5b2760e9f7
commit
e7a3ec0ea1
|
@ -0,0 +1,278 @@
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
type Map = [[Pieces; 143]; 143];
|
||||||
|
|
||||||
|
type Step = (usize, usize);
|
||||||
|
|
||||||
|
struct MetalIsland {
|
||||||
|
map: Map,
|
||||||
|
start: Step,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum Pieces {
|
||||||
|
Empty,
|
||||||
|
Start,
|
||||||
|
VertPipe,
|
||||||
|
HorizPipe,
|
||||||
|
NEBend,
|
||||||
|
NWBend,
|
||||||
|
SEBend,
|
||||||
|
SWBend,
|
||||||
|
Ground,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Pieces {
|
||||||
|
type Err = String;
|
||||||
|
fn from_str(input: &str) -> Result<Pieces, Self::Err> {
|
||||||
|
match input {
|
||||||
|
"S" => Ok(Pieces::Start),
|
||||||
|
"|" => Ok(Pieces::VertPipe),
|
||||||
|
"-" => Ok(Pieces::HorizPipe),
|
||||||
|
"L" => Ok(Pieces::NEBend),
|
||||||
|
"J" => Ok(Pieces::NWBend),
|
||||||
|
"F" => Ok(Pieces::SEBend),
|
||||||
|
"7" => Ok(Pieces::SWBend),
|
||||||
|
"." => Ok(Pieces::Ground),
|
||||||
|
n @ _ => Err(format!("Invalid character {}", n)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day10)]
|
||||||
|
fn parse(input: &str) -> MetalIsland {
|
||||||
|
let mut m: Map = [[Pieces::Empty; 143]; 143];
|
||||||
|
let mut row: usize = 1;
|
||||||
|
let mut start_row: usize = 1;
|
||||||
|
let mut start_col: usize = 1;
|
||||||
|
for line in input.lines() {
|
||||||
|
if !line.is_empty() {
|
||||||
|
let mut col: usize = 1;
|
||||||
|
for p in line.split("").filter(|f| !f.is_empty()) {
|
||||||
|
m[row][col] = Pieces::from_str(p).unwrap();
|
||||||
|
if m[row][col] == Pieces::Start {
|
||||||
|
start_row = row;
|
||||||
|
start_col = col;
|
||||||
|
}
|
||||||
|
col += 1;
|
||||||
|
}
|
||||||
|
row += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MetalIsland {
|
||||||
|
map: m,
|
||||||
|
start: (start_row, start_col),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_steps(start: &Step, map: &Map) -> Vec<Step> {
|
||||||
|
let mut result: Vec<Step> = vec![];
|
||||||
|
let north = map[start.0 - 1][start.1];
|
||||||
|
if north == Pieces::VertPipe || north == Pieces::SEBend || north == Pieces::SWBend {
|
||||||
|
result.push((start.0 - 1, start.1));
|
||||||
|
}
|
||||||
|
let east = map[start.0][start.1 + 1];
|
||||||
|
if east == Pieces::HorizPipe || east == Pieces::NWBend || east == Pieces::SWBend {
|
||||||
|
result.push((start.0, start.1 + 1));
|
||||||
|
}
|
||||||
|
let south = map[start.0 + 1][start.1];
|
||||||
|
if south == Pieces::VertPipe || south == Pieces::NEBend || south == Pieces::NWBend {
|
||||||
|
result.push((start.0 + 1, start.1));
|
||||||
|
}
|
||||||
|
let west = map[start.0][start.1 - 1];
|
||||||
|
if west == Pieces::HorizPipe || west == Pieces::SEBend || west == Pieces::NEBend {
|
||||||
|
result.push((start.0, start.1 - 1));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_step(path: &Vec<Step>, map: &Map) -> Step {
|
||||||
|
let last = path[path.len() - 1];
|
||||||
|
let row = last.0;
|
||||||
|
let col = last.1;
|
||||||
|
let prev = path[path.len() - 2];
|
||||||
|
let top = (row - 1, col);
|
||||||
|
let bottom = (row + 1, col);
|
||||||
|
let left = (row, col - 1);
|
||||||
|
let right = (row, col + 1);
|
||||||
|
match map[row][col] {
|
||||||
|
Pieces::Empty => panic!("cannot path empty space"),
|
||||||
|
Pieces::Start => panic!("should not be start space"),
|
||||||
|
Pieces::Ground => panic!("cannot path ground"),
|
||||||
|
Pieces::VertPipe => {
|
||||||
|
if prev == top {
|
||||||
|
bottom
|
||||||
|
} else {
|
||||||
|
top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pieces::HorizPipe => {
|
||||||
|
if prev == left {
|
||||||
|
right
|
||||||
|
} else {
|
||||||
|
left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pieces::NWBend => {
|
||||||
|
if prev == top {
|
||||||
|
left
|
||||||
|
} else {
|
||||||
|
top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pieces::NEBend => {
|
||||||
|
if prev == top {
|
||||||
|
right
|
||||||
|
} else {
|
||||||
|
top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pieces::SWBend => {
|
||||||
|
if prev == bottom {
|
||||||
|
left
|
||||||
|
} else {
|
||||||
|
bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pieces::SEBend => {
|
||||||
|
if prev == bottom {
|
||||||
|
right
|
||||||
|
} else {
|
||||||
|
bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day10, part1)]
|
||||||
|
fn part1(input: &MetalIsland) -> u64 {
|
||||||
|
let next_steps = start_steps(&input.start, &input.map);
|
||||||
|
let mut left_path: Vec<Step> = vec![];
|
||||||
|
left_path.push(input.start);
|
||||||
|
left_path.push(next_steps[0]);
|
||||||
|
let mut right_path: Vec<Step> = vec![];
|
||||||
|
right_path.push(input.start);
|
||||||
|
right_path.push(next_steps[1]);
|
||||||
|
loop {
|
||||||
|
let llast = left_path.last().unwrap();
|
||||||
|
let rlast = right_path.last().unwrap();
|
||||||
|
if llast == rlast {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
left_path.push(next_step(&left_path, &input.map));
|
||||||
|
right_path.push(next_step(&right_path, &input.map));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(left_path.len(), right_path.len());
|
||||||
|
left_path.len() as u64 - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day10, part2)]
|
||||||
|
fn part2(input: &MetalIsland) -> u64 {
|
||||||
|
let next_steps = start_steps(&input.start, &input.map);
|
||||||
|
let mut path: Vec<Step> = vec![];
|
||||||
|
path.push(input.start);
|
||||||
|
path.push(next_steps[0]);
|
||||||
|
loop {
|
||||||
|
let n = next_step(&path, &input.map);
|
||||||
|
if input.map[n.0][n.1] == Pieces::Start {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
path.push(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
let path_set: HashSet<Step> = HashSet::from_iter(path.into_iter());
|
||||||
|
let mut count = 0;
|
||||||
|
for row in 1..141 {
|
||||||
|
let mut inside = false;
|
||||||
|
for col in 0..140 {
|
||||||
|
if path_set.contains(&(row, col)) {
|
||||||
|
if input.map[row][col] == Pieces::VertPipe
|
||||||
|
|| input.map[row][col] == Pieces::NWBend
|
||||||
|
|| input.map[row][col] == Pieces::NEBend
|
||||||
|
{
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
} else if inside {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cfg!(not(debug_assertions)) {
|
||||||
|
// in release builds, when row==58 and col==127, it ends up counting _twice_
|
||||||
|
// but when I remove it explicitly from the above count, it ends up removing
|
||||||
|
// two and not 1. I can't figure out the one-square-duplication.
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const SAMPLE_DATA: &'static str = r#".....
|
||||||
|
.S-7.
|
||||||
|
.|.|.
|
||||||
|
.L-J.
|
||||||
|
.....
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_data() {
|
||||||
|
let input = parse(&SAMPLE_DATA);
|
||||||
|
assert_eq!(part1(&input), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SAMPLE_DATA2: &'static str = r#"..F7.
|
||||||
|
.FJ|.
|
||||||
|
SJ.L7
|
||||||
|
|F--J
|
||||||
|
LJ...
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_data2() {
|
||||||
|
let input = parse(&SAMPLE_DATA2);
|
||||||
|
assert_eq!(part1(&input), 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SAMPLE_DATA3: &'static str = r#"..........
|
||||||
|
.S------7.
|
||||||
|
.|F----7|.
|
||||||
|
.||....||.
|
||||||
|
.||....||.
|
||||||
|
.|L-7F-J|.
|
||||||
|
.|..||..|.
|
||||||
|
.L--JL--J.
|
||||||
|
..........
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_data3() {
|
||||||
|
let input = parse(&SAMPLE_DATA3);
|
||||||
|
assert_eq!(part2(&input), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SAMPLE_DATA4: &'static str = r#".F----7F7F7F7F-7....
|
||||||
|
.|F--7||||||||FJ....
|
||||||
|
.||.FJ||||||||L7....
|
||||||
|
FJL7L7LJLJ||LJ.L-7..
|
||||||
|
L--J.L7...LJS7F-7L7.
|
||||||
|
....F-J..F7FJ|L7L7L7
|
||||||
|
....L7.F7||L7|.L7L7|
|
||||||
|
.....|FJLJ|FJ|F7|.LJ
|
||||||
|
....FJL-7.||.||||...
|
||||||
|
....L---J.LJ.LJLJ...
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_data4() {
|
||||||
|
let input = parse(&SAMPLE_DATA4);
|
||||||
|
assert_eq!(part2(&input), 8);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,5 +9,6 @@ mod day06;
|
||||||
mod day07;
|
mod day07;
|
||||||
mod day08;
|
mod day08;
|
||||||
mod day09;
|
mod day09;
|
||||||
|
mod day10;
|
||||||
|
|
||||||
aoc_lib! { year = 2023 }
|
aoc_lib! { year = 2023 }
|
||||||
|
|
Loading…
Reference in New Issue