1- import gleam/deque
21import gleam/dict
3- import gleam/erlang/process
42import gleam/int
53import gleam/io
64import gleam/list
75import gleam/option
6+ import gleam/order
87import gleam/result
98import gleam/set
109import 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).
6566pub 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 }
0 commit comments