$\huge{元氣ですかーーーーッ!!!}$
$\huge{元氣があればなんでもできる!}$
$\huge{闘魂とは己に打ち克つこと、}$
$\huge{そして闘いを通じて己の魂を磨いていく}$
$\huge{ことだとおもいます}$
はじめに
闘魂とElixirが出会いました。
闘魂 meets Elixir.です。
Elixir meets 闘魂.でもよいです。
2022-12-26より、アドベントカレンダー2023は開幕しました。
私のアドベントカレンダー一覧は、コチラです。
だれよりも2023/12/25を楽しみにしています。
この記事は、Advent of Code 2022のDay 5: Supply Stacksを解いてみます。
Advent of Code 2022は、競技プログラミングのような問題が25題出題されています。
毎年問題が出題されておりまして、日を追うごとに難しくなる傾向があるようにおもいます。
iex> "Elixir" |> String.graphemes() |> Enum.frequencies()
%{"E" => 1, "i" => 2, "l" => 1, "r" => 1, "x" => 1}
Day 5: Supply Stacks
問題文はこちらをご参照ください。
問題を説明します。
[D]
[N] [C]
[Z] [M] [P]
1 2 3
move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
空行より上の部分は木箱の初期状態です。
move N from a to b
は指示書です。N個の木箱をa列からb列へ移動させなさいという意味です。
木箱は1個ずつ動かせます。
ちなみに余談です。
指示書 と言えば、私が真っ先に思い出すのは
この話です。 指示書 そのものはすごいものだとおもいます。私が 指示書 という単語を使うときにイメージしているものはコレです。
本題に戻ります。
指示書を一つずつ実行してみます。
以下、指示書の通りの指示を実行後の状態を書いておきます。
move 1 from 2 to 1
[D]
[N] [C]
[Z] [M] [P]
1 2 3
move 3 from 1 to 3
[Z]
[N]
[C] [D]
[M] [P]
1 2 3
move 2 from 2 to 1
[Z]
[N]
[M] [D]
[C] [P]
1 2 3
move 1 from 1 to 2
[Z]
[N]
[D]
[C] [M] [P]
1 2 3
指示書を全部実行すると上記のようになります。左から一番上の木箱のアルファベットを並べると答えです。
つまりCMZ
です。
ご存知の方はご存知だとおもいますが、ハノイの塔みたいな問題です。
本番のデータは、行数が8行、列数は9列あります。
解答例
私はElixirで解いてみます。
Livebookをお使いの方は、上記のボタンを迷わず押すと、私の解答例をお試しいただけます!
解答例は閉じておきます。
解答例
私
なんとか解けたけど、めっちゃ長いです。
Nxをうまく使うともっと簡単に解けるのかもしれません。
input = """
[D]
[N] [C]
[Z] [M] [P]
1 2 3
move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
"""
defmodule Stack do
def push("", stack) do
stack
end
def push(e, stack) do
[e | stack]
end
def pop([]), do: {"", []}
def pop([head|tail]), do: {head, tail}
end
[crates, [""], procedures, [""]] = input
|> String.split("\n")
|> Enum.chunk_by(& &1 == "")
crates = crates
|> Enum.take(Enum.count(crates) - 1)
|> Enum.map(fn line ->
String.split(line, [" "])
|> Enum.reduce({[], 0}, fn
"", {acc_list, acc_cnt} ->
acc_cnt = acc_cnt + 1
if acc_cnt >= 4 do
{acc_list ++ [""], 0}
else
{acc_list, acc_cnt}
end
<<"[", s, "]">>, {acc_list, _acc_cnt} ->
{acc_list ++ [s], 0}
end)
|> elem(0)
end)
|> IO.inspect()
[top | _] = crates
rows = Enum.count(top) |> IO.inspect()
crates_map = crates
|> Enum.map(&Enum.with_index/1)
|> Enum.with_index()
|> Enum.reduce(%{}, fn {list, i}, acc ->
list
|> Enum.reduce(acc, fn {crate, j}, acc2 ->
Map.merge(acc2, %{{i, j} => crate})
end)
end)
|> IO.inspect()
stacks_of_crates = for j <- 0..(rows - 1), i <- (Enum.count(crates) - 1)..0, reduce: %{} do
acc -> Map.update(acc, j, Stack.push(crates_map[{i, j}], []), & Stack.push(crates_map[{i, j}], &1))
end
ようやくInputの整理が終わりました。
あとは指示書の通りに作業をこなします。
procedures
|> Enum.map(fn procedure ->
["move", cnt, "from", from, "to", to] = String.split(procedure, " ")
{String.to_integer(cnt), String.to_integer(from) - 1, String.to_integer(to) - 1}
end)
|> Enum.reduce(stacks_of_crates, fn {cnt, from, to}, acc ->
1..cnt
|> Enum.reduce(acc, fn _, acc2 ->
IO.inspect(acc2)
{e, from_stack} = Stack.pop(acc2[from])
to_stack = Stack.push(e, acc2[to])
acc2 |> Map.merge(%{from => from_stack}) |> Map.merge(%{to => to_stack}) |> IO.inspect()
end)
end)
|> Enum.sort_by(fn {column, _stack} -> column end)
|> Enum.map(fn {_, [head | _]} -> head end)
CMZ
が得られます。
あなたの答えをお待ちしています。
編集リクエストかコメントでくださいませ。
読者投稿コーナー
読者の方からいただいたお便りをご紹介します。
読者投稿コーナー
@mnishiguchi さん
毎回ありがとうございます。
parse_stacks = fn stacks_input ->
stacks_input
|> String.split("\n", trim: true)
|> Enum.map(fn row ->
row
|> String.split("", trim: true)
|> Enum.chunk_every(4)
|> Enum.map(fn
["[", crate | _] -> crate
[" ", " " | _] -> nil
row -> Enum.join(row) |> String.trim() |> String.to_integer()
end)
end)
|> Enum.zip_reduce(%{}, fn zipped, acc ->
{stack, [index]} = Enum.split(zipped, -1)
Map.put(acc, index, Enum.reject(stack, &is_nil/1))
end)
end
parse_commands = fn commands_input ->
commands_input
|> String.split("\n", trim: true)
|> Enum.map(fn command ->
captures =
Regex.named_captures(~r/move (?<move>\d+) from (?<from>\d+) to (?<to>\d+)/, command)
{
String.to_integer(captures["move"]),
String.to_integer(captures["from"]),
String.to_integer(captures["to"])
}
end)
end
parse_input = fn input ->
[stacks_input, commands_input] = String.split(input, "\n\n", trim: true)
{parse_stacks.(stacks_input), parse_commands.(commands_input)}
end
update_stacks = fn stacks, {n, from, to} ->
{taken, rest} = Enum.split(stacks[from], n)
stacks
|> Map.replace(from, rest)
|> Map.replace(to, Enum.reverse(taken) ++ stacks[to])
end
get_heads = fn stacks ->
stacks
|> Enum.sort_by(fn {k, _v} -> k end)
|> Enum.map(fn {_k, v} -> List.first(v) end)
end
run = fn input ->
{stacks, commands} = parse_input.(input)
Enum.reduce(commands, stacks, fn command, state ->
update_stacks.(state, command)
end)
|> get_heads.()
|> Enum.join()
end
run.("""
[D]
[N] [C]
[Z] [M] [P]
1 2 3
move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
""")
さいごに
この記事では、Advent of Code 2022の「Advent of Code 2022のDay 5: Supply Stacks」をElixirで解いてみました。
闘魂とは、 「己に打ち克つこと、そして闘いを通じて己の魂を磨いていくことである」 との猪木さんの言葉をそのまま胸に刻み込んでいます。
知っているだけで終わらせることなく、実行する、断行する、一歩を踏み出すことを自らの行動で示していきたいとおもいます。
アントニオ猪木さんのメッセージから元氣をもらったものとして、それを次代に語り継ぎ、自分自身が「闘魂」を体現するものでありたいとおもいます。
$\huge{1、2、3 ぁっダー!}$
$\huge{元氣ですかーーーーッ!!!}$
$\huge{元氣があればなんでもできる!}$
$\huge{1、2、3 ぁっダー!}$