일정 규칙에 따라 해삼(sea cucumber)이 움직일 때, 이들의 움직임이 언제 멈추는지를 구하는 문제였다. 25일차는 문제가 하나로, 나머지 한 문제는 24일차까지의 모든 문제를 맞추는 것으로 해결한 것으로 보는 것 같다(나는 19일차 한 문제가 부족하다. 후샏...).
규칙을 단순히 코드로 옮기면 되는 것뿐이라 구현에 어려움은 없었다. 얼마나 깔끔하게 만들 수 있느냐가 승부지점인 느낌.
defmodule Day25 do
def parse_input(file_name) do
input =
file_name
|> File.read!()
|> String.split("\n")
|> Enum.map(&String.codepoints/1)
w = input |> hd() |> length()
h = input |> length()
{nested_list_to_map(input), w, h}
end
defp nested_list_to_map(nested_list) do
nested_list
|> Enum.with_index()
|> Enum.flat_map(fn {row, row_index} ->
row
|> Enum.with_index()
|> Enum.map(fn {val, col_index} ->
{{col_index, row_index}, val}
end)
|> Enum.reject(fn {_k, v} -> v == "." end)
end)
|> Enum.into(%{})
end
def run_q1(file_name \\ "input/day25.txt") do
file_name
|> parse_input()
|> do_run_q1(0, false)
|> print()
|> IO.puts()
end
defp do_run_q1({map, w, h}, count, true) do
IO.inspect(count)
{map, w, h}
end
defp do_run_q1({map, w, h}, count, false) do
east_move =
map
|> Enum.reduce(%{}, fn
{{col_index, row_index}, ">" = ch}, acc_map ->
case next_point_empty?({map, w, h}, {col_index, row_index}, ch) do
{next_point, ^ch, true} ->
Map.put(acc_map, next_point, ch)
{_, ^ch, false} ->
Map.put(acc_map, {col_index, row_index}, ch)
end
{{col_index, row_index}, "v" = ch}, acc_map ->
Map.put(acc_map, {col_index, row_index}, ch)
end)
# print({east_move, w, h})
# |> IO.puts()
south_move =
east_move
|> Enum.reduce(%{}, fn
{{col_index, row_index}, "v" = ch}, acc_map ->
case next_point_empty?({east_move, w, h}, {col_index, row_index}, ch) do
{next_point, ^ch, true} ->
Map.put(acc_map, next_point, ch)
{_, ^ch, false} ->
Map.put(acc_map, {col_index, row_index}, ch)
end
{{col_index, row_index}, ">" = ch}, acc_map ->
Map.put(acc_map, {col_index, row_index}, ch)
end)
do_run_q1({south_move, w, h}, count + 1, map == south_move)
end
defp next_point_empty?({map, w, h}, {x, y}, ch) do
next_point =
case ch do
">" -> if x == w - 1, do: {0, y}, else: {x + 1, y}
"v" -> if y == h - 1, do: {x, 0}, else: {x, y + 1}
_ -> nil
end
{
next_point,
ch,
not is_nil(next_point) and Map.get(map, next_point, ".") == "."
}
end
defp print({map, w, h}) do
0..(h - 1)
|> Enum.map(fn y ->
0..(w - 1)
|> Enum.map(fn x ->
Map.get(map, {x, y}, ".")
end)
|> Enum.join("")
end)
|> Enum.join("\n")
end
end
이렇게 AoC2021의 모든 문제를 풀어봤다. 답만 겨우 맞춘 문제도 있고, 먼저 푼 다른 사람들의 코드에서 인사이트를 얻은 경우도 있지만, 한 문제를 제외하고 모두 직접 코드를 작성하여 해결했다는 점에 의의를 두고 싶다. 2년 전에 도전했을 때 중도하차했던 것을 생각하면 꽤 만족스럽다.
여러 다른 일로 스케줄이 꼬이면서 중간부터는 리얼타임으로 달리지 못했지만, 한 달동안 매일 몇 시간씩 시간 내는 것 자체가 쉽지 않은 일이므로 12월 안에 완주한 것 자체로 의미 있는 결과라고 평가하고 싶다(글 올리는 것은 조금 늦어 해를 넘겨버렸지만). 자체축하🎉
후반에 난이도가 상승함과 더불어 마음도 급해지면서 알아보기 쉽지 않은 코드를 꽤 작성한 것 같다. 다음 도전에는 조금 더 나은 실력으로 좋은 코드를 작성할 수 있었으면 좋겠다.
덧. 입력 파싱, 순열이나 조합, 중첩 리스트 처리, 진법 변환, 그림 출력과 같이 자주 쓰이는 기능들은 미리 유틸리티성 함수로 만들어 두면 나중에 여러모로 도움이 될 것 같다. 이상!
'호두나무 공방 > Advent of Code' 카테고리의 다른 글
Advent of Code 2022 Day 2 in Elixir - Rock Paper Scissors 풀이 (0) | 2022.12.08 |
---|---|
Advent of Code 2022 Day 1 in Elixir - Calorie Counting 풀이 (0) | 2022.12.08 |
Advent of Code 2021 Day 24 in Elixir - Arithmetic Logic Unit 풀이 (0) | 2022.01.01 |
Advent of Code 2021 Day 23 in Elixir - Amphipod 풀이 (0) | 2021.12.31 |
Advent of Code 2021 Day 22 in Elixir - Reactor Reboot 풀이 (0) | 2021.12.30 |