Skip to content

Commit

Permalink
feat: add 2023/10
Browse files Browse the repository at this point in the history
  • Loading branch information
mtsknn committed Oct 17, 2024
1 parent 5ccdbc2 commit 5e1697c
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 0 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,28 @@ Though note that the test assertions use my personal results
and calculating the rank of cards by handling the cards as (hexadecimal) numbers.
- [`year_2023/day_08.gleam`](./src/year_2023/day_08.gleam) × [adventofcode.com/2023/day/8](https://adventofcode.com/2023/day/8)
- [`year_2023/day_09.gleam`](./src/year_2023/day_09.gleam) × [adventofcode.com/2023/day/9](https://adventofcode.com/2023/day/9)
- [`year_2023/day_10.gleam`](./src/year_2023/day_10.gleam) × [adventofcode.com/2023/day/10](https://adventofcode.com/2023/day/10)

- Ahh, I love how [eleganto](https://www.youtube.com/watch?v=Ywr5E_q8hiM) my solution is.

Part 2 is impossible (fight me) without specific math knowledge –
which I didn't have,
so I had to do some digging.

At first I tried to loop over all points not on the path
and check if each point is inside or outside the path.
Based on [_Point in polygon_ on Wikipedia](https://en.wikipedia.org/wiki/Point_in_polygon),
I tried a ray casting algorithm.

Well, it didn't work,
so I went to [/r/adventofcode](https://old.reddit.com/r/adventofcode/) to look for spoilers.
I found two curious terms:

- [_Shoelace formula_ on Wikipedia](https://en.wikipedia.org/wiki/Shoelace_formula)
- [_Pick's theorem_ on Wikipedia](https://en.wikipedia.org/wiki/Pick%27s_theorem)

A-ha, so instead of looping over all non-path points,
I need to treat the path as a polygon,
and then calculate the polygon's area,
and then subtract the amount of boundary (non-interior) points from the area...
who would have thought!
126 changes: 126 additions & 0 deletions src/year_2023/day_10.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import gleam/bool
import gleam/dict.{type Dict}
import gleam/int
import gleam/list
import gleam/result
import gleam/string

type Coord {
Coord(x: Int, y: Int)
}

type Direction {
N
E
S
W
}

type Map =
Dict(Coord, String)

pub fn part_1(input: String) -> Int {
let path = input |> find_path

list.length(path) / 2
}

pub fn part_2(input: String) -> Int {
let path = input |> find_path

count_points(inside: path)
}

fn find_path(input: String) -> List(Coord) {
let map =
input
|> string.trim
|> string.split("\n")
|> list.index_map(fn(row, y) {
row
|> string.to_graphemes
|> list.index_map(fn(value, x) {
let coord = Coord(x:, y:)
#(coord, value)
})
})
|> list.flatten
|> dict.from_list

let assert Ok(starting_coord) =
map
|> dict.keys
|> list.find(fn(coord) { map |> dict.get(coord) == Ok("S") })

let assert Ok(path) =
[N, E, S]
|> list.find_map(fn(direction) {
let path = [starting_coord]
try_walk(from: starting_coord, towards: direction, on: map, along: path)
})

path
}

fn try_walk(
from current_coord: Coord,
towards direction: Direction,
on map: Map,
along path: List(Coord),
) -> Result(List(Coord), Nil) {
let next_coord = case direction {
N -> Coord(..current_coord, y: current_coord.y - 1)
E -> Coord(..current_coord, x: current_coord.x + 1)
S -> Coord(..current_coord, y: current_coord.y + 1)
W -> Coord(..current_coord, x: current_coord.x - 1)
}
let next_value = map |> dict.get(next_coord) |> result.unwrap(".")

use <- bool.guard(when: next_value == "S", return: Ok(path))

let path = [next_coord, ..path]
case direction, next_value {
N, "|" -> try_walk(from: next_coord, towards: N, on: map, along: path)
N, "F" -> try_walk(from: next_coord, towards: E, on: map, along: path)
N, "7" -> try_walk(from: next_coord, towards: W, on: map, along: path)

E, "J" -> try_walk(from: next_coord, towards: N, on: map, along: path)
E, "-" -> try_walk(from: next_coord, towards: E, on: map, along: path)
E, "7" -> try_walk(from: next_coord, towards: S, on: map, along: path)

S, "L" -> try_walk(from: next_coord, towards: E, on: map, along: path)
S, "|" -> try_walk(from: next_coord, towards: S, on: map, along: path)
S, "J" -> try_walk(from: next_coord, towards: W, on: map, along: path)

W, "F" -> try_walk(from: next_coord, towards: S, on: map, along: path)
W, "-" -> try_walk(from: next_coord, towards: W, on: map, along: path)
W, "L" -> try_walk(from: next_coord, towards: N, on: map, along: path)

_, ___ -> Error(Nil)
}
}

/// Using Pick's theorem: https://en.wikipedia.org/wiki/Pick%27s_theorem
fn count_points(inside path: List(Coord)) -> Int {
let area = calculate_polygon_area(of: path)

let boundary_points = list.length(path)
let interior_points = area - boundary_points / 2 + 1

interior_points
}

/// Using the Shoelace formula: https://en.wikipedia.org/wiki/Shoelace_formula
fn calculate_polygon_area(of path_coords: List(Coord)) -> Int {
let assert Ok(first_coord) = path_coords |> list.first

list.append(path_coords, [first_coord])
|> list.window_by_2
|> list.map(fn(corner_pair) {
let #(Coord(x: x1, y: y1), Coord(x: x2, y: y2)) = corner_pair
x1 * y2 - x2 * y1
})
|> int.sum
|> fn(sum) { sum / 2 }
|> int.absolute_value
}
103 changes: 103 additions & 0 deletions test/year_2023/day_10_test.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import glacier/should
import gleam/result
import utils
import year_2023/day_10

pub fn part_1_with_example_input_test() {
day_10.part_1(part_1_example_input_1)
|> should.equal(4)

day_10.part_1(part_1_example_input_2)
|> should.equal(8)
}

pub fn part_2_with_example_input_test() {
day_10.part_2(part_2_example_input_1)
|> should.equal(4)

day_10.part_2(part_2_example_input_2)
|> should.equal(4)

day_10.part_2(part_2_example_input_3)
|> should.equal(8)

day_10.part_2(part_2_example_input_4)
|> should.equal(10)
}

pub fn full_input_test() {
utils.read_input(2023, 10)
|> result.map(fn(input) {
day_10.part_1(input)
|> should.equal(6931)

day_10.part_2(input)
|> should.equal(357)
})
}

const part_1_example_input_1 = "
-L|F7
7S-7|
L|7||
-L-J|
L|-JF
"

const part_1_example_input_2 = "
7-F7-
.FJ|7
SJLL7
|F--J
LJ.LJ
"

const part_2_example_input_1 = "
...........
.S-------7.
.|F-----7|.
.||.....||.
.||.....||.
.|L-7.F-J|.
.|..|.|..|.
.L--J.L--J.
...........
"

const part_2_example_input_2 = "
..........
.S------7.
.|F----7|.
.||....||.
.||....||.
.|L-7F-J|.
.|..||..|.
.L--JL--J.
..........
"

const part_2_example_input_3 = "
.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...
"

const part_2_example_input_4 = "
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
"

0 comments on commit 5e1697c

Please sign in to comment.