정해진 길이의 로프의 앞 부분을 움직였을 때 끝 부분의 자취를 구하는 문제였다(첫 번째 문제는 짧은 줄, 두 번째 문제는 긴 줄). 플랑크 길이나 온갖 어려운 단어들이 나와서 이게 뭔 소린가 하며 읽었는데, 그런 부분까지 합해서 이번 AoC의 특색 있는 문제 중 하나로 꼽힐 것 같다.
코드 전체적인 구조를 잡는 데 조금 시간이 걸렸다. 로프 끝 부분은 시작 부분과 멀어져야 하는 상황에서만 움직이면 되는 것을 알고, 일단 로프 시작 부분을 움직인 뒤 머리 부분의 방향과 인접 여부를 따져 필요한 경우에만 끝 부분을 움직이도록 구현했다. case문이 좀 많긴 하지만 그래도 제법 깔끔하게 각 경우를 정리하지 않았나 싶다.
defmodule Omicron.Day9 do
@dir "data/day9/"
defmodule Trace do
defstruct head: {0, 0}, tail: {0, 0}, visit: MapSet.new()
end
defmodule Trace2 do
defstruct knots: List.duplicate({0, 0}, 10), visit: MapSet.new()
end
def q1(file_name \\ "q.txt") do
File.read!(@dir <> file_name)
|> String.split("\n")
|> Enum.flat_map(fn move ->
String.split(move, " ")
|> then(fn [dir, cnt] -> List.duplicate(dir, String.to_integer(cnt)) end)
end)
|> Enum.reduce(%Trace{}, fn
dir, %Trace{head: {x_h, y_h}, tail: {x_t, y_t} = t, visit: v} = acc ->
new_head =
case dir do
"R" -> {x_h + 1, y_h}
"L" -> {x_h - 1, y_h}
"D" -> {x_h, y_h - 1}
"U" -> {x_h, y_h + 1}
end
new_tail =
case relative_pos(new_head, t) do
{:same, true} -> t
{_, true} -> t
{:head_is_right, _} -> {x_t + 1, y_t}
{:head_is_up_right, _} -> {x_t + 1, y_t + 1}
{:head_is_down_right, _} -> {x_t + 1, y_t - 1}
{:head_is_up, _} -> {x_t, y_t + 1}
{:head_is_down, _} -> {x_t, y_t - 1}
{:head_is_left, _} -> {x_t - 1, y_t}
{:head_is_up_left, _} -> {x_t - 1, y_t + 1}
{:head_is_down_left, _} -> {x_t - 1, y_t - 1}
end
%{acc | head: new_head, tail: new_tail, visit: MapSet.put(v, new_tail)}
|> tap(fn v -> Map.take(v, [:head, :tail]) |> IO.inspect() end)
end)
|> Map.get(:visit)
|> MapSet.size()
end
def q2(file_name \\ "q.txt") do
File.read!(@dir <> file_name)
|> String.split("\n")
|> Enum.flat_map(fn move ->
String.split(move, " ")
|> then(fn [dir, cnt] -> List.duplicate(dir, String.to_integer(cnt)) end)
end)
|> Enum.reduce(%Trace2{}, fn
dir, %Trace2{knots: [{x_h, y_h} | _] = knots, visit: v} = acc ->
new_head =
case dir do
"R" -> {x_h + 1, y_h}
"L" -> {x_h - 1, y_h}
"D" -> {x_h, y_h - 1}
"U" -> {x_h, y_h + 1}
end
new_knots_reversed =
Enum.reduce(tl(knots), [new_head], fn {x_t, y_t} = t, [h | _] = knot_acc ->
new_tail =
case relative_pos(h, t) do
{:same, true} -> t
{_, true} -> t
{:head_is_right, _} -> {x_t + 1, y_t}
{:head_is_up_right, _} -> {x_t + 1, y_t + 1}
{:head_is_down_right, _} -> {x_t + 1, y_t - 1}
{:head_is_up, _} -> {x_t, y_t + 1}
{:head_is_down, _} -> {x_t, y_t - 1}
{:head_is_left, _} -> {x_t - 1, y_t}
{:head_is_up_left, _} -> {x_t - 1, y_t + 1}
{:head_is_down_left, _} -> {x_t - 1, y_t - 1}
end
[new_tail | knot_acc]
end)
%{
acc
| knots: Enum.reverse(new_knots_reversed),
visit: MapSet.put(v, hd(new_knots_reversed))
}
|> tap(fn v -> Map.take(v, [:knots]) |> IO.inspect() end)
end)
|> Map.get(:visit)
|> MapSet.size()
end
defp relative_pos({x_h, y_h} = h, {x_t, y_t} = t) do
cond do
h == t -> :same
x_h > x_t and y_h == y_t -> :head_is_right
x_h > x_t and y_h > y_t -> :head_is_up_right
x_h > x_t and y_h < y_t -> :head_is_down_right
x_h == x_t and y_h > y_t -> :head_is_up
x_h == x_t and y_h < y_t -> :head_is_down
x_h < x_t and y_h == y_t -> :head_is_left
x_h < x_t and y_h > y_t -> :head_is_up_left
x_h < x_t and y_h < y_t -> :head_is_down_left
true -> :unknown
end
|> then(fn atom ->
{atom, adjacent?(h, t)}
end)
end
defp adjacent?({x_h, y_h}, {x_t, y_t}) do
abs(x_t - x_h) <= 1 and abs(y_t - y_h) <= 1
end
end
'호두나무 공방 > Advent of Code' 카테고리의 다른 글
Advent of Code 2022 Day 11 in Elixir - Monkey in the Middle 풀이 (0) | 2022.12.17 |
---|---|
Advent of Code 2022 Day 10 in Elixir - Cathode-Ray Tube 풀이 (0) | 2022.12.11 |
Advent of Code 2022 Day 8 in Elixir - Treetop Tree House 풀이 (0) | 2022.12.11 |
Advent of Code 2022 Day 7 in Elixir - No Space Left On Device 풀이 (0) | 2022.12.11 |
Advent of Code 2022 Day 6 in Elixir - Tuning Trouble 풀이 (0) | 2022.12.10 |