7-세그먼트를 이루는 신호가 꼬여 있을 때, 입력으로 들어오는 세그먼트 수와 빈도 등을 가지고 원래 어떤 숫자를 표시하려고 했는지를 유추하는 문제였다(거칠게 줄이면 이런데, 이번 문제는 이래저래 설명이 좀 길다. 자세하게는 원문을 참고!) 문제만으로는 '퍼즐'이라는 이름에 걸맞는 소재였는데, 이걸 코드로 짜려니 여간 귀찮은 것이 아니었다.
푼 방법을 대략 소개하면 이렇다. 아이디어가 어렵지는 않아서 구현에 집중하는 문제로 이해하고 풀었는데, 더 쉽게 풀 수 있는 방법이 있지 않을까 싶기도 하다.
# aaaa
# b c
# b c
# dddd
# e f
# e f
# gggg
- 세그먼트 수 2개짜리 입력은 1, 3개짜리 입력은 7, 4개짜리 입력은 4
- 1(
c, f
)과 7(a, c, f
)의 차이에서 윗변(a
) 찾기 - 세그먼트 수 5개짜리 입력인 0, 6, 9에서 공통적으로 나타나는 변은
a, b, f, g
- 0, 6, 9의 공통변(
a, b, f, g
)과 1(c, f
)의 차이에서 오른쪽 윗변(c
)을 찾고, 1을 이루는 변 중c
가 아닌 쪽이 오른쪽 아랫변(f
) - 4를 이루는 변(
b, c, d, f
)과 0, 6, 9의 공통변(a, b, f, g
)의 교집합(b, f
)에서 1을 이루지 않는 변이 왼쪽 윗변(b
) - 4를 이루는 변(
b, c, d, f
)에서 지금까지 구한b, c, f
를 제거하면 중앙변(d
) - 0, 6, 9의 공통변(
a, b, f, g
)에서 지금까지 구한a, b, f
를 제거하면 밑변(g
) - 여기까지 구한 뒤 남는 변이 왼쪽 아랫변(
e
)
올해 AoC에서 지금까지 풀었던 문제 중에 제일 시간 많이 쓴 듯. 통계 페이지를 보니 나만 고생한 게 아닌 것 같아서 조금이나마 위안이 된다.
defmodule Day8 do
@mapping_by_number %{
MapSet.new([:top, :top_right, :bot_right, :bot, :bot_left, :top_left]) => 0,
MapSet.new([:top_right, :bot_right]) => 1,
MapSet.new([:top, :top_right, :mid, :bot_left, :bot]) => 2,
MapSet.new([:top, :top_right, :mid, :bot_right, :bot]) => 3,
MapSet.new([:top_left, :top_right, :mid, :bot_right]) => 4,
MapSet.new([:top, :top_left, :mid, :bot_right, :bot]) => 5,
MapSet.new([:top, :top_left, :mid, :bot_left, :bot_right, :bot]) => 6,
MapSet.new([:top, :top_right, :bot_right]) => 7,
MapSet.new([:top, :top_left, :top_right, :mid, :bot_left, :bot_right, :bot]) => 8,
MapSet.new([:top, :top_left, :top_right, :mid, :bot_right, :bot]) => 9
}
def parse_input do
path = "input/day8.txt"
File.read!(path)
|> String.split("\n")
|> Enum.map(fn row ->
row
|> String.split(" | ")
|> Enum.map(&String.split(&1, " "))
end)
end
def run_q1 do
digits =
parse_input()
|> Enum.map(fn [_pattern, digit] -> digit end)
|> List.flatten()
digits
|> Enum.count(fn digit -> String.length(digit) in [2, 3, 4, 7] end)
end
def run_q2 do
parse_input()
|> Enum.map(fn [pattern, digit] ->
mapping_by_pos = %{
top: nil,
top_left: nil,
top_right: nil,
mid: nil,
bot_left: nil,
bot_right: nil,
bot: nil
}
# finding top
digit_1 = find_digit_by_number_of_segment(pattern, 2) |> hd()
digit_7 = find_digit_by_number_of_segment(pattern, 3) |> hd()
digit_4 = find_digit_by_number_of_segment(pattern, 4) |> hd()
mapping_by_pos =
Map.put(mapping_by_pos, :top, MapSet.difference(digit_7, digit_1) |> first())
# finding top_right, bot_right
[seg5_cand1, seg5_cand2, seg5_cand3] = find_digit_by_number_of_segment(pattern, 6)
segs_abfg =
seg5_cand1
|> MapSet.intersection(seg5_cand2)
|> MapSet.intersection(seg5_cand3)
mapping_by_pos =
Map.put(mapping_by_pos, :top_right, MapSet.difference(digit_1, segs_abfg) |> first())
mapping_by_pos =
Map.put(
mapping_by_pos,
:bot_right,
MapSet.delete(digit_1, mapping_by_pos.top_right) |> first()
)
# finding top_left
mapping_by_pos =
Map.put(
mapping_by_pos,
:top_left,
MapSet.intersection(segs_abfg, digit_4)
|> MapSet.delete(mapping_by_pos.bot_right)
|> first()
)
# finding mid
mapping_by_pos =
Map.put(
mapping_by_pos,
:mid,
digit_4
|> MapSet.delete(mapping_by_pos.top_right)
|> MapSet.delete(mapping_by_pos.top_left)
|> MapSet.delete(mapping_by_pos.bot_right)
|> first()
)
# finding bot
mapping_by_pos =
Map.put(
mapping_by_pos,
:bot,
segs_abfg
|> MapSet.delete(mapping_by_pos.top)
|> MapSet.delete(mapping_by_pos.top_left)
|> MapSet.delete(mapping_by_pos.bot_right)
|> first()
)
# finding bot_left
mapping_by_pos =
Map.put(
mapping_by_pos,
:bot_left,
["a", "b", "c", "d", "e", "f", "g"]
|> Kernel.--(Map.values(mapping_by_pos))
|> List.first()
)
map_to_digit(mapping_by_pos, digit)
end)
|> Enum.sum()
end
@spec find_digit_by_number_of_segment(list(String.t()), integer()) :: list(MapSet.t())
defp find_digit_by_number_of_segment(pattern, number_of_segment) do
pattern
|> Enum.filter(fn p -> String.length(p) == number_of_segment end)
|> Enum.map(fn p -> p |> String.codepoints() |> MapSet.new() end)
end
defp first(mapset) do
mapset |> MapSet.to_list() |> List.first()
end
defp map_to_digit(mapping_by_pos, digit) do
mapping_by_seg = Enum.map(mapping_by_pos, fn {k, v} -> {v, k} end) |> Enum.into(%{})
Enum.map(digit, fn d ->
key =
d |> String.codepoints() |> Enum.map(fn seg -> mapping_by_seg[seg] end) |> MapSet.new()
@mapping_by_number[key]
end)
|> Integer.undigits()
end
end
'호두나무 공방 > Advent of Code' 카테고리의 다른 글
Advent of Code 2021 Day 10 in Elixir - Syntax Scoring (0) | 2021.12.10 |
---|---|
Advent of Code 2021 Day 9 in Elixir - Smoke Basin (0) | 2021.12.09 |
Advent of Code 2021 Day 7 in Elixir - The Treachery of Whales (0) | 2021.12.07 |
Advent of Code 2021 Day 6 in Elixir - Lanternfish (0) | 2021.12.06 |
Advent of Code 2021 Day 5 in Elixir - Hydrothermal Venture (0) | 2021.12.06 |