Skip to content

Commit 481e8ea

Browse files
committed
finished day 10 and advent of code
1 parent eea1c38 commit 481e8ea

File tree

3 files changed

+112
-67
lines changed

3 files changed

+112
-67
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Advent of Code 2025
22

33
[![Tests](https://github.com/devries/advent_of_code_2025/actions/workflows/test.yml/badge.svg)](https://github.com/devries/advent_of_code_2025/actions/workflows/test.yml)
4-
[![Stars: 22](https://img.shields.io/badge/⭐_Stars-22-yellow)](https://adventofcode.com/2025)
4+
[![Stars: 24](https://img.shields.io/badge/⭐_Stars-24-yellow)](https://adventofcode.com/2025)
55

66
This year will be my second year doing Advent of Code in [Gleam](https://gleam.run).
77
Last year I was still learning the language, and in the past year I have used it
@@ -67,19 +67,22 @@ information.
6767

6868
After adding detection of figure edges intersecting box edges, it worked.
6969

70-
- [Day 10](https://adventofcode.com/2025/day/10): [ solution](src/day10/solution.gleam)
70+
- [Day 10](https://adventofcode.com/2025/day/10): [ solution](src/day10/solution.gleam)
7171

7272
I tried doing part 2 a naive way with some memoization, but it seems like
7373
I still need to work on it. Code was getting very convoluted anyway, not
7474
pretty like the first several days.
7575

76+
LittleLily in the Gleam discord references [this post](https://www.reddit.com/r/adventofcode/comments/1pk87hl/2025_day_10_part_2_bifurcate_your_way_to_victory/)
77+
on reddit which I followed to eventually complete part 2.
78+
7679
- [Day 11](https://adventofcode.com/2025/day/11): [⭐ ⭐ solution](src/day11/solution.gleam)
7780

7881
Back on track with today's problem. This is a straightforward depth-first
7982
search, but I add memoization so I don't have to keep revisiting paths
8083
I have counted before.
8184

82-
- [Day 12](https://adventofcode.com/2025/day/12): [ solution](src/day12/solution.gleam)
85+
- [Day 12](https://adventofcode.com/2025/day/12): [ solution](src/day12/solution.gleam)
8386

8487
As I was filtering my input for cases that would require rotation and
8588
flipping I realized that none did. There was either enough room for a

src/day10/solution.gleam

Lines changed: 99 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import gleam/deque
21
import gleam/dict
3-
import gleam/erlang/process
42
import gleam/int
53
import gleam/io
64
import gleam/list
75
import gleam/option
6+
import gleam/order
87
import gleam/result
98
import gleam/set
109
import gleam/string
@@ -62,6 +61,8 @@ pub fn solve_p1(lines: List(String)) -> Result(String, String) {
6261
}
6362

6463
// Part 2
64+
// see https://www.reddit.com/r/adventofcode/comments/1pk87hl/2025_day_10_part_2_bifurcate_your_way_to_victory/
65+
// for an explanation of this method (I did not come up with it).
6566
pub fn solve_p2(lines: List(String)) -> Result(String, String) {
6667
lines
6768
|> list.map(fn(row) {
@@ -71,23 +72,17 @@ pub fn solve_p2(lines: List(String)) -> Result(String, String) {
7172
let assert Ok(count_string) = list.first(count_list)
7273
let joltages = parse_joltages(count_string)
7374

74-
echo joltages
7575
let assert Ok(button_strings) = dict.get(parts, "buttons")
7676
let buttons =
7777
button_strings
7878
|> list.map(fn(one_button) {
7979
parse_button(one_button) |> button_to_list(list.length(joltages))
8080
})
8181

82-
use cache <- memoize.with_cache()
82+
let patterns = parity_patterns(buttons)
83+
// patterns |> dict.to_list |> list.map(fn(v) { echo v })
8384

84-
let queue =
85-
list.fold(buttons, deque.new(), fn(acc, b) {
86-
deque.push_back(acc, #(b, 1))
87-
})
88-
89-
find_p2_solution(cache, queue, buttons, joltages)
90-
|> echo
85+
solve_one_joltage(patterns, joltages)
9186
})
9287
|> int.sum
9388
|> int.to_string
@@ -191,63 +186,103 @@ fn list_subtract(left: List(Int), right: List(Int)) -> List(Int) {
191186
list.map(pairs, fn(tup) { tup.0 - tup.1 })
192187
}
193188

194-
fn find_p2_solution(
195-
cache: memoize.Cache(List(Int), Int),
196-
queue: deque.Deque(#(List(Int), Int)),
197-
buttons: List(List(Int)),
198-
joltages: List(Int),
199-
) -> Int {
200-
case deque.pop_front(queue) {
189+
fn is_not_negative(joltages: List(Int)) -> Bool {
190+
joltages
191+
|> list.filter(fn(v) { v < 0 })
192+
|> list.length
193+
|> fn(l) { l == 0 }
194+
}
195+
196+
fn is_zero(joltages: List(Int)) -> Bool {
197+
joltages
198+
|> list.filter(fn(v) { v != 0 })
199+
|> list.length
200+
|> fn(l) { l == 0 }
201+
}
202+
203+
fn parity_patterns(buttons: List(List(Int))) -> dict.Dict(List(Int), Int) {
204+
list.range(1, list.length(buttons))
205+
|> list.fold(dict.new(), fn(patterns, pushes) {
206+
list.combinations(buttons, pushes)
207+
|> list.fold(patterns, fn(acc, button_presses) {
208+
let result = case list.reduce(button_presses, list_add) {
209+
Ok(r) -> r
210+
Error(Nil) -> panic as "no buttons?"
211+
}
212+
dict.upsert(acc, result, fn(current) {
213+
let push_count = list.length(button_presses)
214+
case current {
215+
option.None -> push_count
216+
option.Some(v) -> int.min(push_count, v)
217+
}
218+
})
219+
})
220+
})
221+
}
222+
223+
fn solve_one_joltage(patterns: dict.Dict(List(Int), Int), joltages: List(Int)) {
224+
use cache <- memoize.with_cache()
225+
226+
case bifurcation_solution(patterns, joltages, cache) {
227+
Ok(v) -> v
201228
Error(Nil) -> panic as "no solution found"
202-
Ok(#(#(current, pushes), new_deque)) -> {
203-
let new_sums =
204-
buttons
205-
|> list.map(list_add(_, current))
206-
207-
let remains =
208-
list.map(new_sums, list_subtract(joltages, _))
209-
// remove negatives
210-
|> list.filter(fn(l) {
211-
list.fold_until(l, True, fn(_, element) {
212-
case element < 0 {
213-
True -> list.Stop(False)
214-
False -> list.Continue(True)
215-
}
216-
})
217-
})
229+
}
230+
}
231+
232+
fn bifurcation_solution(
233+
patterns: dict.Dict(List(Int), Int),
234+
joltages: List(Int),
235+
cache: memoize.Cache(List(Int), Result(Int, Nil)),
236+
) -> Result(Int, Nil) {
237+
use <- memoize.cache_check(cache, joltages)
238+
case is_zero(joltages) {
239+
True -> Ok(0)
240+
False -> {
241+
let parity = list.map(joltages, fn(v) { v % 2 })
242+
243+
let possible_counts =
244+
patterns
245+
|> dict.keys
246+
|> list.filter(fn(p) { list.map(p, fn(v) { v % 2 }) == parity })
247+
|> list.filter_map(fn(p) {
248+
let remaining_jolts = list_subtract(joltages, p)
249+
let assert Ok(press_count) = dict.get(patterns, p)
218250

219-
// check if you found it
220-
case list.contains(remains, list.repeat(0, list.length(joltages))) {
221-
True -> pushes + 1
222-
False -> {
223-
// check if the cache has a way to complete this value
224-
let cache_result =
225-
remains
226-
|> list.map(fn(k) { process.call(cache, 100, memoize.Get(_, k)) })
227-
|> list.fold_until(0, fn(_, cache_value) {
228-
case cache_value {
229-
Ok(v) -> list.Stop(v + pushes + 1)
230-
Error(Nil) -> list.Continue(0)
251+
case is_not_negative(remaining_jolts) {
252+
True ->
253+
case
254+
bifurcation_solution(
255+
patterns,
256+
list.map(remaining_jolts, fn(v) { v / 2 }),
257+
cache,
258+
)
259+
{
260+
Ok(n) -> Ok({ 2 * n } + press_count)
261+
Error(Nil) -> Error(Nil)
231262
}
232-
})
233-
234-
case cache_result {
235-
0 -> {
236-
// add results to cache, queue, and keep going
237-
list.each(new_sums, fn(sum) {
238-
process.send(cache, memoize.Put(sum, pushes + 1))
239-
})
240-
241-
let new_deque =
242-
list.fold(new_sums, new_deque, fn(acc, sum) {
243-
deque.push_back(acc, #(sum, pushes + 1))
244-
})
245-
246-
find_p2_solution(cache, new_deque, buttons, joltages)
247-
}
248-
v -> v
263+
False -> Error(Nil)
249264
}
250-
}
265+
})
266+
267+
let all_counts = case is_zero(parity) {
268+
True ->
269+
case
270+
bifurcation_solution(
271+
patterns,
272+
list.map(joltages, fn(v) { v / 2 }),
273+
cache,
274+
)
275+
{
276+
Ok(n) -> [2 * n, ..possible_counts]
277+
Error(Nil) -> possible_counts
278+
}
279+
False -> possible_counts
280+
}
281+
282+
case all_counts {
283+
[] -> Error(Nil)
284+
_ ->
285+
list.max(all_counts, fn(a, b) { int.compare(a, b) |> order.negate })
251286
}
252287
}
253288
}

test/day10_test.gleam

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ pub fn part2_test() {
1414
let lines = string.split(testinput, "\n")
1515
assert solution.solve_p2(lines) == Ok("33")
1616
}
17+
18+
pub fn panic_bug_test() {
19+
let lines = [
20+
"[#...##] (0,4,5) (3,4) (0,1,2,3,5) (1,2) (1) {13,28,18,18,19,13}",
21+
]
22+
solution.solve_p2(lines)
23+
}

0 commit comments

Comments
 (0)