From 90deae536360f7d3b1d9764c8f2dfbb4236022fe Mon Sep 17 00:00:00 2001 From: Peter Helbing Date: Mon, 18 Dec 2023 10:45:58 +0100 Subject: [PATCH] Year 2023, Day 18, part 2 WIP --- year2023/src/day18.rs | 142 ++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 66 deletions(-) diff --git a/year2023/src/day18.rs b/year2023/src/day18.rs index 01016a8..56c59cb 100644 --- a/year2023/src/day18.rs +++ b/year2023/src/day18.rs @@ -1,5 +1,3 @@ -use std::collections::{HashSet, VecDeque}; - use common::day::Day; use util::grid2d::{Coords, Direction}; @@ -7,24 +5,29 @@ pub struct Day18 {} impl Day for Day18 { fn star1(&self, input: &str) -> String { - let instrs: Vec<_> = input.lines().map(Instruction::parse).collect(); - let outline = outline(&instrs); - area(&outline).to_string() + let instrs: Vec<_> = input + .lines() + .map(|line| Instruction::parse_pt1(line)) + .collect(); + area(&instrs).to_string() } - fn star2(&self, _input: &str) -> String { - String::from("not implemented") + fn star2(&self, input: &str) -> String { + let instrs: Vec<_> = input + .lines() + .map(|line| Instruction::parse_pt2(line)) + .collect(); + area(&instrs).to_string() } } struct Instruction { dir: Direction, length: i64, - length_pt2: u32, } impl Instruction { - fn parse(line: &str) -> Instruction { + fn parse_pt1(line: &str) -> Instruction { let secs: Vec<_> = line.split_whitespace().collect(); let dir = match secs[0].chars().next().unwrap() { 'U' => Direction::N, @@ -34,73 +37,68 @@ impl Instruction { _ => unreachable!(), }; let length = secs[1].parse().unwrap(); - let color_rgb = u32::from_str_radix(&secs[2][2..8], 16).unwrap(); - Instruction { - dir, - length, - length_pt2: color_rgb, - } + Instruction { dir, length } + } + + fn parse_pt2(line: &str) -> Instruction { + let secs: Vec<_> = line.split_whitespace().collect(); + let length = i64::from_str_radix(&secs[2][2..7], 16).unwrap(); + let dir = match secs[2].chars().nth(7).unwrap() { + '0' => Direction::E, + '1' => Direction::S, + '2' => Direction::W, + '3' => Direction::N, + _ => unreachable!(), + }; + Instruction { dir, length } } } -fn outline(instrs: &[Instruction]) -> HashSet { - let mut ret = HashSet::new(); +fn area(instrs: &[Instruction]) -> i64 { + let b = instrs.iter().map(|instr| instr.length).sum::(); + let v = vertices(&instrs); + // Shoelace formula / Gauss trapezoid + let i = area_shoelace(&v); + // Pick's theorem + let a = i - b / 2 + 1; + // Apparently outline points are not counted in above formula! + a + b +} + +fn vertices(instrs: &[Instruction]) -> Vec { + let mut ret = vec![]; let pos_start = Coords { x: 0, y: 0 }; let mut pos = pos_start; - ret.insert(pos); + ret.push(pos); for instr in instrs { - for _ in 0..instr.length { - pos = pos.mov(instr.dir); - ret.insert(pos); + match instr.dir { + Direction::N => { + pos.y -= instr.length; + } + Direction::E => { + pos.x += instr.length; + } + Direction::S => { + pos.y += instr.length; + } + Direction::W => { + pos.x -= instr.length; + } + _ => unreachable!(), } + ret.push(pos); } assert_eq!(pos, pos_start); ret } -fn area(outline_coords: &HashSet) -> i64 { - let (x_min, x_max, y_min, y_max) = outline_coords.iter().fold( - (i64::MAX, i64::MIN, i64::MAX, i64::MIN), - |(x_min, x_max, y_min, y_max), c| { - ( - x_min.min(c.x), - x_max.max(c.x), - y_min.min(c.y), - y_max.max(c.y), - ) - }, - ); - - // one more row / column so we have a connected outside - let (x_min, x_max, y_min, y_max) = (x_min - 1, x_max + 1, y_min - 1, y_max + 1); - let total_area = (x_max - x_min + 1) * (y_max - y_min + 1); - - println!("total area: {total_area}"); - - // flood fill - let mut outside = HashSet::new(); - let mut queue = VecDeque::new(); - let start_pos = Coords { x: x_min, y: y_min }; - queue.push_back(start_pos); - outside.insert(start_pos); - - while let Some(pos) = queue.pop_front() { - for d in [Direction::N, Direction::E, Direction::S, Direction::W] { - let new_pos = pos.mov(d); - if new_pos.x >= x_min - && new_pos.x <= x_max - && new_pos.y >= y_min - && new_pos.y <= y_max - && !outline_coords.contains(&new_pos) - && !outside.contains(&new_pos) - { - outside.insert(new_pos); - queue.push_back(new_pos); - } - } +fn area_shoelace(vertices: &[Coords]) -> i64 { + let mut a = 0; + for i in 0..vertices.len() { + let j = (i + 1) % vertices.len(); + a += (vertices[i].x * vertices[j].y) - (vertices[i].y * vertices[j].x); } - - total_area - outside.len() as i64 + a / 2 } #[cfg(test)] @@ -129,10 +127,22 @@ U 2 (#7a21e3)"#; } #[test] - fn test_parse() { - let instr = Instruction::parse("R 6 (#70c710)"); + fn ex2() { + let d = Day18 {}; + assert_eq!(d.star2(INPUT), "952408144115"); + } + + #[test] + fn test_parse_pt1() { + let instr = Instruction::parse_pt1("R 6 (#70c710)"); assert_eq!(instr.dir, Direction::E); assert_eq!(instr.length, 6); - assert_eq!(instr.length_pt2, 0x70c710); + } + + #[test] + fn test_parse_pt2() { + let instr = Instruction::parse_pt2("R 6 (#70c710)"); + assert_eq!(instr.dir, Direction::E); + assert_eq!(instr.length, 461937); } }