Day 4
Mix.install([
{:kino, "~> 0.7.0"}
])
Ceres Search
Part 1
“Looks like the Chief’s not here. Next!” One of The Historians pulls out a device and pushes the only button on it. After a brief flash, you recognize the interior of the Ceres monitoring station!
As the search for the Chief continues, a small Elf who lives on the station tugs on your shirt; she’d like to know if you could help her with her word search (your puzzle input). She only has to find one word: XMAS.
This word search allows words to be horizontal, vertical, diagonal, written backwards, or even overlapping other words. It’s a little unusual, though, as you don’t merely need to find one instance of XMAS - you need to find all of them. Here are a few ways XMAS might appear, where irrelevant characters have been replaced with .
:
..X...
.SAMX.
.A..A.
XMAS.S
.X....
The actual word search will be full of letters instead. For example:
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
In this word search, XMAS occurs a total of 18 times; here’s the same word search again, but where letters not involved in any XMAS have been replaced with .:
....XXMAS.
.SAMXMS...
...S..A...
..A.A.MS.X
XMASAMX.MM
X.....XA.A
S.S.S.S.SS
.A.A.A.A.A
..M.M.M.MM
.X.X.XMASX
Take a look at the little Elf’s word search. How many times does XMAS appear?
input = Kino.Input.textarea("Puzzle input")
directions = [
# ➡️
[{0, 0}, {0, 1}, {0, 2}, {0, 3}],
# ↘️
[{0, 0}, {1, 1}, {2, 2}, {3, 3}],
# ⬇️
[{0, 0}, {1, 0}, {2, 0}, {3, 0}],
# ↙️
[{0, 0}, {1, -1}, {2, -2}, {3, -3}],
# ⬅️
[{0, 0}, {0, -1}, {0, -2}, {0, -3}],
# ↖️
[{0, 0}, {-1, -1}, {-2, -2}, {-3, -3}],
# ⬆️
[{0, 0}, {-1, 0}, {-2, 0}, {-3, 0}],
# ↗️
[{0, 0}, {-1, 1}, {-2, 2}, {-3, 3}]
]
positions =
Kino.Input.read(input)
|> String.split("\n")
|> Enum.with_index()
|> Enum.flat_map(fn {line, line_index} ->
line
|> String.graphemes()
|> Enum.with_index()
|> Enum.map(fn {grapheme, index} -> {{line_index, index}, grapheme} end)
end)
|> Map.new()
x_positions =
positions |> Map.filter(fn {_position, grapheme} -> grapheme == "X" end) |> Map.keys()
x_positions
|> Enum.map(fn {x_line, x_index} ->
directions
|> Enum.map(fn direction ->
direction
|> Enum.map(fn {line_offset, index_offset} ->
Map.get(positions, {x_line + line_offset, x_index + index_offset})
end)
|> Enum.join()
end)
|> Enum.count(fn word -> word == "XMAS" end)
end)
|> Enum.sum()
Part 2
The Elf looks quizzically at you. Did you misunderstand the assignment?
Looking for the instructions, you flip over the word search to find that this isn’t actually an XMAS puzzle; it’s an X-MAS puzzle in which you’re supposed to find two MAS in the shape of an X. One way to achieve that is like this:
M.S
.A.
M.S
Irrelevant characters have again been replaced with . in the above diagram. Within the X, each MAS can be written forwards or backwards.
Here’s the same example from before, but this time all of the X-MASes have been kept instead:
.M.S......
..A..MSMS.
.M.S.MAA..
..A.ASMSM.
.M.S.M....
..........
S.S.S.S.S.
.A.A.A.A..
M.M.M.M.M.
..........
In this example, an X-MAS appears 9 times.
Flip the word search from the instructions back over to the word search side and try again. How many times does an X-MAS appear?
directions = [
# ↘️
[{-1, -1}, {0, 0}, {1, 1}],
# ↙️
[{-1, 1}, {0, 0}, {1, -1}],
# ↖️
[{1, 1}, {0, 0}, {-1, -1}],
# ↗️
[{1, -1}, {0, 0}, {-1, 1}]
]
positions =
Kino.Input.read(input)
|> String.split("\n")
|> Enum.with_index()
|> Enum.flat_map(fn {line, line_index} ->
line
|> String.graphemes()
|> Enum.with_index()
|> Enum.map(fn {grapheme, index} -> {{line_index, index}, grapheme} end)
end)
|> Map.new()
a_positions =
positions |> Map.filter(fn {_position, grapheme} -> grapheme == "A" end) |> Map.keys()
a_positions
|> Enum.map(fn {a_line, a_index} ->
directions
|> Enum.map(fn direction ->
direction
|> Enum.map(fn {line_offset, index_offset} ->
Map.get(positions, {a_line + line_offset, a_index + index_offset})
end)
|> Enum.join()
end)
|> Enum.count(fn word -> word == "MAS" end) == 2
end)
|> Enum.count(& &1)