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 day08;
|
||||
mod day09;
|
||||
mod day10;
|
||||
|
||||
aoc_lib! { year = 2023 }
|
||||
|
|
Loading…
Reference in New Issue