this repo has no description
at master 4.5 kB view raw
1# Day 10 2 3```elixir 4Mix.install([:kino_aoc, {:dantzig, github: "hauleth/dantzig", ref: "use-proper-binary"}], 5config: [ 6 dantzig: [ 7 highs_binary_path: System.find_executable("highs") 8 ] 9]) 10``` 11 12## Section 13 14<!-- livebook:{"attrs":"eyJhc3NpZ25fdG8iOiJwdXp6bGVfaW5wdXQiLCJkYXkiOiIxMCIsInNlc3Npb25fc2VjcmV0IjoiQURWRU5UX09GX0NPREVfU0VTU0lPTiIsInllYXIiOiIyMDI1In0","chunks":null,"kind":"Elixir.KinoAOC.HelperCell","livebook_object":"smart_cell"} --> 15 16```elixir 17{:ok, puzzle_input} = 18 KinoAOC.download_puzzle("2025", "10", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION")) 19``` 20 21```elixir 22defmodule Decoder do 23 def decode("[" <> pattern), do: do_lights(String.reverse(String.trim_trailing(pattern, "]")), 0) 24 25 def decode("(" <> rest) do 26 <<seq::binary-size(byte_size(rest) - 1), ")">> = rest 27 28 seq 29 |> String.split(",") 30 |> Enum.map(&String.to_integer/1) 31 end 32 33 def decode("{" <> rest) do 34 <<seq::binary-size(byte_size(rest) - 1), "}">> = rest 35 36 seq 37 |> String.split(",") 38 |> Enum.map(&String.to_integer/1) 39 end 40 41 defp do_lights("", num), do: num 42 defp do_lights("." <> rest, num), do: do_lights(rest, 2 * num) 43 defp do_lights("#" <> rest, num), do: do_lights(rest, 2 * num + 1) 44end 45``` 46 47```elixir 48indicators = 49 puzzle_input 50 |> String.split("\n", trim: true) 51 |> Enum.map(fn raw -> 52 [lights | rest] = 53 raw 54 |> String.split() 55 |> Enum.map(&Decoder.decode/1) 56 57 {buttons, [whatever]} = Enum.split(rest, -1) 58 59 {lights, buttons, whatever} 60 end) 61``` 62 63<!-- livebook:{"branch_parent_index":0} --> 64 65## Part 1 66 67We are looking for such sequence of buttons that will provide pattern we are looking for. It can be solved by brute force with simple observation that buttons $a_n$ as well as light pattern $p$ can be represented by binary pattern. This allows us to use $\oplus$ (exclusive or, aka `XOR`) as an operation for pressing button. 68 69Thanks to that observation we can see, that order in which we press buttons doesn't matter, as $\oplus$ is reflexive, i.e.: 70 71$$ 72\begin{equation} 73a \oplus b = b \oplus a 74\end{equation} 75$$ 76 77Additionally, wrt. $\oplus$ we have identity element $0$ and inverse element is the same as an original element, i.e. 78 79$$ 80\begin{align} 81a \oplus 0 = a \\ 82a \oplus a = 0 83\end{align} 84$$ 85 86Additionally we can observe that: 87 88$$ 89\begin{equation} 90(a \oplus b) \oplus c = a \oplus (b \oplus c) 91\end{equation} 92$$ 93 94With that observation we can deduce that each button will be pressed at most once and the order in which we press buttons doesn't matter. 95 96--- 97 98So now we look for set $A = \{a_n\}$ of buttons that 99 100$$ 101A \in \mathcal{P}(\text{Buttons}) \\ 102\forall_{i, j} \; i \ne j \implies a_i \ne a_j \\ 103\bigoplus A = p 104$$ 105 106```elixir 107defmodule Comb do 108 def all_possible([]), do: [[]] 109 def all_possible([a | rest]) do 110 sub = all_possible(rest) 111 112 sub ++ Enum.map(sub, &[a | &1]) 113 end 114end 115``` 116 117```elixir 118indicators 119|> Enum.sum_by(fn {p, a, _} -> 120 a 121 |> Enum.map(&Enum.sum_by(&1, fn p -> 2 ** p end)) 122 |> Comb.all_possible() 123 |> Enum.sort_by(&length/1) 124 |> Enum.find(fn seq -> 125 r = Enum.reduce(seq, 0, &Bitwise.bxor/2) 126 127 p == r 128 end) 129 |> length() 130end) 131``` 132 133<!-- livebook:{"branch_parent_index":0} --> 134 135## Part 2 136 137```elixir 138defmodule Joltage do 139 alias Dantzig.Polynomial 140 alias Dantzig.Constraint 141 alias Dantzig.Problem 142 143 def solve({_pat, buttons, goal}) do 144 p = Problem.new(direction: :minimize) 145 146 {vars, {p, map}} = 147 buttons 148 |> Enum.with_index() 149 |> Enum.map_reduce({p, %{}}, fn {list, idx}, {p, acc} -> 150 {p, var} = Problem.new_variable(p, "v#{idx}", min: 0, type: :integer) 151 152 acc = 153 Enum.reduce(list, acc, fn key, map -> 154 Map.update(map, key, [var], &[var | &1]) 155 end) 156 157 {var, {p, acc}} 158 end) 159 160 p = 161 map 162 |> Enum.sort() 163 |> Enum.map(&elem(&1, 1)) 164 |> Enum.zip(goal) 165 |> Enum.reduce(p, fn {vars, target}, p -> 166 poly = Polynomial.sum(vars) 167 const = Constraint.new(poly, :==, target) 168 169 Problem.add_constraint(p, const) 170 end) 171 172 p = Problem.increment_objective(p, Polynomial.sum(vars)) 173 174 {:ok, s} = Dantzig.HiGHS.solve(p) 175 176 Enum.sum_by(vars, &Dantzig.Solution.evaluate(s, &1)) 177 end 178end 179``` 180 181```elixir 182indicators 183|> Task.async_stream(&Joltage.solve/1, ordered: false) 184|> Enum.sum_by(&elem(&1, 1)) 185|> trunc() 186``` 187 188<!-- livebook:{"offset":4336,"stamp":{"token":"XCP.sfoBGIHkFvWbEHVrY-dL-QiEcwRz_ZCIjn3lQ0RFHActIOwEapmOPBt0ygbQAmrnEjYPlFm5KrcWx4LfIPbSznxxcea0fYORG9GbBBpRCm-tYbGXYTCGgqgTvOsifyWDNg","version":2}} -->