Advent of code 2024 Day 4 Part 1 を Livebook で楽しむ

## はじめに

Advent of code 2024 Day 4 の Part 1 を解きます

:::note info
Part 1 と Part 2 で大きく解法が異なるため分割しました
:::

問題文はこちら

https://adventofcode.com/2024/day/4

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

https://github.com/RyoWakabayashi/elixir-learning/blob/main/livebooks/aoc/2024/2024_day4_part1.livemd

## セットアップ

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

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

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

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

## 入力の取得

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

![スクリーンショット 2024-12-04 19.17.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/e2318148-82ba-c5a4-9d62-2efae32dad76.png)

## 私の答え

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

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

まずは小さい入力例で解いてみます

```elixir
input =
  """
  MMMSXXMASM
  MSAMXMSMSA
  AMXSXMAAMM
  MSAMASMSMX
  XMASAMXAMM
  XXAMMXXAMA
  SMSMSASXSS
  SAXAMASAAA
  MAMMMXMMMM
  MXMXAXMASX
  """
```

右から左、左から右に読むパターンは単純に正規表現で取得できます

```elixir
Regex.scan(~r/XMAS/, input)
```

実行結果

```elixir
[["XMAS"], ["XMAS"], ["XMAS"]]
```

```elixir
Regex.scan(~r/SAMX/, input)
```

実行結果

```elixir
[["SAMX"], ["SAMX"]]
```

上下方向は縦横を入れ替えれば取得可能です

```elixir
turned_input =
  input
  |> String.trim()
  |> String.split("\n")
  |> Enum.map(&String.codepoints(&1))
  |> Enum.zip()
  |> Enum.map(fn col -> col |> Tuple.to_list() |> Enum.join() end)
  |> Enum.join("\n")

IO.puts(turned_input)
```

標準出力

```text
MMAMXXSSMM
MSMSMXMAAX
MAXAAASXMM
SMSMSMMAMX
XXXAAMSMMA
XMMSMXAAXX
MSAMXXSSMM
AMASAAXAMA
SSMMMMSAMS
MAMXMASAMX
```

```elixir
Regex.scan(~r/XMAS/, turned_input)
```

```elixir
Regex.scan(~r/SAMX/, turned_input)
```

斜め方向を取得するために、まずは横にずらします

```elixir
turned_input =
  input
  |> String.trim()
  |> String.split("\n")
  |> Enum.with_index()
  |> Enum.map(fn {row, index} ->
    String.duplicate(".", index) <> row <> String.duplicate(".", String.length(row) - index - 1)
  end)
  |> Enum.join("\n")

IO.puts(turned_input)
```

標準出力

```text
MMMSXXMASM.........
.MSAMXMSMSA........
..AMXSXMAAMM.......
...MSAMASMSMX......
....XMASAMXAMM.....
.....XXAMMXXAMA....
......SMSMSASXSS...
.......SAXAMASAAA..
........MAMMMXMMMM.
.........MXMXAXMASX
```

これを回転させることで、斜め方向に読むことができます

```elixir
turned_input =
  input
  |> String.trim()
  |> String.split("\n")
  |> Enum.with_index()
  |> Enum.map(fn {row, index} ->
    String.duplicate(".", index) <> row <> String.duplicate(".", String.length(row) - index - 1)
  end)
  |> Enum.map(&String.codepoints(&1))
  |> Enum.zip()
  |> Enum.map(fn col -> col |> Tuple.to_list() |> Enum.join() end)
  |> Enum.join("\n")

IO.puts(turned_input)
```

標準出力

```text
M.........
MM........
MSA.......
SAMM......
XMXSX.....
XXSAMX....
MMXMAXS...
ASMASAMS..
SMASAMSAM.
MSAMMMMXAM
.AMSXXSAMX
..MMAXAMMM
...XMASAMX
....MMXSXA
.....ASAMX
......SAMM
.......AMA
........MS
.........X
```

```elixir
Regex.scan(~r/XMAS/, turned_input)
```

```elixir
Regex.scan(~r/SAMX/, turned_input)
```

ずらす向きを逆にすれば逆の斜め方向も読むことができます

ここまでのことを踏まえ、ある方向に対して `XMAS` の存在する数を取得する関数を用意します

```elixir
search_xmas = fn input ->
  IO.puts("")
  IO.puts(input)
  Enum.sum([
    Regex.scan(~r/XMAS/, input) |> length(),
    Regex.scan(~r/SAMX/, input) |> length()
  ])
end
```

```elixir
search_xmas.(input)
```

実行結果

```elixir
5
```

全ての方向に対して `XMAS` を探す関数を用意します

```elixir
search_all_xmas = fn input ->
  Enum.sum([
    input
    |> String.trim()
    |> search_xmas.(),
    input
    |> String.trim()
    |> String.split("\n")
    |> Enum.map(&String.codepoints(&1))
    |> Enum.zip()
    |> Enum.map(fn col -> col |> Tuple.to_list() |> Enum.join() end)
    |> Enum.join("\n")
    |> search_xmas.(),
    input
    |> String.trim()
    |> String.split("\n")
    |> Enum.with_index()
    |> Enum.map(fn {row, index} ->
      String.duplicate(".", index) <> row <> String.duplicate(".", String.length(row) - index - 1)
    end)
    |> Enum.map(&String.codepoints(&1))
    |> Enum.zip()
    |> Enum.map(fn col -> col |> Tuple.to_list() |> Enum.join() end)
    |> Enum.join("\n")
    |> search_xmas.(),
    input
    |> String.trim()
    |> String.split("\n")
    |> Enum.with_index()
    |> Enum.map(fn {row, index} ->
      String.duplicate(".", String.length(row) - index - 1) <> row <> String.duplicate(".", index)
    end)
    |> Enum.map(&String.codepoints(&1))
    |> Enum.zip()
    |> Enum.map(fn col -> col |> Tuple.to_list() |> Enum.join() end)
    |> Enum.join("\n")
    |> search_xmas.()
  ])
end
```

```elixir
search_all_xmas.(input)
```

標準出力

```text
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX

MMAMXXSSMM
MSMSMXMAAX
MAXAAASXMM
SMSMSMMAMX
XXXAAMSMMA
XMMSMXAAXX
MSAMXXSSMM
AMASAAXAMA
SSMMMMSAMS
MAMXMASAMX

M.........
MM........
MSA.......
SAMM......
XMXSX.....
MMXMAXS...
ASMASAMS..
SMASAMSAM.
MSAMMMMXAM
.AMSXXSAMX
..MMAXAMMM
...XMASAMX
....MMXSXA
.....ASAMX
......SAMM
.......AMA
........MS
.........X

.........M
........MX
.......SAM
......SAMX
.....XMXMA
....XXSAMX
...MMAMMXM
..ASAMSAMA
.MMASMASMS
MSXMAXSAMX
MASAMXXAM.
MMXSXASA..
SXMMAMS...
XMASMA....
XSAMM.....
MMMX......
ASM.......
SA........
M.........
```

実行結果

```elixir
18
```

実際の入力に対して実行すれば答えが出ます

```elixir
search_all_xmas.(puzzle_input)
```

</details>

## まとめ

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

![aoc2024_day4_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/525eee6c-f7a2-7a5a-f534-82ba21886062.png)

文字列を斜めに読むのが面白い問題でした

Part 2 はこちら

https://qiita.com/RyoWakabayashi/private/1cf1f65e6cd394ada32d