はじめに
Advent of code 2024 の準備として、過去回の Advent of code 2015 を Livebook で楽しみます
本記事では Day 7 の Part 2 を解きます
問題文はこちら
実装したノートブックはこちら
Part 1 はこちら
セットアップ
Kino AOC をインストールします
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Kino AOC の使い方はこちらを参照
入力の取得
"Advent of Code Helper" スマートセルを追加し、 Day 7 の入力を取得します
私の答え
私の答えです。
折りたたんでおきます。
▶を押して開いてください。
回答
Part 1 でかなり苦戦しましたが、 Part 2 は単純でした
まずビット演算のために Bitwise
を import しておき、 Part 1 と同じモジュールを定義します
import Bitwise
defmodule Solver do
import Bitwise
defp bit_not(input) do
<<output::integer-size(2)-unit(8)>> = <<(~~~input)::size(16)>>
output
end
defp parse_variable(nil, _), do: nil
defp parse_variable("", _), do: nil
defp parse_variable(variable, _) when is_integer(variable), do: variable
defp parse_variable(variable, dict) do
case Integer.parse(variable) do
{number, ""} -> number
_ -> Map.get(dict, variable, variable)
end
end
defp calc("", input_a, _) when is_integer(input_a), do: input_a
defp calc("NOT", _, input_b) when is_integer(input_b), do: bit_not(input_b)
defp calc("AND", input_a, input_b) when (is_integer(input_a) and is_integer(input_b)) do
input_a &&& input_b
end
defp calc("OR", input_a, input_b) when (is_integer(input_a) and is_integer(input_b)) do
input_a ||| input_b
end
defp calc("LSHIFT", input_a, input_b) when (is_integer(input_a) and is_integer(input_b)) do
input_a <<< input_b
end
defp calc("RSHIFT", input_a, input_b) when (is_integer(input_a) and is_integer(input_b)) do
input_a >>> input_b
end
defp calc(_, _, _), do: nil
defp solve(instruction, dict) do
%{
"input_a" => input_a,
"input_b" => input_b,
"op" => op,
"output" => output
} = instruction
input_a = parse_variable(input_a, dict)
input_b = parse_variable(input_b, dict)
result = calc(op, input_a, input_b)
solved = !is_nil(result)
instruction = Map.put(instruction, :solved, solved)
dict = if solved, do: Map.put(dict, output, result), else: dict
{instruction, dict}
end
def parse_instraction(row) do
Regex.named_captures(
~r/(?<input_a>[a-z0-9]*) *(?<op>[A-Z]*) *(?<input_b>[a-z0-9]*) -> (?<output>.+)/,
row
)
end
def cyclic_solve(%{solved: true}, instructions, dict), do: {instructions, dict}
def cyclic_solve(new_instruction, instructions, dict) do
{new_instruction, dict} = solve(new_instruction, dict)
if new_instruction.solved do
{instructions, dict} =
instructions
|> Enum.reduce({[], dict}, fn sub_instruction, {acc_instructions, acc_dict} ->
cyclic_solve(sub_instruction, acc_instructions, acc_dict)
end)
{[new_instruction | instructions], dict}
else
{[new_instruction | instructions], dict}
end
end
end
2 回、全ワイヤーの値を計算しないといけないので、関数化しておきます
get_a = fn rows ->
rows
|> Enum.reduce({[], %{}}, fn row, {instructions, dict} ->
new_instruction = Solver.parse_instraction(row)
Solver.cyclic_solve(new_instruction, instructions, dict)
end)
|> elem(1)
|> Map.get("a")
end
1回目の計算で取得した a ワイヤーの値で b ワイヤーの入力を上書き、再度 a ワイヤーの値を取得します
rows = String.split(puzzle_input, "\n")
override_value = get_a.(rows)
rows
|> Enum.map(fn row ->
if String.ends_with?(row, "-> b") do
"#{override_value} -> b"
else
row
end
end)
|> get_a.()
まとめ
問題文から ChatGPT に画像を生成してもらいました
Part 1 でモジュール化しておいたので、 Part 2 は何も躓くことなく解けました