mend

nnuuvv 456a9eef 910dc8c4

Changed files
+629
src
+192
src/aoc_2025/day_10.gleam
··· 1 + import crew 2 + import gleam/erlang/process 3 + import gleam/int 4 + import gleam/io 5 + import gleam/list 6 + import gleam/result 7 + import gleam/set.{type Set} 8 + import gleam/string 9 + 10 + pub type Joltages { 11 + Joltages(joltages: List(Int)) 12 + } 13 + 14 + pub type State { 15 + State(lamps: List(Bool)) 16 + } 17 + 18 + pub type Button { 19 + Button(toggles: List(Int)) 20 + } 21 + 22 + pub type Machine { 23 + Machine(target: State, buttons: List(Button), joltages: Joltages) 24 + } 25 + 26 + pub fn parse(input: String) -> List(Machine) { 27 + input 28 + |> string.split("\n") 29 + |> list.map(fn(line) { 30 + line 31 + |> string.split(" ") 32 + |> list.fold(Machine(State([]), [], Joltages([])), fn(acc, item) { 33 + let Machine(state, buttons, joltages) = acc 34 + 35 + case item { 36 + "[" <> state -> 37 + state 38 + |> string.drop_end(1) 39 + |> parse_state([]) 40 + |> Machine(buttons, joltages) 41 + 42 + "(" <> button -> 43 + button 44 + |> string.drop_end(1) 45 + |> parse_int_list 46 + |> Button 47 + |> fn(button) { Machine(state, [button, ..buttons], joltages) } 48 + 49 + "{" <> joltages -> 50 + joltages 51 + |> string.drop_end(1) 52 + |> parse_int_list 53 + |> Joltages 54 + |> fn(joltages) { Machine(state, buttons, joltages) } 55 + 56 + "" -> acc 57 + 58 + other -> { 59 + echo other 60 + panic as "huh" 61 + } 62 + } 63 + }) 64 + }) 65 + } 66 + 67 + fn parse_state(state_string: String, acc) { 68 + case state_string { 69 + "." <> rest -> parse_state(rest, [False, ..acc]) 70 + "#" <> rest -> parse_state(rest, [True, ..acc]) 71 + _ -> State(acc |> list.reverse) 72 + } 73 + } 74 + 75 + fn parse_int_list(button_string: String) { 76 + button_string 77 + |> string.split(",") 78 + |> list.map(int.parse) 79 + |> result.values 80 + } 81 + 82 + pub fn pt_1(input: List(Machine)) { 83 + input 84 + |> list.fold(0, fn(sum, machine) { 85 + sum + pt_1_loop(machine, [#(machine.target, 0)], set.new()) 86 + }) 87 + } 88 + 89 + fn pt_1_loop( 90 + machine: Machine, 91 + last_states: List(#(State, Int)), 92 + next_states: Set(#(State, Int)), 93 + ) { 94 + case last_states { 95 + [] -> pt_1_loop(machine, next_states |> set.to_list, set.new()) 96 + [#(state, num), ..rest] -> { 97 + // all off? 98 + case state.lamps |> list.all(fn(lamp) { !lamp }) { 99 + True -> num 100 + False -> { 101 + // click all buttons on this state 102 + machine.buttons 103 + |> list.fold(next_states, fn(next_states, button) { 104 + next_states 105 + |> set.insert(#(click_lamp_button(button, state), num + 1)) 106 + }) 107 + |> pt_1_loop(machine, rest, _) 108 + } 109 + } 110 + } 111 + } 112 + } 113 + 114 + fn click_lamp_button(button: Button, state: State) { 115 + state.lamps 116 + |> list.index_map(fn(lamp, index) { 117 + case button.toggles |> list.contains(index) { 118 + True -> !lamp 119 + False -> lamp 120 + } 121 + }) 122 + |> State 123 + } 124 + 125 + pub fn pt_2(input: List(Machine)) { 126 + let pool_name = process.new_name("worker_pool") 127 + let assert Ok(_) = 128 + crew.new(pool_name, run_pt_2_loop_for_machine) 129 + |> crew.fixed_size(input |> list.length) 130 + |> crew.start() 131 + 132 + input 133 + |> crew.call_parallel(pool_name, 120_000, _) 134 + |> list.fold(0, int.add) 135 + } 136 + 137 + fn run_pt_2_loop_for_machine(machine: Machine) { 138 + pt_2_loop( 139 + machine, 140 + [ 141 + #( 142 + list.repeat(0, times: list.length(machine.joltages.joltages)) 143 + |> Joltages, 144 + 0, 145 + ), 146 + ], 147 + set.new(), 148 + ) 149 + |> echo 150 + } 151 + 152 + fn pt_2_loop( 153 + machine: Machine, 154 + last_joltages: List(#(Joltages, Int)), 155 + next_joltages: Set(#(Joltages, Int)), 156 + ) { 157 + case last_joltages { 158 + [] -> pt_2_loop(machine, next_joltages |> set.to_list, set.new()) 159 + 160 + [#(joltages, num), ..rest] if num > 100 -> { 161 + echo machine 162 + 163 + num 164 + } 165 + [#(joltages, num), ..rest] -> { 166 + // all correct? 167 + case joltages.joltages == machine.joltages.joltages { 168 + True -> num 169 + False -> { 170 + // click all buttons on this state 171 + machine.buttons 172 + |> list.fold(next_joltages, fn(next_joltages, button) { 173 + next_joltages 174 + |> set.insert(#(click_joltage_button(button, joltages), num + 1)) 175 + }) 176 + |> pt_2_loop(machine, rest, _) 177 + } 178 + } 179 + } 180 + } 181 + } 182 + 183 + fn click_joltage_button(button: Button, joltages: Joltages) -> Joltages { 184 + joltages.joltages 185 + |> list.index_map(fn(joltage, index) { 186 + case button.toggles |> list.contains(index) { 187 + True -> joltage + 1 188 + False -> joltage 189 + } 190 + }) 191 + |> Joltages 192 + }
+195
src/aoc_2025/day_8.gleam
··· 1 + import gleam/bool 2 + import gleam/dict 3 + import gleam/float 4 + import gleam/int 5 + import gleam/list 6 + import gleam/option.{None, Some} 7 + import gleam/pair 8 + import gleam/string 9 + 10 + pub type Position { 11 + Position(x: Int, y: Int, z: Int) 12 + } 13 + 14 + type Edge { 15 + Edge(length: Int, a: Position, b: Position) 16 + } 17 + 18 + fn equal_edge(edge_a, edge_b) { 19 + let Edge(_, a1, b1) = edge_a 20 + let Edge(_, a2, b2) = edge_b 21 + 22 + a1 == a2 && b1 == b2 || a1 == b2 && a2 == b1 23 + } 24 + 25 + fn connecting_edge(edge_a, edge_b) { 26 + let Edge(_, a1, b1) = edge_a 27 + let Edge(_, a2, b2) = edge_b 28 + 29 + a1 == a2 || b1 == b2 || a1 == b2 || b1 == a2 30 + } 31 + 32 + pub fn parse(input: String) { 33 + input 34 + |> string.split("\n") 35 + |> list.map(string.split(_, ",")) 36 + |> list.map(fn(coords) { 37 + case coords { 38 + [x, y, z] -> { 39 + let assert Ok(x) = int.parse(x) 40 + let assert Ok(y) = int.parse(y) 41 + let assert Ok(z) = int.parse(z) 42 + Position(x:, y:, z:) 43 + } 44 + _ -> panic as "Invalid input" 45 + } 46 + }) 47 + } 48 + 49 + pub fn pt_1(input: List(Position)) { 50 + let distances = 51 + input 52 + |> list.fold(dict.new(), fn(distances, point) { 53 + list.fold(input, distances, fn(distances, npoint) { 54 + let dist = distance_3d(point, npoint) 55 + dict.upsert(distances, point, fn(item) { 56 + case item { 57 + Some(item) -> [Edge(dist, point, npoint), ..item] 58 + _ -> [Edge(dist, point, npoint)] 59 + } 60 + }) 61 + }) 62 + }) 63 + |> dict.map_values(fn(key, value) { 64 + list.filter(value, fn(item) { item.length != 0 }) 65 + }) 66 + 67 + let circuits = dict.new() 68 + pt_1_loop(distances, circuits, 1) 69 + // |> echo 70 + |> dict.values() 71 + |> list.map(fn(circuit) { 72 + list.fold(circuit, [], fn(acc, edge) { 73 + let is_in_acc = 74 + acc 75 + |> list.any(fn(in_acc) { equal_edge(in_acc, edge) }) 76 + case is_in_acc { 77 + True -> acc 78 + False -> [edge, ..acc] 79 + } 80 + }) 81 + }) 82 + |> list.map(list.length) 83 + |> list.sort(int.compare) 84 + |> echo 85 + |> list.take(3) 86 + |> echo 87 + |> list.reduce(fn(a, b) { a * b }) 88 + // todo as "part 1 not implemented" 89 + } 90 + 91 + fn pt_1_loop( 92 + distances: dict.Dict(Position, List(Edge)), 93 + circuits: dict.Dict(Int, List(Edge)), 94 + index_multi: Int, 95 + ) -> dict.Dict(Int, List(Edge)) { 96 + let distances = 97 + distances 98 + // filter out already assigned edges 99 + |> dict.map_values(fn(_, edges) { 100 + let circuits = 101 + circuits 102 + |> dict.values 103 + |> list.flatten 104 + 105 + edges 106 + |> list.filter(fn(edge) { 107 + circuits 108 + |> list.any(fn(in_circuit) { equal_edge(edge, in_circuit) }) 109 + |> bool.negate() 110 + }) 111 + }) 112 + 113 + let remaining = distances |> dict.values() |> list.flatten() |> list.length() 114 + 115 + case remaining == 0 { 116 + True -> circuits 117 + False -> { 118 + pt_1_loop( 119 + distances, 120 + pt_1_loop_inner(distances, circuits, index_multi), 121 + index_multi * 10, 122 + ) 123 + } 124 + } 125 + } 126 + 127 + fn pt_1_loop_inner(distances, circuits, index_multi) { 128 + distances 129 + |> dict.fold([], fn(smallest, _, distances) { 130 + let closest = closest(distances) 131 + [closest, ..smallest] 132 + }) 133 + |> list.filter_map(fn(item) { item }) 134 + // get shortest first 135 + |> list.sort(fn(a, b) { int.compare(a.length, b.length) }) 136 + // make sure there are no self referencing edges 137 + |> list.filter(fn(a) { a.length != 0 }) 138 + |> list.index_fold(circuits, fn(circuits, edge, index) { 139 + // are there any already existing circuits? 140 + let connected = 141 + dict.filter(circuits, fn(key, value) { 142 + list.any(value, fn(item) { connecting_edge(item, edge) }) 143 + }) 144 + 145 + let index = case connected |> dict.keys() { 146 + // yes? add to it 147 + [index, ..] -> index 148 + // no? add to current index 149 + _ -> index * index_multi 150 + } 151 + dict_append(circuits, index, edge) 152 + }) 153 + } 154 + 155 + fn dict_append(dict, key, item) { 156 + dict.upsert(dict, key, fn(in_dict) { 157 + case in_dict { 158 + Some(in_dict) -> [item, ..in_dict] |> list.unique() 159 + None -> [item] 160 + } 161 + }) 162 + } 163 + 164 + fn closest(distances: List(Edge)) { 165 + list.fold(distances, [], fn(smallest, item) { 166 + case smallest { 167 + [Edge(0, _, _)] | [] -> [item] 168 + [Edge(distance, _, _)] if distance > item.length -> [item] 169 + [Edge(distance, _, _)] if distance <= item.length -> smallest 170 + _ -> todo as "huh" 171 + } 172 + }) 173 + |> list.first() 174 + } 175 + 176 + fn distance_3d(point_a, point_b) { 177 + let Position(x1, y1, z1) = point_a 178 + let Position(x2, y2, z2) = point_b 179 + 180 + let xdistance = dist_1d(x1, x2) 181 + let ydistance = dist_1d(y1, y2) 182 + let zdistance = dist_1d(z1, z2) 183 + 184 + xdistance + ydistance + zdistance 185 + } 186 + 187 + fn dist_1d(c1, c2) { 188 + let diff = c2 - c1 189 + let dist = diff * diff 190 + dist 191 + } 192 + 193 + pub fn pt_2(input: List(Position)) { 194 + todo as "part 2 not implemented" 195 + }
+242
src/aoc_2025/day_9.gleam
··· 1 + import gleam/float 2 + import gleam/int 3 + import gleam/list 4 + import gleam/set 5 + import gleam/string 6 + 7 + pub fn parse(input: String) { 8 + input 9 + |> string.split("\n") 10 + |> list.map(fn(line) { 11 + let assert [x, y] = string.split(line, ",") 12 + let assert Ok(x) = int.parse(x) 13 + let assert Ok(y) = int.parse(y) 14 + Point(x:, y:) 15 + }) 16 + } 17 + 18 + pub type Point { 19 + Point(x: Int, y: Int) 20 + } 21 + 22 + pub type FloatPoint { 23 + FloatPoint(x: Float, y: Float) 24 + } 25 + 26 + pub type Area { 27 + Area(a: Point, b: Point, area: Int) 28 + } 29 + 30 + pub type Area4Point { 31 + Area4Point( 32 + a: FloatPoint, 33 + b: FloatPoint, 34 + c: FloatPoint, 35 + d: FloatPoint, 36 + area: Float, 37 + ) 38 + } 39 + 40 + pub fn pt_1(input: List(Point)) { 41 + input 42 + |> list.fold(set.new(), fn(areas, point_a) { 43 + input 44 + |> list.fold(areas, fn(areas, point_b) { 45 + let area = 46 + { int.absolute_value(point_a.x - point_b.x) + 1 } 47 + * { int.absolute_value(point_a.y - point_b.y) + 1 } 48 + 49 + areas 50 + |> set.insert(Area(point_a, point_b, area)) 51 + }) 52 + }) 53 + |> set.fold(0, fn(acc, area) { 54 + case area { 55 + Area(a:, b:, area:) if area > acc -> area 56 + _ -> acc 57 + } 58 + }) 59 + } 60 + 61 + pub fn pt_2(input: List(Point)) { 62 + // echo intersect(#(Point(0, 0), Point(10, 10)), #(Point(0, 5), Point(0, 2))) 63 + 64 + // let mid_x = { largest_x - smallest_x } / 2 65 + // |> echo 66 + // let mid_y = { largest_y - smallest_y } / 2 67 + // |> echo 68 + 69 + let input = 70 + input 71 + |> list.map(fn(point) { 72 + FloatPoint(int.to_float(point.x), int.to_float(point.y)) 73 + }) 74 + 75 + input 76 + |> list.fold(set.new(), fn(acc, point_a) { 77 + input 78 + |> list.fold(acc, fn(acc, point_b) { 79 + case point_a, point_b { 80 + point_a, point_b if point_a == point_b -> acc 81 + 82 + _, _ -> acc |> set.insert(other_corners(point_a, point_b)) 83 + } 84 + }) 85 + }) 86 + |> set.filter(fn(area) { 87 + let Area4Point(_, _, c, d, _) = area 88 + let assert Ok(first) = list.first(input) 89 + 90 + let input = list.append(input, [first]) 91 + 92 + // case a { 93 + // FloatPoint(x:, y:) if x == 11.0 && y == 1.0 && area.area == 50.0 -> { 94 + // echo area 95 + // echo count_intersections(of: c, in: input, acc: 0) as "c" 96 + // echo count_intersections(of: d, in: input, acc: 0) as "d" 97 + // Nil 98 + // } 99 + // _ -> Nil 100 + // } 101 + 102 + count_intersections(of: c, in: input, acc: 0) |> int.is_odd() 103 + && count_intersections(of: d, in: input, acc: 0) |> int.is_odd() 104 + // #( 105 + // area, 106 + // count_intersections(of: c, in: input, acc: 0), 107 + // count_intersections(of: d, in: input, acc: 0), 108 + // ) 109 + // False 110 + }) 111 + |> echo 112 + |> set.fold(0.0, fn(acc, area) { 113 + case area { 114 + Area4Point(a: _, b: _, c: _, d: _, area:) if area >. acc -> area 115 + _ -> acc 116 + } 117 + }) 118 + // |> echo 119 + } 120 + 121 + fn count_intersections( 122 + of intersecting_point: FloatPoint, 123 + in all_points: List(FloatPoint), 124 + acc sum: Int, 125 + ) { 126 + case all_points { 127 + [] | [_] -> sum 128 + [a, b, ..rest] -> { 129 + case a { 130 + a if a == intersecting_point -> 1 131 + _ -> { 132 + case 133 + intersect( 134 + line: #(FloatPoint(0.0, 0.0), intersecting_point), 135 + on_line: #(a, b), 136 + ) 137 + { 138 + True -> { 139 + // echo string.inspect(a) <> " | " <> string.inspect(b) as "a-b, true" 140 + count_intersections(intersecting_point, [b, ..rest], sum + 1) 141 + } 142 + False -> count_intersections(intersecting_point, [b, ..rest], sum) 143 + } 144 + } 145 + } 146 + } 147 + 148 + s -> { 149 + echo s 150 + panic as "huh" 151 + } 152 + } 153 + } 154 + 155 + fn other_corners(point_a: FloatPoint, point_b: FloatPoint) { 156 + let area = 157 + { float.absolute_value(point_a.x -. point_b.x) +. 1.0 } 158 + *. { float.absolute_value(point_a.y -. point_b.y) +. 1.0 } 159 + 160 + Area4Point( 161 + point_a, 162 + point_b, 163 + FloatPoint(point_a.x, point_b.y), 164 + FloatPoint(point_b.x, point_a.y), 165 + area, 166 + ) 167 + } 168 + 169 + fn intersect( 170 + line line_a: #(FloatPoint, FloatPoint), 171 + on_line line_b: #(FloatPoint, FloatPoint), 172 + ) { 173 + let #(FloatPoint(x1, y1), FloatPoint(x2, y2)) = line_a 174 + let #(FloatPoint(x3, y3), FloatPoint(x4, y4)) = line_b 175 + 176 + let denominator = { x1 -. x2 } *. { y3 -. y4 } -. { y1 -. y2 } *. { x3 -. x4 } 177 + 178 + case denominator { 179 + 0.0 -> False 180 + _ -> { 181 + let px = 182 + { 183 + { x1 *. y2 -. y1 *. x2 } 184 + *. { x3 -. x4 } 185 + -. { x1 -. x2 } 186 + *. { x3 *. y4 -. y3 *. x4 } 187 + } 188 + /. denominator 189 + 190 + let py = 191 + { 192 + { x1 *. y2 -. y1 *. x2 } 193 + *. { y3 -. y4 } 194 + -. { y1 -. y2 } 195 + *. { x3 *. y4 -. y3 *. x4 } 196 + } 197 + /. denominator 198 + 199 + echo string.inspect(line_a) 200 + <> " intersects " 201 + <> string.inspect(line_b) 202 + <> " at " 203 + <> string.inspect(FloatPoint(px, py)) 204 + <> " on line_b?: " 205 + <> string.inspect(on_line(line_b, FloatPoint(px, py))) 206 + 207 + on_line(line_b, FloatPoint(px, py)) 208 + } 209 + } 210 + } 211 + 212 + fn on_line(line: #(FloatPoint, FloatPoint), point: FloatPoint) { 213 + case line { 214 + #(a, b) 215 + if { 216 + a.x >=. point.x 217 + && b.x <=. point.x 218 + && a.y >=. point.y 219 + && b.y <=. point.y 220 + } 221 + || { 222 + a.x >=. point.x 223 + && b.x <=. point.x 224 + && a.y <=. point.y 225 + && b.y >=. point.y 226 + } 227 + || { 228 + a.x <=. point.x 229 + && b.x >=. point.x 230 + && a.y >=. point.y 231 + && b.y <=. point.y 232 + } 233 + || { 234 + a.x <=. point.x 235 + && b.x >=. point.x 236 + && a.y <=. point.y 237 + && b.y >=. point.y 238 + } 239 + -> True 240 + _ -> False 241 + } 242 + }