はじめに
Advent of code 2024 Day 14 の Part 2 と Part 2 を解きます
問題文はこちら
実装したノートブックはこちら
セットアップ
Kino AOC をインストールします
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Kino AOC の使い方はこちらを参照
入力の取得
"Advent of Code Helper" スマートセルを追加し、 Day 14 の入力を取得します
私の答え
私の答えです。
折りたたんでおきます。
▶を押して開いてください。
Part 1
回答
入力例で考えます
sample_input =
"""
p=0,4 v=3,-3
p=6,3 v=-1,-3
p=10,3 v=-1,2
p=2,0 v=2,-1
p=0,0 v=1,3
p=3,0 v=-2,-2
p=7,6 v=-1,-3
p=3,0 v=-1,-2
p=9,3 v=2,3
p=7,3 v=-1,2
p=2,4 v=2,-3
p=9,5 v=-3,-3
"""
|> String.trim()
入力を初期位置と移動速度を持つロボットとして読み込みます
parse_robots = fn input ->
input
|> String.split("\n")
|> Enum.map(fn row ->
Regex.named_captures(
~r/p=(?<px>\d+),(?<py>\d+) v=(?<vx>\-*\d+),(?<vy>\-*\d+)/,
row
)
end)
|> Enum.map(fn %{"px" => px, "py" => py, "vx" => vx, "vy" => vy} ->
%{
p: {String.to_integer(px), String.to_integer(py)},
v: {String.to_integer(vx), String.to_integer(vy)}
}
end)
end
robots = parse_robots.(sample_input)
実行結果
[
%{p: {0, 4}, v: {3, -3}},
%{p: {6, 3}, v: {-1, -3}},
%{p: {10, 3}, v: {-1, 2}},
%{p: {2, 0}, v: {2, -1}},
%{p: {0, 0}, v: {1, 3}},
%{p: {3, 0}, v: {-2, -2}},
%{p: {7, 6}, v: {-1, -3}},
%{p: {3, 0}, v: {-1, -2}},
%{p: {9, 3}, v: {2, 3}},
%{p: {7, 3}, v: {-1, 2}},
%{p: {2, 4}, v: {2, -3}},
%{p: {9, 5}, v: {-3, -3}}
]
ロボットが指定回数動いた場合の位置を計算します
指定されたタイル数の範囲でループするため、現在位置はタイル数で割った余りになります
move = fn %{p: {px, py}, v: {vx, vy}}, {tx, ty}, step ->
{
(px + vx * step) |> rem(tx) |> Kernel.+(tx) |> rem(tx),
(py + vy * step) |> rem(ty) |> Kernel.+(ty) |> rem(ty)
}
end
tiles = {11, 7}
0..5
|> Enum.map(fn step ->
move.(%{p: {2, 4}, v: {2, -3}}, tiles, step)
end)
実行結果
[{2, 4}, {4, 1}, {6, 5}, {8, 2}, {10, 6}, {1, 3}]
%{p: {2, 4}, v: {2, -3}}
のロボットを 5 回動かした位置が問題文と一致しています
各ロボットの位置が四象限のどこに入るかを取得します
ただし、縦横中央線上にいる場合は無視します
get_quadrants = fn points, {tx, ty} ->
init_quadrants = %{
{0, 0} => 0,
{0, 1} => 0,
{1, 0} => 0,
{1, 1} => 0
}
bx = div(tx, 2)
by = div(ty, 2)
points
|> Enum.reduce(init_quadrants, fn {px, py}, acc_quadrants ->
{
cond do
px == bx -> nil
px < bx -> 0
true -> 1
end,
cond do
py == by -> nil
py < by -> 0
true -> 1
end
}
|> case do
{nil, _} ->
acc_quadrants
{_, nil} ->
acc_quadrants
key ->
Map.put(acc_quadrants, key, Map.get(acc_quadrants, key) + 1)
end
end)
end
quadrants =
robots
|> Enum.map(fn robot ->
move.(robot, tiles, 100)
end)
|> get_quadrants.(tiles)
実行結果
%{{0, 0} => 1, {0, 1} => 4, {1, 0} => 3, {1, 1} => 1}
各象限のロボット数を掛け合わせます
Enum.reduce(quadrants, 1, fn {_, num}, acc -> acc * num end)
実行結果
12
タイルを {101, 103}
として、実際の入力で計算します
tiles = {101, 103}
puzzle_input
|> parse_robots.()
|> Enum.map(fn robot ->
move.(robot, tiles, 100)
end)
|> get_quadrants.(tiles)
|> Enum.reduce(1, fn {_, num}, acc -> acc * num end)
Part 2
回答
Part 1 は素直に解けばいいだけでしたが、 Part 2 ははっきり言って問題文が悪いです
「ロボットの配置がクリスマスツリーに見えるとき」というのが、どんな絵なのか示されていないため、全くの手探りになってしまいます
最終的に、実は「ロボットが1台も重ならないとき」が答えでした
ロボットの配置を視覚化する関数を用意します
display_map = fn points, {tx, ty} ->
0..ty-1
|> Enum.map(fn y ->
0..tx-1
|> Enum.map(fn x ->
case Enum.find(points, fn point -> point == {x, y} end) do
nil -> " "
_ -> "X"
end
end)
|> Enum.join()
end)
|> Enum.join("\n")
end
入力例で表示してみます
tiles = {11, 7}
sample_input
|> parse_robots.()
|> Enum.map(fn robot ->
move.(robot, tiles, 100)
end)
|> display_map.(tiles)
|> Kino.Text.new(terminal: true)
実行結果
X X
X
XX
X
XX
X X
実際の入力でロボットが重なっていない(重複排除しても変わらない)瞬間を探索し、視覚化します
robots = parse_robots.(puzzle_input)
tiles = {101, 103}
1..10000
|> Enum.find(fn step ->
points =
robots
|> Enum.map(fn robot ->
move.(robot, tiles, step)
end)
|> Enum.sort()
Enum.uniq(points) == points
end)
|> then(fn step ->
IO.inspect(step)
robots
|> Enum.map(fn robot ->
move.(robot, tiles, step)
end)
|> display_map.(tiles)
|> Kino.Text.new(terminal: true)
end)
実行結果
X
X
X X
X X
X X
X X X X
X X
X
X X
X X X X
X
X X
X X X
X X
X X X
X X
X X X
X X XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X X X
X X
X X X X
X X X X
X X X
X X XXX X
X XXXXX X
X XXXXXXX X X X
X XXXXXXXXX X X X
X XXXXX X X
X XXXXXXX X
X X X XXXXXXXXX X X X
X XXXXXXXXXXX X
X XXXXXXXXXXXXX X
X XXXXXXXXX X X XX
X XXXXXXXXXXX X
X XXXXXXXXXXXXX X
X X XXXXXXXXXXXXXXX X
X X XXXXXXXXXXXXXXXXX X
X X X XXXXXXXXXXXXX X X
X XXXXXXXXXXXXXXX X X
X XXXXXXXXXXXXXXXXX X X X
X X XXXXXXXXXXXXXXXXXXX X X
X X XXXXXXXXXXXXXXXXXXXXX X X
X X XXX X
X XXX X X
X XXX X
X X
X X X
X X
X X X X
X X XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X
X X
X X X X
X
X
X X
X X
X
X X
X X X
X X
X
X X
X
X X
X
X
X X X
X X
X X X X X
X X
X
X X X
X X X
X X
X
X X X
X
X
X X
X X X X
X X X
X X
X
X
X
X
確かにクリスマスツリーです
まとめ
問題文から ChatGPT に画像を生成してもらいましたが、 ChatGPT はなぜか画像ではなくアスキーアートを生成してくれました
/\_/\
( o.o ) < イースターバニー本部
> ^ <
~~~~~~~~~~~~
| E B H Q |
| (トイレ) |
|____________|
||||||||||||
||||||||||||
..............
.............. ← タイル状フロア
...r...r..... ← ロボットたち (r)
......r......
..r.......r..
...r..r.......
...........r..
可愛いからいいか
/\_/\
( o.o ) < イースターバニー本部
> ^ <
あと、 Part 2 は問題文を修正すべきだと思う