Skip to content

Commit

Permalink
feat: add 2023/15
Browse files Browse the repository at this point in the history
  • Loading branch information
mtsknn committed Oct 23, 2024
1 parent dc86d26 commit 44eb97c
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,5 @@ Though note that the test assertions use my personal results
a poor result,
which I attribute to Gleam's immutable data structures.
(Mutating lists/arrays could maybe be significantly faster.)

- [**`year_2023/day_15.gleam`**](./src/year_2023/day_15.gleam) × [adventofcode.com/2023/day/15](https://adventofcode.com/2023/day/15)
105 changes: 105 additions & 0 deletions src/year_2023/day_15.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import gleam/dict.{type Dict}
import gleam/int
import gleam/list
import gleam/option.{None, Some}
import gleam/regex
import gleam/result
import gleam/string

type Boxes =
Dict(Int, List(Lens))

type Lens {
Lens(label: String, focal_length: Int)
}

pub fn part_1(input: String) -> Int {
input
|> to_words
|> list.map(hash_word)
|> int.sum
}

pub fn part_2(input: String) -> Int {
let assert Ok(operation) = regex.from_string("[=-]")

input
|> to_words
|> list.fold(dict.new(), fn(boxes, word) {
let assert [label, focal_length] = word |> regex.split(with: operation)

let box_number = label |> hash_word

case focal_length |> int.parse {
Ok(focal_length) -> {
let lens = Lens(label:, focal_length:)
boxes |> upsert(lens, to: box_number)
}
Error(Nil) -> {
boxes |> remove(label, from: box_number)
}
}
})
|> dict.fold(0, fn(sum, box_number, lenses) {
let focusing_power =
lenses
|> list.index_map(fn(lens, index) {
{ box_number + 1 } * { index + 1 } * lens.focal_length
})
|> int.sum

sum + focusing_power
})
}

fn to_words(input: String) -> List(String) {
input
|> string.trim
|> string.split(",")
}

fn hash_word(word: String) -> Int {
word
|> string.to_graphemes
|> list.fold(0, fn(hash_value, char) {
let assert [utf_codepoint] = char |> string.to_utf_codepoints
let ascii_code = utf_codepoint |> string.utf_codepoint_to_int
{ hash_value + ascii_code } * 17 % 256
})
}

fn upsert(boxes: Boxes, lens: Lens, to box_number: Int) -> Boxes {
boxes
|> dict.upsert(box_number, fn(lenses) {
case lenses {
None -> [lens]
Some(lenses) ->
lenses
|> update(lens, acc: [])
|> result.unwrap(list.append(lenses, [lens]))
}
})
}

fn update(
lenses: List(Lens),
lens: Lens,
acc acc: List(Lens),
) -> Result(List(Lens), Nil) {
case lenses {
[] -> Error(Nil)
[Lens(label, ..), ..rest] if label == lens.label ->
Ok(list.append(acc |> list.reverse, [lens, ..rest]))
[first, ..rest] -> update(rest, lens, [first, ..acc])
}
}

fn remove(boxes: Boxes, label: String, from box_number: Int) -> Boxes {
boxes
|> dict.upsert(box_number, fn(lenses) {
case lenses {
None -> []
Some(lenses) -> lenses |> list.filter(fn(lens) { lens.label != label })
}
})
}
29 changes: 29 additions & 0 deletions test/year_2023/day_15_test.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import glacier/should
import gleam/result
import utils
import year_2023/day_15

pub fn part_1_with_example_input_test() {
day_15.part_1(example_input)
|> should.equal(1320)
}

pub fn part_2_with_example_input_test() {
day_15.part_2(example_input)
|> should.equal(145)
}

pub fn full_input_test() {
utils.read_input(2023, 15)
|> result.map(fn(input) {
day_15.part_1(input)
|> should.equal(514_025)

day_15.part_2(input)
|> should.equal(244_461)
})
}

const example_input = "
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
"

0 comments on commit 44eb97c

Please sign in to comment.