Skip to content

Commit

Permalink
Day 18 puzzle 2
Browse files Browse the repository at this point in the history
  • Loading branch information
jhrcook committed Dec 29, 2023
1 parent 06fe380 commit 5ffc547
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 23 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ ndarray = "0.15.6"
cached = "0.46.1"
strum = { version = "0.25", features = ["derive", "strum_macros"] }
strum_macros = "0.25"
hex = "0.4.3"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
| 15 | [src/solutions/day15.rs](src/solutions/day15.rs) | ⭐️⭐️ |
| 16 | [src/solutions/day16.rs](src/solutions/day16.rs) | ⭐️⭐️ |
| 17 | [src/solutions/day17.rs](src/solutions/day17.rs) | ⭐️⭐️ |
<!-- | 18 | [src/solutions/day18.rs](src/solutions/day18.rs) | ⭐️⭐️ | -->
| 18 | [src/solutions/day18.rs](src/solutions/day18.rs) | ⭐️⭐️ |
<!-- | 19 | [src/solutions/day19.rs](src/solutions/day19.rs) | ⭐️⭐️ | -->
<!-- | 20 | [src/solutions/day20.rs](src/solutions/day20.rs) | ⭐️⭐️ | -->
<!-- | 21 | [src/solutions/day21.rs](src/solutions/day21.rs) | ⭐️⭐️ | -->
Expand Down
67 changes: 46 additions & 21 deletions src/solutions/day18.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::data::load;
use num::integer::div_floor;
use regex::Regex;
use std::i64;
use std::{iter::zip, num::ParseIntError};
use thiserror::Error;

Expand Down Expand Up @@ -36,31 +37,49 @@ impl TryFrom<&char> for Direction {
#[derive(Debug, Clone)]
struct Dig {
dir: Direction,
n: i32,
_color: String,
n: i64,
}

fn line_to_dig(line: &str) -> Result<Dig, PuzzleErr> {
let re = Regex::new(r"^(?<dir>\w{1}) (?<n>\d+) \(\#(?<color>.+)\)").unwrap();
let re = Regex::new(r"^(?<dir>\w{1}) (?<n>\d+)").unwrap();
let Some(caps) = re.captures(line) else {
return Err(PuzzleErr::ParseInputError(line.trim().to_string()));
};
Ok(Dig {
dir: Direction::try_from(&caps["dir"].chars().next().unwrap())?,
n: caps["n"].parse::<i32>()?,
_color: caps["color"].to_string().clone(),
n: caps["n"].parse::<i64>()?,
})
}

fn parse_input(input: &str) -> Result<Vec<Dig>, PuzzleErr> {
fn line_to_dig_2(line: &str) -> Result<Dig, PuzzleErr> {
let re = Regex::new(r"\(\#(?<color>.+)\)").unwrap();
let Some(caps) = re.captures(line) else {
return Err(PuzzleErr::ParseInputError(line.trim().to_string()));
};
let color = caps["color"].to_string();
let dir = match color.chars().last().unwrap() {
'0' => Direction::R,
'1' => Direction::D,
'2' => Direction::L,
'3' => Direction::U,
c => panic!("Unknown char: {}", c),
};
let n = i64::from_str_radix(&color.as_str()[..5], 16).unwrap();
Ok(Dig { dir, n })
}

fn parse_input(
input: &str,
line_parse_func: &dyn Fn(&str) -> Result<Dig, PuzzleErr>,
) -> Result<Vec<Dig>, PuzzleErr> {
input
.trim()
.lines()
.map(line_to_dig)
.map(line_parse_func)
.collect::<Result<Vec<_>, PuzzleErr>>()
}

fn move_pos(p: &(i32, i32), dig: &Dig) -> (i32, i32) {
fn move_pos(p: &(i64, i64), dig: &Dig) -> (i64, i64) {
match dig.dir {
Direction::U => (p.0 + dig.n, p.1),
Direction::D => (p.0 - dig.n, p.1),
Expand All @@ -69,7 +88,7 @@ fn move_pos(p: &(i32, i32), dig: &Dig) -> (i32, i32) {
}
}

fn dig_plan_to_vertices(dig_plan: &[Dig]) -> Vec<(i32, i32)> {
fn dig_plan_to_vertices(dig_plan: &[Dig]) -> Vec<(i64, i64)> {
let mut vertices = Vec::from_iter([(0, 0)]);
for dig in dig_plan.iter() {
let a = vertices.last().unwrap();
Expand All @@ -79,21 +98,27 @@ fn dig_plan_to_vertices(dig_plan: &[Dig]) -> Vec<(i32, i32)> {
vertices
}

fn shoelace(vertices: &[(i32, i32)]) -> i32 {
let a: i32 = zip(vertices.iter(), vertices[1..].iter())
fn shoelace(vertices: &[(i64, i64)]) -> i64 {
let a: i64 = zip(vertices.iter(), vertices[1..].iter())
.map(|(a, b)| (a.0 * b.1) - (a.1 * b.0))
.sum();
a / 2
}

fn perimeter(vertices: &[(i32, i32)]) -> i32 {
fn perimeter(vertices: &[(i64, i64)]) -> i64 {
zip(vertices.iter(), vertices[1..].iter())
.map(|(a, b)| ((((a.0 - b.0).pow(2) + (a.1 - b.1).pow(2)) as f32).sqrt()) as i32)
.map(|(a, b)| ((((a.0 - b.0).pow(2) + (a.1 - b.1).pow(2)) as f32).sqrt()) as i64)
.sum()
}

pub fn puzzle_1(input: &str) -> Result<i32, PuzzleErr> {
let dig_plan = parse_input(input)?;
pub fn puzzle_1(input: &str) -> Result<i64, PuzzleErr> {
let dig_plan = parse_input(input, &line_to_dig)?;
let vertices = dig_plan_to_vertices(&dig_plan);
Ok(shoelace(&vertices) + div_floor(perimeter(&vertices), 2) + 1)
}

pub fn puzzle_2(input: &str) -> Result<i64, PuzzleErr> {
let dig_plan = parse_input(input, &line_to_dig_2)?;
let vertices = dig_plan_to_vertices(&dig_plan);
Ok(shoelace(&vertices) + div_floor(perimeter(&vertices), 2) + 1)
}
Expand All @@ -111,10 +136,10 @@ pub fn main(data_dir: &str) {
assert_eq!(answer_1, Ok(72821));

// Puzzle 2.
// let answer_2 = puzzle_2(&data);
// match answer_2 {
// Ok(x) => println!(" Puzzle 2: {}", x),
// Err(e) => panic!("No solution to puzzle 2: {}", e),
// }
// assert_eq!(answer_2, Ok(30449))
let answer_2 = puzzle_2(&data);
match answer_2 {
Ok(x) => println!(" Puzzle 2: {}", x),
Err(e) => panic!("No solution to puzzle 2: {}", e),
}
assert_eq!(answer_2, Ok(127844509405501))
}
8 changes: 7 additions & 1 deletion tests/test_day18.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use aoc_2023::solutions::day18::puzzle_1;
use aoc_2023::solutions::day18::{puzzle_1, puzzle_2};

const EXAMPLE_INPUT_1: &str = "
R 6 (#70c710)
Expand All @@ -22,3 +22,9 @@ fn puzzle_1_example_1() {
let _ = env_logger::try_init();
assert_eq!(puzzle_1(self::EXAMPLE_INPUT_1), Ok(62));
}

#[test]
fn puzzle_2_example_1() {
let _ = env_logger::try_init();
assert_eq!(puzzle_2(self::EXAMPLE_INPUT_1), Ok(952408144115));
}

0 comments on commit 5ffc547

Please sign in to comment.