闘魂Elixir ── Advent of code 2023 Day 3 Part 1 を Livebook で楽しむ

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>

<b><font color="red">$\huge{闘魂とは己に打ち克つこと。}$</font></b>
<b><font color="red">$\huge{そして闘いを通じて己の魂を磨いていく}$</font></b>
<b><font color="red">$\huge{ことだと思います}$</font></b>

https://qiita.com/advent-calendar/2023/elixir

## はじめに

@torifukukaiou さんの ~~パク~~ リスペクト記事です

https://qiita.com/torifukukaiou/items/e4c4eb6e3f75b79bcd57

Elixir Livebook で Advent of Code 2023 の問題を解いてみます

https://adventofcode.com/2023

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

https://github.com/RyoWakabayashi/elixir-learning/blob/main/livebooks/aoc/2023/2023_day3_part1.livemd

問題はこちら

https://adventofcode.com/2023/day/3

## セットアップ

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

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

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

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

## 入力の取得

Day 3 の入力を取得します

![スクリーンショット 2023-12-26 11.05.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/ec9a837d-93b2-3d5f-1d59-f31f70d0037e.png)

## 私の答え

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

<details><summary>details</summary>

回答用のモジュールです

入力を扱いやすい形にするための `parse` と、回答を作るための `resolve` 関数を持っています

```elixir
defmodule Resolver do
  def parse(input) do
    parsed_input =
      input
      |> String.split("\n")
      |> Enum.with_index()
      |> Enum.map(fn {line, row_index} ->
        number = Regex.scan(~r/[0-9]+/, line)
        number_index = Regex.scan(~r/[0-9]+/, line, return: :index)
        symbol = Regex.scan(~r/[^0-9\.]+/, line)
        symbol_index = Regex.scan(~r/[^0-9\.]+/, line, return: :index)

        numbers =
          Enum.zip(number_index, number)
          |> Enum.into(%{}, fn {[{col_index, length}], [number]} ->
            {{row_index, col_index, length}, String.to_integer(number)}
          end)

        symbols =
          Enum.zip(symbol_index, symbol)
          |> Enum.into(%{}, fn {[{col_index, length}], [symbol]} ->
            {{row_index, col_index, length}, symbol}
          end)

        %{
          numbers: numbers,
          symbols: symbols
        }
      end)

    numbers =
      parsed_input
      |> Enum.reduce(%{}, fn line, acc ->
        Map.merge(acc, line.numbers)
      end)

    symbols =
      parsed_input
      |> Enum.reduce(%{}, fn line, acc ->
        Map.merge(acc, line.symbols)
      end)

    {numbers, symbols}
  end

  def resolve(numbers, symbols) do
    numbers
    |> Enum.filter(fn {{num_row_index, num_col_index, num_length}, _number} ->
      Enum.find(symbols, fn {{sym_row_index, sym_col_index, _sym_length}, _symbol} ->
        sym_row_index >= num_row_index - 1 and \
        sym_row_index <= num_row_index + 1 and \
        sym_col_index >= num_col_index - 1 and \
        sym_col_index <= num_col_index + num_length
      end)
    end)
    |> Enum.map(fn {_index, number} -> number end)
    |> Enum.sum()
  end
end
```

入力の各行について、正規表現を使って数字と記号を取得します

数字の取得
```elixir
Regex.scan(~r/[0-9]+/, line)
```

記号（数字でも `.` でもない文字）の取得
```elixir
Regex.scan(~r/[^0-9\.]+/, line)
```

`Regex.scan` のオプションに `return: :index` を指定すると、該当する文字列の位置を返してくれます

これを利用し、以下のような形式で数値と記号を取得します

`{<行番号>, <列番号>, <長さ>} => <数値 or 記号>`

例を示します

入力
```
467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..
```

数字の情報
```elixir
%{
  {0, 0, 3} => 467,
  {0, 5, 3} => 114,
  {2, 2, 2} => 35,
  {2, 6, 3} => 633,
  {4, 0, 3} => 617,
  {5, 7, 2} => 58,
  {6, 2, 3} => 592,
  {7, 6, 3} => 755,
  {9, 1, 3} => 664,
  {9, 5, 3} => 598
}
```

記号の情報
```elixir
%{
  {1, 3, 1} => "*",
  {3, 6, 1} => "#",
  {4, 3, 1} => "*",
  {5, 5, 1} => "+",
  {8, 3, 1} => "$",
  {8, 5, 1} => "*"
}
```

数字の上下左右斜めに記号が隣接するか、というのを以下の条件で判定しています
（記号は全て連続していなかったので、記号の長さは考慮していません）

```elixir
numbers
|> Enum.filter(fn {{num_row_index, num_col_index, num_length}, _number} ->
  Enum.find(symbols, fn {{sym_row_index, sym_col_index, _sym_length}, _symbol} ->
    sym_row_index >= num_row_index - 1 and \
    sym_row_index <= num_row_index + 1 and \
    sym_col_index >= num_col_index - 1 and \
    sym_col_index <= num_col_index + num_length
  end)
end)
```

</details>

## まとめ

全く速度や計算量を考慮していませんが、比較的素直な解法ではないでしょうか

Part 2 はこちら

https://qiita.com/RyoWakabayashi/items/c4717de1caf89391d358