part 1:
part 2:
part 1:
part 2:
My naive part 2 decoder:
%{
2 => [one],
3 => [seven],
4 => [four],
5 => two_three_five,
6 => zero_six_nine,
7 => [eight]
} =
Enum.group_by(input, &byte_size/1, fn segments ->
segments |> to_charlist() |> Enum.sort()
end)
[nine, zero, six] =
Enum.sort_by(zero_six_nine, fn segments ->
# 0 -> 4 + 3
# 6 -> 5 + 3
# 9 -> 4 + 2
length(segments -- one) + length(segments -- four)
end)
[three, five, two] =
Enum.sort_by(two_three_five, fn segments ->
# 3 -> 3 + 2
# 2 -> 4 + 3
# 5 -> 4 + 2
length(segments -- one) + length(segments -- four)
end)
which when given
input = ["be", "cfbegad", "cbdgef", "fgaecd", "cgeb", "fdcge", "agebfd", "fecdb", "fabcd", "edb"]
results in
iex> decoded = %{0 => zero, 1 => one, 2 => two, 3 => three, 4 => four, 5 => five, 6 => six, 7 => seven, 8 => eight, 9 => nine}
%{
0 => 'abdefg',
1 => 'be',
2 => 'abcdf',
3 => 'bcdef',
4 => 'bceg',
5 => 'cdefg',
6 => 'acdefg',
7 => 'bde',
8 => 'abcdefg',
9 => 'bcdefg'
}
I suppose that yesterday’s puzzles were very easy to compensate for today’s harder puzzles. Here is my solution:
Second part approach:
def solve(stream, :second) do
stream
|> parse() # returns a tuple {patterns, output}, both being sorted charlists
|> Stream.map(&derive_numbers/1)
|> Enum.sum()
end
defp derive_numbers({patterns, output}) do
%{2 => [one], 3 => [seven], 4 => [four], 5 => len_five, 6 => len_six, 7 => [eight]} =
(output ++ patterns)
|> Enum.uniq()
|> Enum.group_by(&Kernel.length/1)
{[nine], len_six} = Enum.split_with(len_six, fn nr -> four -- nr == [] end)
{[zero], [six]} = Enum.split_with(len_six, fn nr -> seven -- nr == [] end)
{[three], len_five} = Enum.split_with(len_five, fn nr -> seven -- nr == [] end)
{[five], [two]} = Enum.split_with(len_five, fn nr -> nr -- six == [] end)
output
|> Enum.map(fn
^one -> 1
^two -> 2
^three -> 3
^four -> 4
^five -> 5
^six -> 6
^seven -> 7
^eight -> 8
^nine -> 9
^zero -> 0
end)
|> Integer.undigits()
end
This year I am learning Rust with AoC, but today I could not. It is sooo straightforward in Elixir that I could not look elsewhere.
I like your approach @mruoss , my code is finding a mapping from segment to actual segment and the remap the segments of each digit to finally find the digit value, but yours is way simpler.
I went down the same route initially - of looking segment-by-segment. But somewhere I realised that I just need the numbers themselves - and then I modified the code.
That was definitely a red herring - given how much time they spent explaining the segments and matching them…
Used a very similar approach to @mruoss - but used MapSet
defmodule Day8 do
def input2nums(input) do
segs =
input
|> String.split
|> Enum.map(&String.to_charlist/1)
|> Enum.map(&MapSet.new/1)
|> Enum.sort_by(&MapSet.size/1)
one = Enum.at(segs, 0)
seven = Enum.at(segs, 1)
four = Enum.at(segs, 2)
eight = Enum.at(segs, 9)
possible_253 = [Enum.at(segs, 3), Enum.at(segs, 4), Enum.at(segs, 5)]
possible_069 = [Enum.at(segs, 6), Enum.at(segs, 7), Enum.at(segs, 8)]
[six] =
Enum.filter(
possible_069,
fn num -> 1 == MapSet.intersection(num, one) |> MapSet.size end)
[three] =
Enum.filter(
possible_253,
fn num -> 2 == MapSet.intersection(num, one) |> MapSet.size end)
[nine] =
Enum.filter(
possible_069,
fn num -> MapSet.intersection(num, three) == three end)
[zero] = possible_069 -- [six, nine]
[two] =
Enum.filter(
possible_253,
fn num -> 2 == MapSet.intersection(num, four) |> MapSet.size end)
[five] = possible_253 -- [two, three]
[zero, one, two, three, four, five, six, seven, eight, nine]
end
def output2num(line, nums_set) do
line =
line
|> String.split
|> Enum.map(&String.to_charlist/1)
|> Enum.map(&MapSet.new/1)
out = for x <- line, do: Enum.find_index(nums_set, &(&1 == x))
Enum.join(out)
|> String.to_integer
end
def convert_line(input, output) do
nums_set = input2nums(input)
output2num(output, nums_set)
end
end
Thanks @josevalim for custom operators. Again - LiveBook:
input =
File.stream!("day8.txt")
|> Stream.map(fn line ->
line
|> String.split(" | ")
|> Enum.map(fn part ->
part
|> String.trim()
|> String.split(" ")
|> Enum.map(fn disp ->
disp
|> String.to_charlist()
|> Enum.sort()
|> List.to_string()
end)
end)
|> List.to_tuple()
end)
#Stream<[
enum: %File.Stream{
line_or_bytes: :line,
modes: [:raw, :read_ahead, :binary],
path: "day8.txt",
raw: true
},
funs: [#Function<47.58486609/1 in Stream.map/2>]
]>
input
|> Enum.map(fn {_, output} ->
Enum.count(output, &(byte_size(&1) in [2, 3, 4, 7]))
end)
|> Enum.sum()
390
defmodule Day8.Task2 do
def a --- b do
MapSet.difference(a, b)
end
def a +++ b do
MapSet.union(a, b)
end
def a <~> b do
MapSet.intersection(a, b)
end
def a <|> b do
(a +++ b) --- (a <~> b)
end
# 1. 7. 4. 2|3|5. 2|3|5. 2|3|5. 6|9|0. 6|9|0. 6|9|0. 8.
def deduce({cf, acf, bcdf, acdeg, acdfg, abdfg, abdefg, abcdfg, abcefg, abcdefg}) do
a = acf --- cf
eg = abcdefg --- (acf +++ bcdf)
bd = bcdf --- cf
abfg = abdefg <|> abcdfg <|> abcefg
b = abfg <~> bd
f = abfg <~> cf
g = abfg --- (a +++ b +++ f)
d = bd --- b
c = cf --- f
e = eg --- g
{a, b, c, d, e, f, g} =
[a, b, c, d, e, f, g]
|> Enum.map(&extract/1)
|> List.to_tuple()
[
# 0
[a, b, c, e, f, g],
# 1
[c, f],
# 2
[a, c, d, e, g],
# 3
[a, c, d, f, g],
# 4
[b, c, d, f],
# 5
[a, b, d, f, g],
# 6
[a, b, d, e, f, g],
# 7
[a, c, f],
# 8
[a, b, c, d, e, f, g],
# 9
[a, b, c, d, f, g]
]
|> Enum.map(&List.to_string(Enum.sort(&1)))
|> Enum.with_index()
|> Map.new()
end
defp extract(a) do
[v] = MapSet.to_list(a)
v
end
def decode(matches, output) do
output
|> Enum.map(&matches[&1])
|> Integer.undigits()
end
end
input
|> Enum.map(fn {input, output} ->
input
|> Enum.sort_by(&byte_size/1)
|> Enum.map(&MapSet.new(String.to_charlist(&1)))
|> List.to_tuple()
|> Day8.Task2.deduce()
|> Day8.Task2.decode(output)
end)
|> Enum.sum()
warning: variable "abdfg" is unused (if the variable is not meant to be used, prefix it with an underscore)
solutions.livemd#cell:19: Day8.Task2.deduce/1
warning: variable "acdeg" is unused (if the variable is not meant to be used, prefix it with an underscore)
solutions.livemd#cell:19: Day8.Task2.deduce/1
warning: variable "acdfg" is unused (if the variable is not meant to be used, prefix it with an underscore)
solutions.livemd#cell:19: Day8.Task2.deduce/1
1011785
Ended up going with charlists in p2. Surprisingly straightforward once I wrote all the rules down.
How problematic is it that I keep getting away with just lists and maps?
# 1,4,7,8 -> 9,3 -> 5,0 -> 2,6
create_map = fn rosseta ->
%{2 => [one], 3 => [seven], 4 => [four], 5 => p_235, 6 => p_069, 7 => [eight]} =
rosseta
|> Enum.map(&String.to_charlist/1)
|> Enum.map(&Enum.sort/1)
|> Enum.group_by(&length/1)
nine = Enum.find(p_069, fn d -> length(d -- four) == 2 end)
three = Enum.find(p_235, fn d -> length(d -- one) == 3 end)
five = Enum.find(p_235 -- [three], fn d -> length(nine -- d) == 1 end)
zero = Enum.find(p_069 -- [nine], fn d -> length(d -- one) == 4 end)
two = (p_235 -- [three, five]) |> List.flatten()
six = (p_069 -- [nine, zero]) |> List.flatten()
%{
zero => 0,
one => 1,
two => 2,
three => 3,
four => 4,
five => 5,
six => 6,
seven => 7,
eight => 8,
nine => 9
}
end
decode = fn map, digits ->
digits
|> Enum.map(&String.to_charlist/1)
|> Enum.map(&Enum.sort/1)
|> Enum.map(&Map.get(map, &1))
|> Integer.undigits()
end
# [ [[first_part], [second]], [[first_part], [second]] ]
lines
|> Enum.map(fn line ->
create_map.(Enum.at(line, 0)) |> decode.(Enum.at(line, 1))
end)
|> Enum.sum()
Full day 8 livebook here AdventOfCode/day8.livemd at main · Klohto/AdventOfCode · GitHub
here my solution
It is indeed getting trickier… not sure how much more I can keep up the pace. Kids get in the way
Did anyone else came to the realisation that each line can be processed individually, thus you can just pipe the input into Task.async_stream/5
and enjoy BEAM using more of those cores on your computer? Granted I realised that only in the evening when cleaning up my initial solution.
Anyhow here’s the code for those who are curious: aoc/day-08.exs at master · ramuuns/aoc · GitHub
Not sure how efficient it is, but I finally am modestly satisfied with my solution. My original was quite ugly.
Today I keep on practicing with the genetic algorithm on part 2. Preventing prematurity is still kind of hard.
solution
Day 8 was my favorite day by far, here is my solution: aoc/day-08.livemd at main · josevalim/aoc · GitHub
Ended up using a lot of pattern matching and charlists. Streaming here: Twitch (there is a discussion about bytes, charlists, and Unicode at the end).
A summary of Jose’s day 8 stream is up! It was interesting to see him more frustrated with this day compared to other days. Day 8 is just ridiculous - YouTube