Advent of Code solutions
at main 121 lines 3.8 kB view raw
1import "trilogy:io" use readline 2import "trilogy:debug" use dbg 3import "trilogy:iterator" as it 4import "trilogy:array" use collect, take, skip, length, all 5import "trilogy:heap" use heap 6import "trilogy:record" use contains_key 7import "trilogy:compare" use min 8 9slot GOAL = [ 10 ['open, 'open, 'empty, 'open, 'empty, 'open, 'empty, 'open, 'empty, 'open, 'open], 11 ['A', 'A', 'A', 'A'], 12 ['B', 'B', 'B', 'B'], 13 ['C', 'C', 'C', 'C'], 14 ['D', 'D', 'D', 'D'], 15] 16 17func replace pos val array = [ 18 ..take pos array, 19 val, 20 ..skip (pos + 1) array, 21] 22 23func cost 'A' = 1 24func cost 'B' = 10 25func cost 'C' = 100 26func cost 'D' = 1000 27 28proc eject!(value, position, depth, [spaces, ..stacks]) { 29 for pos in collect <| it::range position 10 { 30 match spaces.pos { 31 case 'open then yield 'next([replace pos value spaces, ..stacks]:cost value * (pos - position + depth)) 32 case 'empty then continue unit 33 else break unit 34 } 35 } 36 for pos in collect <| it::range position 0 { 37 match spaces.pos { 38 case 'open then yield 'next([replace pos value spaces, ..stacks]:cost value * (position - pos + depth)) 39 case 'empty then continue unit 40 else break unit 41 } 42 } 43} 44 45proc fall_in!(value, position, depth, [spaces, ..stacks]) { 46 for pos in collect <| it::range position 10 { 47 match spaces.pos { 48 case ^value then yield 'next([replace pos 'open spaces, ..stacks]:cost value * (pos - position + depth)) 49 case 'empty then continue unit 50 case 'open then continue unit 51 } 52 break unit 53 } 54 for pos in collect <| it::range position 0 { 55 match spaces.pos { 56 case ^value then yield 'next([replace pos 'open spaces, ..stacks]:cost value * (position - pos + depth)) 57 case 'empty then continue unit 58 case 'open then continue unit 59 } 60 break unit 61 } 62} 63 64func next_states [spaces, aa, bb, cc, dd] = do() { 65 match aa { 66 case ['A', 'A', 'A', 'A'] {} 67 case aas if all ((==) 'A') aas then fall_in!('A', 2, 4 - length aas, [spaces, [..aas, 'A'], bb, cc, dd]) 68 case [..aas, v] then eject!(v, 2, 4 - length aas, [spaces, aas, bb, cc, dd]) 69 } 70 match bb { 71 case ['C', 'C', 'C', 'C'] {} 72 case bbs if all ((==) 'B') bbs then fall_in!('B', 4, 4 - length bbs, [spaces, aa, [..bbs, 'B'], cc, dd]) 73 case [..bbs, v] then eject!(v, 4, 4 - length bbs, [spaces, aa, bbs, cc, dd]) 74 } 75 match cc { 76 case ['C', 'C', 'C', 'C'] {} 77 case ccs if all ((==) 'C') ccs then fall_in!('C', 6, 4 - length ccs, [spaces, aa, bb, [..ccs, 'C'], dd]) 78 case [..ccs, v] then eject!(v, 6, 4 - length ccs, [spaces, aa, bb, ccs, dd]) 79 } 80 match dd { 81 case ['D', 'D', 'D', 'D'] {} 82 case dds if all ((==) 'D') dds then fall_in!('D', 8, 4 - length dds, [spaces, aa, bb, cc, [..dds, 'D']]) 83 case [..dds, v] then eject!(v, 8, 4 - length dds, [spaces, aa, bb, cc, dds]) 84 } 85} 86 87proc main!() { 88 readline!() 89 readline!() 90 let upper = readline!() 91 let lower = readline!() 92 let initial = [ 93 ['open, 'open, 'empty, 'open, 'empty, 'open, 'empty, 'open, 'empty, 'open, 'open], 94 [lower.3, 'D', 'D', upper.3], 95 [lower.5, 'B', 'C', upper.5], 96 [lower.7, 'A', 'B', upper.7], 97 [lower.9, 'C', 'A', upper.9], 98 ] 99 let min_heap = heap (fn _:a _:b. a <= b) 100 let queue = [initial:0] 101 let cache = {|"${initial}" => 0|} 102 while queue != [] { 103 let state:score = min_heap::pop!(queue) 104 if cache."${state}" < score { 105 continue unit 106 } 107 if state == GOAL { 108 dbg!(score) 109 exit 0 110 } 111 next_states state 112 |> it::map (fn state:cost. state:cost + score) 113 |> it::for_each do(entry and next_state:next_score) { 114 let key = "${next_state}" 115 if !(contains_key key cache) || cache.key > next_score { 116 cache.key = next_score 117 min_heap::push!(queue, entry) 118 } 119 } 120 } 121}