$\huge{元氣ですかーーーーッ!!!}$
$\huge{元氣があればなんでもできる!}$
$\huge{闘魂とは己に打ち克つこと。}$
$\huge{そして闘いを通じて己の魂を磨いていく}$
$\huge{ことだと思います}$
はじめに
@torifukukaiou さんの パク リスペクト記事です
Elixir Livebook で Advent of Code 2023 の問題を解いてみます
実装したノートブックはこちら
問題はこちら
セットアップ
Kino AOC をインストールします
Mix.install([
{:kino_aoc, "~> 0.1.5"}
])
Kino AOC の使い方はこちらを参照
入力の取得
Day 3 の入力を取得します
私の答え
私の答えです。
折りたたんでおきます。
▶を押して開いてください。
details
回答用のモジュールです
入力を扱いやすい形にするための parse
と、回答を作るための resolve
関数を持っています
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
入力の各行について、正規表現を使って数字と記号を取得します
数字の取得
Regex.scan(~r/[0-9]+/, line)
記号(数字でも .
でもない文字)の取得
Regex.scan(~r/[^0-9\.]+/, line)
Regex.scan
のオプションに return: :index
を指定すると、該当する文字列の位置を返してくれます
これを利用し、以下のような形式で数値と記号を取得します
{<行番号>, <列番号>, <長さ>} => <数値 or 記号>
例を示します
入力
467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..
数字の情報
%{
{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
}
記号の情報
%{
{1, 3, 1} => "*",
{3, 6, 1} => "#",
{4, 3, 1} => "*",
{5, 5, 1} => "+",
{8, 3, 1} => "$",
{8, 5, 1} => "*"
}
数字の上下左右斜めに記号が隣接するか、というのを以下の条件で判定しています
(記号は全て連続していなかったので、記号の長さは考慮していません)
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)
まとめ
全く速度や計算量を考慮していませんが、比較的素直な解法ではないでしょうか
Part 2 はこちら