summaryrefslogtreecommitdiffstats
path: root/lib/day10.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/day10.ml')
-rw-r--r--lib/day10.ml110
1 files changed, 110 insertions, 0 deletions
diff --git a/lib/day10.ml b/lib/day10.ml
new file mode 100644
index 0000000..06be150
--- /dev/null
+++ b/lib/day10.ml
@@ -0,0 +1,110 @@
+(*
+ * SPDX-FileCopyrightText: Copyright 2025 Alexandre Jesus <https://adbjesus.com>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *)
+
+open Glpk.C.Functions
+open Glpk.C.Types
+
+type machine = { lights : int; buttons : int list list; ibuttons : int list; joltage : int list }
+
+let parse_machine s =
+ let a = String.split_on_char ' ' s in
+ let lights =
+ List.hd a
+ |> String.fold_left
+ (fun acc c ->
+ match c with
+ | '.' -> acc * 2
+ | '#' -> (acc * 2) + 1
+ | '[' | ']' -> acc
+ | _ -> failwith (Printf.sprintf "Wrong data: %s" s))
+ 0
+ in
+ let nlights = String.length (List.hd a) - 2 in
+ let parse_button s =
+ if s.[0] = '(' then
+ let sub = String.sub s 1 (String.length s - 2) in
+ Some (String.split_on_char ',' sub |> List.map int_of_string)
+ else None
+ in
+ let parse_ibutton b =
+ b
+ |> List.map (fun v ->
+ let v = nlights - v - 1 in
+ Int.shift_left 1 v)
+ |> List.fold_left ( + ) 0
+ in
+ let buttons = List.filter_map parse_button (List.tl a) in
+ let ibuttons = List.map parse_ibutton buttons in
+ let joltage = List.rev a |> List.hd in
+ let joltage = String.sub joltage 1 (String.length joltage - 2) in
+ let joltage = String.split_on_char ',' joltage |> List.map int_of_string in
+ { lights; buttons; ibuttons; joltage }
+
+let print_machines machines =
+ List.iter
+ (fun machine ->
+ Printf.printf "%d ->" machine.lights;
+ List.iter (Printf.printf " %d") machine.ibuttons;
+ Printf.printf " ->";
+ List.iter (Printf.printf " %d") machine.joltage;
+ Printf.printf "\n")
+ machines
+
+let parse ch = ch |> In_channel.input_lines |> List.map parse_machine
+
+let part1 ch =
+ let machines = parse ch in
+ let solve machine =
+ let rec fn v c bs =
+ if v = machine.lights then c
+ else
+ match bs with
+ | [] -> Int.max_int
+ | b :: bt -> Int.min (fn (Int.logxor v b) (c + 1) bt) (fn v c bt)
+ in
+ fn 0 0 machine.ibuttons
+ in
+ List.map solve machines |> List.fold_left ( + ) 0 |> Printf.printf "%d\n"
+
+let part2 ch =
+ let machines = parse ch in
+ let solve machine =
+ let p = glp_create_prob () in
+ let n = List.length machine.joltage in
+ let m = List.length machine.buttons in
+ glp_set_obj_dir p glp_min;
+ glp_add_rows p n;
+ machine.joltage
+ |> List.to_seq
+ |> Seq.map Float.of_int
+ |> Seq.iteri (fun i v -> glp_set_row_bnds p (i + 1) glp_fx v v);
+ glp_add_cols p m;
+ for i = 1 to m do
+ glp_set_col_bnds p i glp_lo 0.0 0.0;
+ glp_set_col_kind p i glp_iv;
+ glp_set_obj_coef p i 1.0;
+ done;
+ (machine.buttons
+ |> List.iteri
+ (fun i b ->
+ let len = List.length b in
+ let ind = 0 :: (List.map (( + ) 1) b) |> Ctypes.CArray.of_list Ctypes.int in
+ let vals = Ctypes.CArray.make Ctypes.double ~initial:1.0 (len + 1) in
+ glp_set_mat_col p (i + 1) len (Ctypes.CArray.start ind) (Ctypes.CArray.start vals)
+ ));
+ glp_simplex p Ctypes.null;
+ glp_intopt p Ctypes.null;
+ (if (glp_mip_status p) <> glp_opt then
+ failwith "Infeasible ILP");
+ let ans = glp_mip_obj_val p in
+ glp_delete_prob p;
+ Int.of_float (ans +. 0.1)
+ in
+ ignore (glp_term_out glp_off);
+ machines
+ |> List.map solve
+ |> List.fold_left ( + ) 0
+ |> Printf.printf "%d\n"