Skip to content

Commit

Permalink
Solve day 10
Browse files Browse the repository at this point in the history
  • Loading branch information
ricohageman committed Dec 10, 2023
1 parent b9b7766 commit f48f89f
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 11 deletions.
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.

| Day | Part 1 | Part 2 |
| :---: | :---: | :---: |
| [Day 1](./src/bin/01.rs) | `71.3µs` | `851.6µs` |
| [Day 2](./src/bin/02.rs) | `39.3µs` | `48.3µs` |
| [Day 3](./src/bin/03.rs) | `230.8µs` | `247.2µs` |
| [Day 4](./src/bin/04.rs) | `199.9µs` | `195.7µs` |
| [Day 5](./src/bin/05.rs) | `21.1µs` | `74.1µs` |
| [Day 6](./src/bin/06.rs) | `673.0ns` | `498.0ns` |
| [Day 7](./src/bin/07.rs) | `299.0µs` | `287.8µs` |
| [Day 8](./src/bin/08.rs) | `150.6µs` | `497.0µs` |
| [Day 9](./src/bin/09.rs) | `171.1µs` | `165.8µs` |

**Total: 3.55ms**
| [Day 1](./src/bin/01.rs) | `70.7µs` | `851.5µs` |
| [Day 2](./src/bin/02.rs) | `38.6µs` | `48.7µs` |
| [Day 3](./src/bin/03.rs) | `232.8µs` | `246.2µs` |
| [Day 4](./src/bin/04.rs) | `202.3µs` | `195.7µs` |
| [Day 5](./src/bin/05.rs) | `21.1µs` | `74.4µs` |
| [Day 6](./src/bin/06.rs) | `654.0ns` | `504.0ns` |
| [Day 7](./src/bin/07.rs) | `295.7µs` | `287.5µs` |
| [Day 8](./src/bin/08.rs) | `150.2µs` | `496.9µs` |
| [Day 9](./src/bin/09.rs) | `171.6µs` | `167.1µs` |
| [Day 10](./src/bin/10.rs) | `250.2µs` | `258.5µs` |

**Total: 4.06ms**
<!--- benchmarking table --->

---
Expand Down
5 changes: 5 additions & 0 deletions data/examples/10-1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.....
.S-7.
.|.|.
.L-J.
.....
5 changes: 5 additions & 0 deletions data/examples/10-2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
..F7.
.FJ|.
SJ.L7
|F--J
LJ...
9 changes: 9 additions & 0 deletions data/examples/10-3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
...........
.S-------7.
.|F-----7|.
.||.....||.
.||.....||.
.|L-7.F-J|.
.|..|.|..|.
.L--J.L--J.
...........
10 changes: 10 additions & 0 deletions data/examples/10-4.txt
Original file line number Diff line number Diff line change
@@ -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...
10 changes: 10 additions & 0 deletions data/examples/10-5.txt
Original file line number Diff line number Diff line change
@@ -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
217 changes: 217 additions & 0 deletions src/bin/10.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
use itertools::Itertools;
use std::isize;
advent_of_code::solution!(10);

#[derive(Clone, Copy, Debug)]
enum Tile {
Start,
Ground,
Pipe(Pipe),
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Pipe {
Vertical,
Horizontal,
NorthEast,
NorthWest,
SouthWest,
SouthEast,
}

type Coordinate = (isize, isize);

struct Grid {
tiles: Vec<Vec<Tile>>,
}

impl Grid {
pub fn new(tiles: Vec<Vec<Tile>>) -> Self {
Self { tiles }
}

pub fn pipe_at(&self, coordinate: Coordinate) -> Option<Pipe> {
let x: usize = coordinate.0.try_into().ok()?;
let y: usize = coordinate.1.try_into().ok()?;

match self.tiles.get(y)?.get(x)? {
Tile::Start => panic!(),
Tile::Ground => None,
Tile::Pipe(pipe) => Some(*pipe),
}
}
}

fn parse_grid(input: &str) -> (Coordinate, Grid) {
let mut start = None;

let tiles = input
.lines()
.enumerate()
.map(|(y, line)| {
line.chars()
.enumerate()
.map(|(x, char)| match char {
'.' => Tile::Ground,
'S' => {
start = Some((x as isize, y as isize));
Tile::Start
}
'|' => Tile::Pipe(Pipe::Vertical),
'-' => Tile::Pipe(Pipe::Horizontal),
'L' => Tile::Pipe(Pipe::NorthEast),
'J' => Tile::Pipe(Pipe::NorthWest),
'7' => Tile::Pipe(Pipe::SouthWest),
'F' => Tile::Pipe(Pipe::SouthEast),
_ => panic!(),
})
.collect()
})
.collect();

(start.unwrap(), Grid::new(tiles))
}

fn neighbours(coordinate: Coordinate, pipe: Pipe) -> (Coordinate, Coordinate) {
let (x, y) = coordinate;

match pipe {
Pipe::Vertical => ((x, y - 1), (x, y + 1)),
Pipe::Horizontal => ((x - 1, y), (x + 1, y)),
Pipe::NorthEast => ((x, y - 1), (x + 1, y)),
Pipe::NorthWest => ((x, y - 1), (x - 1, y)),
Pipe::SouthEast => ((x, y + 1), (x + 1, y)),
Pipe::SouthWest => ((x, y + 1), (x - 1, y)),
}
}

fn find_cycle_starting_at(
start_coordinate: Coordinate,
pipe: Pipe,
grid: &Grid,
) -> Option<Vec<(isize, isize)>> {
let mut cycle = vec![start_coordinate];

let (_, c2) = neighbours(start_coordinate, pipe);
let mut pipe = grid.pipe_at(c2)?;

let mut previous_coordinate = start_coordinate;
let mut coordinate = c2;
cycle.push(coordinate);

while start_coordinate != coordinate {
let (c1, c2) = neighbours(coordinate, pipe);

let next_coordinate = match (c1 == previous_coordinate, c2 == previous_coordinate) {
(true, false) => c2,
(false, true) => c1,
_ => return None,
};

if next_coordinate == start_coordinate {
break;
}

pipe = grid.pipe_at(next_coordinate)?;
previous_coordinate = coordinate;
coordinate = next_coordinate;

cycle.push(coordinate);
}

Some(cycle)
}

pub fn part_one(input: &str) -> Option<usize> {
let (start_coordinate, grid) = parse_grid(input);

let all_pipes = [
Pipe::Horizontal,
Pipe::Vertical,
Pipe::NorthEast,
Pipe::NorthWest,
Pipe::SouthWest,
Pipe::SouthEast,
];

all_pipes
.into_iter()
.filter_map(|pipe| find_cycle_starting_at(start_coordinate, pipe, &grid))
.next()
.map(|cycle| cycle.len() / 2)
}

pub fn part_two(input: &str) -> Option<isize> {
let (start_coordinate, grid) = parse_grid(input);

let all_pipes = [
Pipe::Horizontal,
Pipe::Vertical,
Pipe::NorthEast,
Pipe::NorthWest,
Pipe::SouthWest,
Pipe::SouthEast,
];

let cycle = all_pipes
.into_iter()
.filter_map(|pipe| find_cycle_starting_at(start_coordinate, pipe, &grid))
.next()
.unwrap();

// Pick's theorem enables us to easily compute the number of points based on the area of the
// polygon which is obtained using Shoelace's algorithm.
let area: isize = cycle
.iter()
.chain(std::iter::once(&cycle[0]))
.tuple_windows()
.map(|((x1, y1), (x2, y2))| (y1 + y2) * (x1 - x2))
.sum();

Some((area.abs() - cycle.len() as isize) / 2 + 1)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_part_one_1() {
let result = part_one(&advent_of_code::template::read_file_part(
"examples", DAY, 1,
));
assert_eq!(result, Some(4));
}

#[test]
fn test_part_one_2() {
let result = part_one(&advent_of_code::template::read_file_part(
"examples", DAY, 2,
));
assert_eq!(result, Some(8));
}

#[test]
fn test_part_two_1() {
let result = part_two(&advent_of_code::template::read_file_part(
"examples", DAY, 3,
));
assert_eq!(result, Some(4));
}

#[test]
fn test_part_two_2() {
let result = part_two(&advent_of_code::template::read_file_part(
"examples", DAY, 4,
));
assert_eq!(result, Some(8));
}

#[test]
fn test_part_two_3() {
let result = part_two(&advent_of_code::template::read_file_part(
"examples", DAY, 5,
));
assert_eq!(result, Some(10));
}
}

0 comments on commit f48f89f

Please sign in to comment.