diff --git a/aoc23/examples/day10_2_1.txt b/aoc23/examples/day10_2_1.txt new file mode 100644 index 0000000..6933a28 --- /dev/null +++ b/aoc23/examples/day10_2_1.txt @@ -0,0 +1,9 @@ +........... +.S-------7. +.|F-----7|. +.||.....||. +.||.....||. +.|L-7.F-J|. +.|..|.|..|. +.L--J.L--J. +........... \ No newline at end of file diff --git a/aoc23/examples/day10_2_2.txt b/aoc23/examples/day10_2_2.txt new file mode 100644 index 0000000..2e5dcbb --- /dev/null +++ b/aoc23/examples/day10_2_2.txt @@ -0,0 +1,10 @@ +.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... \ No newline at end of file diff --git a/aoc23/examples/day10_2_3.txt b/aoc23/examples/day10_2_3.txt new file mode 100644 index 0000000..fbc0300 --- /dev/null +++ b/aoc23/examples/day10_2_3.txt @@ -0,0 +1,10 @@ +FF7FSF7F7F7F7F7F---7 +L|LJ||||||||||||F--J +FL-7LJLJ||||||LJL-77 +F--JF--7||LJLJ7F7FJ- +L---JF-JLJ.||-FJLJJ7 +|F|F-JF---7F7-L7L|7| +|FFJF7L7F-JF7|JL---7 +7-L-JL7||F7|L7F-7F7| +L.L7LFJ|||||FJL7||LJ +L7JLJL-JLJLJL--JLJ.L \ No newline at end of file diff --git a/aoc23/src/solutions/day10.rs b/aoc23/src/solutions/day10.rs index acd86a4..8bf8d6c 100644 --- a/aoc23/src/solutions/day10.rs +++ b/aoc23/src/solutions/day10.rs @@ -10,13 +10,16 @@ pub fn part1(input: &str) -> usize { } pub fn part2(input: &str) -> usize { - 0 + let (coordinates, start) = parse(input); + let circle = find_circle(start, &coordinates); + + enclosed_area(&circle) } -fn find_circle(start: Coordinate, coordinates: &HashMap) -> String { +fn find_circle(start: Coordinate, coordinates: &HashMap) -> Vec { let mut queue = init_queue(&start, coordinates); - while let Some((coordinate, direction, path)) = queue.pop_front() { + while let Some((coordinate, direction, mut path)) = queue.pop_front() { if coordinate == start { return path; } @@ -24,10 +27,9 @@ fn find_circle(start: Coordinate, coordinates: &HashMap) -> St let pipe = coordinates.get(&coordinate).unwrap(); let next_direction = next_direction(*pipe, direction); let next_coordinate = coordinate.step(&next_direction, 1); - let mut next_path = path.clone(); - next_path.push(*pipe); + path.push(next_coordinate); - queue.push_back((next_coordinate, next_direction, next_path)); + queue.push_back((next_coordinate, next_direction, path)); } unreachable!() @@ -36,7 +38,7 @@ fn find_circle(start: Coordinate, coordinates: &HashMap) -> St fn init_queue( start: &Coordinate, coordinates: &HashMap, -) -> VecDeque<(Coordinate, Direction, String)> { +) -> VecDeque<(Coordinate, Direction, Vec)> { [ Direction::Up, Direction::Left, @@ -49,7 +51,7 @@ fn init_queue( coordinates.contains_key(coordinate) && valid_directions(*coordinates.get(coordinate).unwrap(), direction) }) - .map(|(coordinate, direction)| (coordinate, direction, String::from("S"))) + .map(|(coordinate, direction)| (coordinate, direction, vec![coordinate])) .collect() } @@ -84,6 +86,32 @@ fn valid_directions(pipe: char, direction: &Direction) -> bool { ) } +fn enclosed_area(circle: &[Coordinate]) -> usize { + let area = shoelace(circle); + internal_points(circle, area) +} + +fn shoelace(polygon: &[Coordinate]) -> usize { + // Use the shoelace formula (https://en.wikipedia.org/wiki/Shoelace_formula) to calculate the area of the polygon. + + let mut polygon: Vec = polygon.to_vec(); + polygon.push(polygon[0]); + + let area: i32 = polygon + .iter() + .zip(polygon.iter().skip(1)) + .map(|(p1, p2)| (p1.x * p2.y) - (p1.y * p2.x)) + .sum(); + + area.unsigned_abs() as usize / 2 +} + +fn internal_points(polygon: &[Coordinate], area: usize) -> usize { + // Calculate number of inside points of the polygon defined though the coordinates with the Pick's Theorem (https://en.wikipedia.org/wiki/Pick%27s_theorem). + + area - polygon.len() / 2 + 1 +} + fn parse(input: &str) -> (HashMap, Coordinate) { let mut coordinates = HashMap::new(); for (y, row) in input.lines().enumerate() { @@ -114,17 +142,22 @@ fn find_start(input: &str) -> Coordinate { mod tests { use super::*; - const EXAMPLE_1: &str = include_str!("../../examples/day10_1_1.txt"); - const EXAMPLE_2: &str = include_str!("../../examples/day10_1_2.txt"); + const EXAMPLE_1_1: &str = include_str!("../../examples/day10_1_1.txt"); + const EXAMPLE_1_2: &str = include_str!("../../examples/day10_1_2.txt"); + const EXAMPLE_2_1: &str = include_str!("../../examples/day10_2_1.txt"); + const EXAMPLE_2_2: &str = include_str!("../../examples/day10_2_2.txt"); + const EXAMPLE_2_3: &str = include_str!("../../examples/day10_2_3.txt"); #[test] fn part1_ex() { - assert_eq!(4, part1(EXAMPLE_1)); - assert_eq!(8, part1(EXAMPLE_2)); + assert_eq!(4, part1(EXAMPLE_1_1)); + assert_eq!(8, part1(EXAMPLE_1_2)); } #[test] fn part2_ex() { - assert_eq!(0, part2("")); + assert_eq!(4, part2(EXAMPLE_2_1)); + assert_eq!(8, part2(EXAMPLE_2_2)); + assert_eq!(10, part2(EXAMPLE_2_3)); } }