Advent of code 2015 Day 14 Part 1 & Part 2 を Livebook で楽しむ

## はじめに

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

本記事では Day 14 の Part 1 と Part 2 を解きます

問題文はこちら

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

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

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

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

## セットアップ

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

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

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

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

## 入力の取得

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

![スクリーンショット 2024-11-26 1.17.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/dd2ada76-49a6-0220-d777-cc66c8ed5682.png)

## 私の答え

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

### Part 1

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

入力を正規表現でパースします

```elixir
Regex.named_captures(
  ~r/(?<reindeer>[a-zA-Z]+) can fly (?<speed>\d+) km\/s for (?<keep>\d+) seconds, but then must rest for (?<rest>\d+)/,
  "Comet can fly 14 km/s for 10 seconds, but then must rest for 127 seconds."
)
```

実行結果

```elixir
%{"keep" => "10", "reindeer" => "Comet", "rest" => "127", "speed" => "14"}
```

各トナカイのスピード、持続時間、休憩時間、走行距離のマップ配列にします

```elixir
reindeer_list =
  puzzle_input
  |> String.split("\n")
  |> Enum.map(fn row ->
    Regex.named_captures(
      ~r/(?<reindeer>[a-zA-Z]+) can fly (?<speed>\d+) km\/s for (?<keep>\d+) seconds, but then must rest for (?<rest>\d+)/,
      row
    )
    |> then(fn %{"speed" => speed, "keep" => keep, "rest" => rest} ->
      %{
        speed: String.to_integer(speed),
        keep: String.to_integer(keep),
        rest: String.to_integer(rest),
        distance: 0
      }
    end)
  end)
```

実行結果

```elixir
[
  %{speed: 19, keep: 7, rest: 124, distance: 0},
  %{speed: 3, keep: 15, rest: 28, distance: 0},
  %{speed: 19, keep: 9, rest: 164, distance: 0},
  %{speed: 19, keep: 9, rest: 158, distance: 0},
  %{speed: 13, keep: 7, rest: 82, distance: 0},
  %{speed: 25, keep: 6, rest: 145, distance: 0},
  %{speed: 14, keep: 3, rest: 38, distance: 0},
  %{speed: 3, keep: 16, rest: 37, distance: 0},
  %{speed: 25, keep: 6, rest: 143, distance: 0}
]
```

各秒時点で各トナカイの走行距離を計算し、 2503 秒時点での最大値を求めます

```elixir
1..2503
|> Enum.reduce(reindeer_list, fn sec, acc_reindeer_list ->
  acc_reindeer_list
  |> Enum.map(fn reindeer ->
    remain = rem(sec, reindeer.keep + reindeer.rest)
    if remain > 0 and remain <= reindeer.keep do
      Map.put(reindeer, :distance, reindeer.distance + reindeer.speed)
    else
      reindeer
    end
  end)
end)
|> Enum.map(&(&1.distance))
|> Enum.max()
```

</details>

### Part 2

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

各トナカイに `point: 0` を初期値として与えます

```elixir
reindeer_list =
  puzzle_input
  |> String.split("\n")
  |> Enum.map(fn row ->
    Regex.named_captures(
      ~r/(?<reindeer>[a-zA-Z]+) can fly (?<speed>\d+) km\/s for (?<keep>\d+) seconds, but then must rest for (?<rest>\d+)/,
      row
    )
    |> then(fn %{"speed" => speed, "keep" => keep, "rest" => rest} ->
      %{
        speed: String.to_integer(speed),
        keep: String.to_integer(keep),
        rest: String.to_integer(rest),
        distance: 0,
        point: 0
      }
    end)
  end)
```

各秒数時点で最も走行距離が長いトナカイにポイントを加算していき、最大ポイントを求めます

```elixir
1..2503
|> Enum.reduce(reindeer_list, fn sec, acc_reindeer_list ->
  acc_reindeer_list =
    acc_reindeer_list
    |> Enum.map(fn reindeer ->
      remain = rem(sec, reindeer.keep + reindeer.rest)
      if remain > 0 and remain <= reindeer.keep do
        Map.put(reindeer, :distance, reindeer.distance + reindeer.speed)
      else
        reindeer
      end
    end)

  max_distance =
    acc_reindeer_list
    |> Enum.map(&(&1.distance))
    |> Enum.max()

  acc_reindeer_list
  |> Enum.map(fn reindeer ->
    if reindeer.distance == max_distance do
       Map.put(reindeer, :point, reindeer.point + 1)
    else
      reindeer
    end
  end)
end)
|> Enum.map(&(&1.point))
|> Enum.max()
```

</details>

## まとめ

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

![reindeer.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/a27d61ea-f5f0-e4ef-30c0-b58a13bebac9.jpeg)

繰り返しだけで解ける問題だったので簡単でした

今のところ、 Day 7 で一番苦労しています