Skip to content

Commit

Permalink
Year 2023, Day 18, part 2 WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Helbing authored and phelbingkg committed Dec 18, 2023
1 parent 2f34530 commit 90deae5
Showing 1 changed file with 76 additions and 66 deletions.
142 changes: 76 additions & 66 deletions year2023/src/day18.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
use std::collections::{HashSet, VecDeque};

use common::day::Day;
use util::grid2d::{Coords, Direction};

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,
Expand All @@ -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<Coords> {
let mut ret = HashSet::new();
fn area(instrs: &[Instruction]) -> i64 {
let b = instrs.iter().map(|instr| instr.length).sum::<i64>();
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<Coords> {
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<Coords>) -> 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)]
Expand Down Expand Up @@ -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);
}
}

0 comments on commit 90deae5

Please sign in to comment.