summaryrefslogtreecommitdiffstats
path: root/lib/day10.ml
blob: 06be150364b0ddfe84eddefad005187e67685862 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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"