(* * SPDX-FileCopyrightText: Copyright 2025 Alexandre Jesus * * 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"