$\huge{元氣ですかーーーーッ!!!}$
$\huge{元氣があればなんでもできる!}$
$\huge{闘魂とは己に打ち克つこと。}$
$\huge{そして闘いを通じて己の魂を磨いていく}$
$\huge{ことだと思います}$
はじめに
@torifukukaiou さんの パク リスペクト記事です
Elixir Livebook で Advent of Code 2023 の問題を解いてみます
実装したノートブックはこちら
問題はこちら
セットアップ
Kino AOC をインストールします
Mix.install([
{:kino_aoc, "~> 0.1.5"}
])
Kino AOC の使い方はこちらを参照
入力の取得
Day 5 の入力を取得します
私の答え
私の答えです。
折りたたんでおきます。
▶を押して開いてください。
details
回答用のモジュールです
入力を扱いやすい形にするための parse
と、回答を作るための resolve
関数を持っています
defmodule Resolver do
def parse(input) do
input
|> String.split("\n")
|> split_by_blank_line([])
|> Enum.into(%{}, fn group ->
parse_group(group)
end)
end
defp split_by_blank_line(lines, acc) do
{head, tail} =
Enum.split_while(lines, fn line -> line != "" end)
if Enum.count(tail) == 0 do
acc ++ [head]
else
tail
|> tl()
|> split_by_blank_line(acc ++ [head])
end
end
defp parse_group(group) when length(group) == 1 do
seeds =
group
|> hd()
|> String.split(" ")
|> tl()
|> Enum.map(&String.to_integer(&1))
{:seeds, seeds}
end
defp parse_group(group) do
group_name =
group
|> hd()
|> String.slice(0..-6)
|> String.replace("-", "_")
|> String.to_atom()
mappings =
group
|> tl()
|> Enum.map(fn line ->
[dst, src, len] =
line
|> String.split(" ")
|> Enum.map(&String.to_integer(&1))
%{
src: src,
dst: dst,
len: len
}
end)
{group_name, mappings}
end
def resolve(maps) do
maps.seeds
|> Enum.map(fn seed ->
seed
|> search(maps.seed_to_soil)
|> search(maps.soil_to_fertilizer)
|> search(maps.fertilizer_to_water)
|> search(maps.water_to_light)
|> search(maps.light_to_temperature)
|> search(maps.temperature_to_humidity)
|> search(maps.humidity_to_location)
end)
|> Enum.min()
end
defp search(key, mappings) do
target_mapping =
Enum.find(mappings, fn mapping ->
key >= mapping.src and key <= (mapping.src + mapping.len - 1)
end)
if is_nil(target_mapping) do
key
else
target_mapping.dst + key - target_mapping.src
end
end
end
parse
の時点でなかなか複雑です
入力の各行を空白行区切りでグループ化しています
Enum.split_while
で空白行前後に分けるのを再帰する形にしましたが、余計に複雑になった気がします
seeds
は種 ID の配列、それ以外は %{src: <変換元開始インデックス>, dst: <変換先開始インデックス>, len: <変換範囲の長さ>}
の形式のマップの配列にしています
パースの例は以下のようになります
入力
seeds: 79 14 55 13
seed-to-soil map:
50 98 2
52 50 48
soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15
fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4
water-to-light map:
88 18 7
18 25 70
light-to-temperature map:
45 77 23
81 45 19
68 64 13
temperature-to-humidity map:
0 69 1
1 0 69
humidity-to-location map:
60 56 37
56 93 4
パース結果
%{
seeds: [79, 14, 55, 13],
seed_to_soil: [%{len: 2, dst: 50, src: 98}, %{len: 48, dst: 52, src: 50}],
soil_to_fertilizer: [
%{len: 37, dst: 0, src: 15},
%{len: 2, dst: 37, src: 52},
%{len: 15, dst: 39, src: 0}
],
fertilizer_to_water: [
%{len: 8, dst: 49, src: 53},
%{len: 42, dst: 0, src: 11},
%{len: 7, dst: 42, src: 0},
%{len: 4, dst: 57, src: 7}
],
water_to_light: [%{len: 7, dst: 88, src: 18}, %{len: 70, dst: 18, src: 25}],
light_to_temperature: [
%{len: 23, dst: 45, src: 77},
%{len: 19, dst: 81, src: 45},
%{len: 13, dst: 68, src: 64}
],
temperature_to_humidity: [%{len: 1, dst: 0, src: 69}, %{len: 69, dst: 1, src: 0}],
humidity_to_location: [%{len: 37, dst: 60, src: 56}, %{len: 4, dst: 56, src: 93}]
}
resolve
では用意した変換マップで各 ID を変換していきます
defp search(key, mappings) do
target_mapping =
Enum.find(mappings, fn mapping ->
key >= mapping.src and key <= (mapping.src + mapping.len - 1)
end)
if is_nil(target_mapping) do
key
else
target_mapping.dst + key - target_mapping.src
end
end
変換元インデックスが変換範囲内にあれば変換し、なければそのままの値を返します
ここまで用意した後、一気にパイプを繋げられるのが気持ちいいです
seed
|> search(maps.seed_to_soil)
|> search(maps.soil_to_fertilizer)
|> search(maps.fertilizer_to_water)
|> search(maps.water_to_light)
|> search(maps.light_to_temperature)
|> search(maps.temperature_to_humidity)
|> search(maps.humidity_to_location)
まとめ
問題を解く以上に入力をパースするのが大変でした
Part 2 はこちら