Advent of code 2015 Day 7 Part 2 を Livebook で楽しむ

## はじめに

Advent of code 2024 の準備として、過去回の Advent of code 2015 を Livebook で楽しみます

本記事では Day 7 の Part 2 を解きます

問題文はこちら

https://adventofcode.com/2015/day/7

実装したノートブックはこちら

https://github.com/RyoWakabayashi/elixir-learning/blob/main/livebooks/aoc/2015/2015_day7_part2.livemd

Part 1 はこちら

https://qiita.com/RyoWakabayashi/private/2d31d32cb2f8163254a4

## セットアップ

Kino AOC をインストールします

```elixir
Mix.install([
  {:kino_aoc, "~> 0.1"}
])
```

Kino AOC の使い方はこちらを参照

https://qiita.com/RyoWakabayashi/items/3d3abcf9134cee4dff05

## 入力の取得

"Advent of Code Helper" スマートセルを追加し、 Day 7 の入力を取得します

![スクリーンショット 2024-11-21 16.55.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/63e79fdf-649e-5dd5-6b0a-d8656f3a6d50.png)

## 私の答え

私の答えです。
折りたたんでおきます。
▶を押して開いてください。

<details><summary>回答</summary>

Part 1 でかなり苦戦しましたが、 Part　2 は単純でした

まずビット演算のために `Bitwise` を import しておき、 Part 1 と同じモジュールを定義します

```elixir
import Bitwise
```

```elixir
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 回、全ワイヤーの値を計算しないといけないので、関数化しておきます

```elixir
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 ワイヤーの値を取得します

```elixir
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.()
```

</details>

## まとめ

問題文から ChatGPT に画像を生成してもらいました

![wire.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/d565e9f0-c59d-68cd-da45-7759352e2ae8.jpeg)

Part 1 でモジュール化しておいたので、 Part 2 は何も躓くことなく解けました