はじめに
Advent of code 2024 Day 4 の Part 2 を解きます
問題文はこちら
実装したノートブックはこちら
セットアップ
Kino AOC をインストールします
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Kino AOC の使い方はこちらを参照
入力の取得
"Advent of Code Helper" スマートセルを追加し、 Day 4 の入力を取得します
私の答え
私の答えです。
折りたたんでおきます。
▶を押して開いてください。
回答
まずは小さい入力例で解いてみます
input =
"""
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
"""
斜めに読んで MAS
を探すため、入力をずらして回転します
turned_input =
input
|> String.trim()
|> String.split("\n")
|> Enum.with_index()
|> Enum.map(fn {row, index} ->
String.duplicate(".", index) <> row <> String.duplicate(".", String.length(row) - index - 1)
end)
|> Enum.map(&String.codepoints(&1))
|> Enum.zip()
|> Enum.map(fn col -> col |> Tuple.to_list() |> Enum.join() end)
|> Enum.join("\n")
IO.puts(turned_input)
標準出力
M.........
MM........
MSA.......
SAMM......
XMXSX.....
XXSAMX....
MMXMAXS...
ASMASAMS..
SMASAMSAM.
MSAMMMMXAM
.AMSXXSAMX
..MMAXAMMM
...XMASAMX
....MMXSXA
.....ASAMX
......SAMM
.......AMA
........MS
.........X
斜めの中で MAS
を M+S
、 SAM
を S+M
に変換します
turned_input
|> String.replace(~r/MAS/, "M+S")
|> String.replace(~r/SAM/, "S+M")
|> IO.puts()
標準出力
M.........
MM........
MSA.......
S+MM......
XMXSX.....
XXS+MX....
MMXMAXS...
ASM+S+MS..
SM+S+MS+M.
MS+MMMMXAM
.AMSXXS+MX
..MMAXAMMM
...XM+S+MX
....MMXSXA
.....AS+MX
......S+MM
.......AMA
........MS
.........X
元の角度に戻して +
以外を .
に変換して見やすくします
output_a =
turned_input
|> String.replace(~r/MAS/, "M+S")
|> String.replace(~r/SAM/, "S+M")
|> String.split("\n")
|> Enum.map(&String.codepoints(&1))
|> Enum.zip()
|> Enum.map(fn col ->
col
|> Tuple.to_list()
|> Enum.join()
|> String.trim(".")
|> String.replace(~r/[A-Z]/, ".")
end)
output_a
|> Enum.join("\n")
|> IO.puts()
標準出力
..........
..+.......
......++..
..+.+.....
....+.....
..+....+..
..........
.+.+.+.++.
..........
..........
逆方向の斜めで読んだとき、同じ位置に +
があれば、交差したとみなすことができます
逆方向で試す前に、回転と置換を関数化しておきます
turn = fn input, direction ->
input
|> String.trim()
|> String.split("\n")
|> Enum.with_index()
|> Enum.map(fn {row, index} ->
case direction do
:left ->
String.duplicate(".", index) <> row <> String.duplicate(".", String.length(row) - index - 1)
_ ->
String.duplicate(".", String.length(row) - index - 1) <> row <> String.duplicate(".", index)
end
end)
|> Enum.map(&String.codepoints(&1))
|> Enum.zip()
|> Enum.map(fn col -> col |> Tuple.to_list() |> Enum.join() end)
|> Enum.join("\n")
end
replase_mas = fn turned_input ->
turned_input
|> String.replace(~r/MAS/, "M+S")
|> String.replace(~r/SAM/, "S+M")
|> String.split("\n")
|> Enum.map(&String.codepoints(&1))
|> Enum.zip()
|> Enum.map(fn col ->
col
|> Tuple.to_list()
|> Enum.join()
|> String.trim(".")
|> String.replace(~r/[A-Z]/, ".")
end)
end
関数を使って回転と置換を実行します
output_b =
input
|> turn.(:right)
|> replase_mas.()
output_b
|> Enum.join("\n")
|> IO.puts()
標準出力
..........
..+.......
......++..
..+.+.....
..+.......
..........
.....+....
.+.+.+.+..
.+........
..........
MAS
が交差した点 = 左回転した方と右回転した方の両方で +
になった点を数えます
Enum.zip(output_a, output_b)
|> Enum.map(fn {row_a, row_b} ->
Enum.zip(
String.codepoints(row_a),
String.codepoints(row_b)
)
|> Enum.count(fn ab -> ab == {"+", "+"} end)
end)
|> Enum.sum()
実行結果
9
同じことを実際の入力に対して実行します
output_a =
puzzle_input
|> turn.(:left)
|> replase_mas.()
output_b =
puzzle_input
|> turn.(:right)
|> replase_mas.()
Enum.zip(output_a, output_b)
|> Enum.map(fn {row_a, row_b} ->
Enum.zip(
String.codepoints(row_a),
String.codepoints(row_b)
)
|> Enum.count(fn ab -> ab == {"+", "+"} end)
end)
|> Enum.sum()
まとめ
問題文から ChatGPT に画像を生成してもらいました
「交差する点」をどう探すかが面白い問題でした
Part 1 もそうですが、出力がアートのようになってキレイですね