diff --git a/src/aoc_2025/day_2.gleam b/src/aoc_2025/day_2.gleam new file mode 100644 index 0000000..302062f --- /dev/null +++ b/src/aoc_2025/day_2.gleam @@ -0,0 +1,93 @@ +import gleam/int +import gleam/list +import gleam/result +import gleam/string + +pub fn parse(input: String) -> List(Int) { + let assert Ok(values) = { + use values <- result.try( + string.split(input, ",") + |> list.map(string.split_once(_, "-")) + |> result.all, + ) + + use values <- result.try( + values + |> list.map(fn(val) { + use first <- result.try(int.parse(val.0)) + use second <- result.try(int.parse(val.1)) + + Ok(#(first, second)) + }) + |> result.all, + ) + + let values = list.flat_map(values, fn(val) { list.range(val.0, val.1) }) + + Ok(values) + } + as "Invalid input" + + values +} + +pub fn pt_1(input: List(Int)) { + input + |> list.filter(filter_pt_1) + |> list.fold(0, int.add) +} + +fn filter_pt_1(value: Int) -> Bool { + let value = int.to_string(value) + let length = string.length(value) + + case int.is_odd(length) { + True -> False + False -> { + let half = + value + |> string.drop_start(length / 2) + string.starts_with(value, half) + } + } +} + +pub fn pt_2(input: List(Int)) { + input + |> list.filter(pt_2_filter) + |> list.fold(0, int.add) +} + +fn pt_2_filter(value: Int) -> Bool { + let value = int.to_string(value) + let length = string.length(value) + + divisors(of: length, starting_at: length / 2, acc: []) + |> list.any(fn(divisor) { + let after_split_reassemble = + value + |> string.to_graphemes + |> list.sized_chunk(length / divisor) + |> list.map(list.fold(_, "", fn(a, b) { a <> b })) + |> list.unique + + case after_split_reassemble { + // there is one item left + // and it isnt just the initial string split and reassembled + [remaining] if remaining != value -> True + _ -> False + } + }) +} + +fn divisors( + of n: Int, + starting_at potential_divisor: Int, + acc acc: List(Int), +) -> List(Int) { + case n % potential_divisor { + _ if potential_divisor == 0 -> [n, ..acc] + 0 -> divisors(n, potential_divisor - 1, [potential_divisor, ..acc]) + _ -> divisors(n, potential_divisor - 1, acc) + } +} diff --git a/src/aoc_2025/day_3.gleam b/src/aoc_2025/day_3.gleam new file mode 100644 index 0000000..ef4c4de --- /dev/null +++ b/src/aoc_2025/day_3.gleam @@ -0,0 +1,106 @@ +import gleam/int +import gleam/list +import gleam/pair +import gleam/result +import gleam/string + +pub fn parse(input: String) { + string.split(input, "\n") + |> list.map(string.to_graphemes) + |> list.map(list.map(_, int.parse)) + |> list.map(parsed_to_batterybank) +} + +fn parsed_to_batterybank(parsed: List(Result(Int, Nil))) { + let assert Ok(value) = result.all(parsed) + BatteryBank(value) +} + +pub type BatteryBank { + BatteryBank(batteries: List(Int)) +} + +pub fn pt_1(input: List(BatteryBank)) { + input + |> list.map(fn(battery_bank) { + list.fold_right(battery_bank.batteries, #(0, 0), fn(largest, current) { + case largest { + // first loop + #(0, 0) -> #(0, current) + // second loop + #(0, last) -> #(current, last) + #(ten_spot, one_spot) if current >= ten_spot && ten_spot > one_spot -> #( + current, + ten_spot, + ) + #(ten_spot, one_spot) if current >= ten_spot && ten_spot <= one_spot -> #( + current, + one_spot, + ) + _ -> largest + } + }) + }) + |> list.map(fn(pair) { pair.0 * 10 + pair.1 }) + |> list.fold(0, int.add) +} + +pub fn pt_2(input: List(BatteryBank)) { + input + |> list.map(fn(bank) { + let length = list.length(bank.batteries) + + let #(left_to_check, initial_selection) = + bank.batteries + |> list.split(length - 12) + + loop(left_to_check:, initial_selection:, acc: []) + |> list.fold(#(1, 0), fn(acc, current_value) { + let #(multiplier, accumulated_value) = acc + + #(multiplier * 10, accumulated_value + current_value * multiplier) + }) + |> pair.second + }) + |> list.fold(0, int.add) +} + +fn loop( + left_to_check left_to_check, + initial_selection initial_selection, + acc result_selected, +) { + case initial_selection { + [] -> result_selected + [current, ..rest] -> { + let left_to_check = list.reverse(left_to_check) + + let #(result, left_to_check) = inner_loop(current, left_to_check, [], []) + + // echo result as "selected" + + loop(left_to_check, rest, [result, ..result_selected]) + } + } +} + +fn inner_loop(current, left_to_check, discard, passed_cause_smaller) { + // echo left_to_check as "left" + case left_to_check { + // nothing left to check, return selection and discard + [] -> #(current, discard) + // head >= current -> discard current, head is new current + [head, ..tail] if head >= current -> + inner_loop( + head, + tail, + list.append(passed_cause_smaller, [current, ..discard]), + [], + ) + // head < current -> temp_discard head, keep current + [head, ..tail] if head < current -> + inner_loop(current, tail, discard, [head, ..passed_cause_smaller]) + + [_, ..] -> panic as "what" + } +}