this repo has no description
1<!--
2 vim:ft=markdown --!>
3 livebook:{"persist_outputs":true}
4-->
5
6<!-- livebook:{"persist_outputs":true} -->
7
8# Advent of Code 2021
9
10## Setup
11
12```elixir
13Mix.install([
14 {:nx, github: "elixir-nx/nx", sparse: "nx"},
15 {:kino, github: "livebook-dev/kino"}
16])
17```
18
19```output
20:ok
21```
22
23## Day 1
24
25### Load input
26
27```elixir
28stream =
29 File.stream!("day1.txt")
30 |> Stream.map(&String.to_integer(String.trim(&1)))
31```
32
33```output
34#Stream<[
35 enum: %File.Stream{
36 line_or_bytes: :line,
37 modes: [:raw, :read_ahead, :binary],
38 path: "day1.txt",
39 raw: true
40 },
41 funs: [#Function<47.58486609/1 in Stream.map/2>]
42]>
43```
44
45### Task 1
46
47<!-- livebook:{"break_markdown":true} -->
48
49Compute count of consecutive increases
50
51```elixir
52stream
53|> Stream.chunk_every(2, 1, :discard)
54|> Enum.count(fn [a, b] -> a < b end)
55```
56
57```output
581688
59```
60
61### Task 2
62
63<!-- livebook:{"break_markdown":true} -->
64
65Compute count of consecutive increases of sums of trigrams.
66
67However we can notice, that if we have list like:
68
69$$
70[a, b, c, d]
71$$
72
73Then when we want to compare consecutive trigrams then we compare:
74
75$$
76a + b + c < b + c + d \\
77a < d
78$$
79
80So we can traverse each 4 elements and then just compare first and last one
81instead of summing and then traversing it again.
82
83```elixir
84stream
85|> Stream.chunk_every(4, 1, :discard)
86|> Enum.count(fn [a, _, _, b] -> a < b end)
87```
88
89```output
901728
91```
92
93## Day 2
94
95### Load input
96
97We do parsing there, as it will help us with the latter tasks. Pattern matching
98is the simplest approach there, as input is in form of:
99
100```
101forward 10
102up 20
103down 30
104```
105
106We need to `trim/1` input to make sure that the last newline will not interrupt
107`String.to_integer/1` calls.
108
109```elixir
110stream =
111 File.stream!("day2.txt")
112 |> Stream.map(fn input ->
113 case String.trim(input) do
114 "forward " <> n -> {:forward, String.to_integer(n)}
115 "up " <> n -> {:up, String.to_integer(n)}
116 "down " <> n -> {:down, String.to_integer(n)}
117 end
118 end)
119```
120
121```output
122#Stream<[
123 enum: %File.Stream{
124 line_or_bytes: :line,
125 modes: [:raw, :read_ahead, :binary],
126 path: "day2.txt",
127 raw: true
128 },
129 funs: [#Function<47.58486609/1 in Stream.map/2>]
130]>
131```
132
133### Task 1
134
135```elixir
136{h, d} =
137 stream
138 |> Enum.reduce({0, 0}, fn
139 {:forward, n}, {h, d} -> {h + n, d}
140 {:up, n}, {h, d} -> {h, d - n}
141 {:down, n}, {h, d} -> {h, d + n}
142 end)
143
144h * d
145```
146
147```output
1481499229
149```
150
151### Task 2
152
153```elixir
154{h, d, _} =
155 stream
156 |> Enum.reduce({0, 0, 0}, fn
157 {:forward, n}, {h, d, a} -> {h + n, d + a * n, a}
158 {:up, n}, {h, d, a} -> {h, d, a - n}
159 {:down, n}, {h, d, a} -> {h, d, a + n}
160 end)
161
162h * d
163```
164
165```output
1661340836560
167```
168
169## Day 3
170
171### Input
172
173```elixir
174stream =
175 File.stream!("day3.txt")
176 |> Enum.map(&String.trim/1)
177 |> Enum.map(&String.to_charlist/1)
178
179defmodule Day3 do
180 def count(list) do
181 Enum.reduce(list, List.duplicate(0, 12), fn input, acc ->
182 for {value, counter} <- Enum.zip(input, acc) do
183 case value do
184 ?1 -> counter + 1
185 ?0 -> counter
186 end
187 end
188 end)
189 end
190end
191```
192
193```output
194{:module, Day3, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:count, 1}}
195```
196
197### Task 1
198
199```elixir
200half = div(length(stream), 2)
201
202{a, b} =
203 stream
204 |> Day3.count()
205 |> Enum.reduce({0, 0}, fn elem, {a, b} ->
206 if elem > half do
207 {a * 2 + 1, b * 2}
208 else
209 {a * 2, b * 2 + 1}
210 end
211 end)
212
213a * b
214```
215
216```output
2173847100
218```
219
220### Task 2
221
222```elixir
223defmodule Day3.Task2 do
224 def reduce(list, cb), do: reduce(list, 0, cb)
225
226 defp reduce([elem], _, _), do: elem
227
228 defp reduce(list, at, cb) do
229 counts = Day3.count(list)
230
231 half = div(length(list), 2)
232 count = Enum.at(counts, at)
233
234 bit =
235 cond do
236 count == half and cb.(count + 1, half) -> ?1
237 count != half and cb.(count, half) -> ?1
238 true -> ?0
239 end
240
241 reduce(Enum.filter(list, &(Enum.at(&1, at) == bit)), at + 1, cb)
242 end
243end
244
245co2 = List.to_integer(Day3.Task2.reduce(stream, &</2), 2)
246o2 = List.to_integer(Day3.Task2.reduce(stream, &>/2), 2)
247
248co2 * o2
249```
250
251```output
2524105235
253```
254
255## Day 4
256
257### Input
258
259This time it is a little bit more convoluted, as there are 2 parts of the input.
260Fortunately we can easily disect the parts via pattern matching.
261
262Technically the conversion to the numbers is not needed, but it does no harm
263and provides additional layer of safety against some whitespace characters left there
264and here.
265
266The `Day4.win/2` function is manually unrolled, as it is easier to write than some
267random jumping in the list.
268
269<!-- livebook:{"disable_formatting":true} -->
270
271```elixir
272[numbers | bingos] =
273 File.read!("day4.txt")
274 |> String.split("\n\n", trim: true)
275
276numbers =
277 numbers
278 |> String.trim()
279 |> String.split(",")
280 |> Enum.map(&String.to_integer/1)
281
282bingos =
283 bingos
284 |> Enum.map(fn bingo ->
285 bingo
286 |> String.split(~r/\s+/, trim: true)
287 |> Enum.map(&String.to_integer/1)
288 end)
289
290defmodule Day4 do
291 def win(
292 [
293 a1, a2, a3, a4, a5,
294 b1, b2, b3, b4, b5,
295 c1, c2, c3, c4, c5,
296 d1, d2, d3, d4, d5,
297 e1, e2, e3, e4, e5
298 ],
299 nums
300 ) do
301 # Rows
302 all_in([a1, a2, a3, a4, a5], nums) or
303 all_in([b1, b3, b3, b4, b5], nums) or
304 all_in([c1, c2, c3, c4, c5], nums) or
305 all_in([d1, d2, d3, d4, d5], nums) or
306 all_in([e1, e2, e3, e4, e5], nums) or
307 # Columns
308 all_in([a1, b1, c1, d1, e1], nums) or
309 all_in([a2, b2, c2, d2, e2], nums) or
310 all_in([a3, b3, c3, d3, e3], nums) or
311 all_in([a4, b4, c4, d4, e4], nums) or
312 all_in([a5, b5, c5, d5, e5], nums)
313 end
314
315 def not_matched(bingo, nums) do
316 Enum.reject(bingo, &(&1 in nums))
317 end
318
319 defp all_in(list, nums) do
320 Enum.all?(list, &(&1 in nums))
321 end
322end
323```
324
325```output
326{:module, Day4, <<70, 79, 82, 49, 0, 0, 15, ...>>, {:all_in, 2}}
327```
328
329### Task 1
330
331We simply traverse the `numbers` list aggregating the numbers (order doesn't really matter,
332here we aggregate them in reverse order to speedup the code). When we have enough numbers
333that any of the `bingos` is winning one, then we halt the reduction and return computed
334result.
335
336```elixir
337numbers
338|> Enum.reduce_while([], fn elem, acc ->
339 matches = [elem | acc]
340
341 case Enum.find(bingos, &Day4.win(&1, matches)) do
342 nil -> {:cont, matches}
343 bingo -> {:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem}
344 end
345end)
346```
347
348```output
34934506
350```
351
352### Task 2
353
354```elixir
355numbers
356|> Enum.reduce_while({bingos, []}, fn elem, {bingos, acc} ->
357 matches = [elem | acc]
358
359 case bingos do
360 [bingo] ->
361 if Day4.win(bingo, matches) do
362 {:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem}
363 else
364 {:cont, {bingos, matches}}
365 end
366
367 _ ->
368 {:cont, {Enum.reject(bingos, &Day4.win(&1, matches)), matches}}
369 end
370end)
371```
372
373```output
3747686
375```
376
377## Day 5
378
379```elixir
380defmodule Day5 do
381 defmodule Point do
382 defstruct [:x, :y]
383
384 def parse(input) do
385 [x, y] = String.split(input, ",")
386
387 %__MODULE__{x: String.to_integer(x), y: String.to_integer(y)}
388 end
389 end
390
391 defmodule Line do
392 defstruct [:start, :finish]
393
394 def new(a, b) do
395 {start, finish} =
396 cond do
397 a.x < b.x -> {a, b}
398 a.y < b.y -> {a, b}
399 true -> {b, a}
400 end
401
402 %__MODULE__{start: start, finish: finish}
403 end
404
405 def horizontal?(a), do: a.start.y == a.finish.y
406 def vertical?(a), do: a.start.x == a.finish.x
407
408 def points(a) do
409 case {sign(a.finish.x - a.start.x), sign(a.finish.y - a.start.y)} do
410 {0, dy} -> for y <- a.start.y..a.finish.y//dy, do: {a.start.x, y}
411 {dx, 0} -> for x <- a.start.x..a.finish.x//dx, do: {x, a.start.y}
412 {dx, dy} -> Enum.zip(a.start.x..a.finish.x//dx, a.start.y..a.finish.y//dy)
413 end
414 end
415
416 def orientation(a) do
417 cond do
418 horizontal?(a) -> :horizontal
419 vertical?(a) -> :vertical
420 true -> :diagonal
421 end
422 end
423
424 defp sign(0), do: 0
425 defp sign(x) when x < 0, do: -1
426 defp sign(x) when x > 0, do: 1
427 end
428end
429
430lines =
431 File.stream!("day5.txt")
432 |> Stream.map(&String.trim/1)
433 |> Stream.map(fn input ->
434 [a, b] = String.split(input, " -> ")
435
436 pa = Day5.Point.parse(a)
437 pb = Day5.Point.parse(b)
438
439 Day5.Line.new(pa, pb)
440 end)
441```
442
443```output
444#Stream<[
445 enum: %File.Stream{
446 line_or_bytes: :line,
447 modes: [:raw, :read_ahead, :binary],
448 path: "day5.txt",
449 raw: true
450 },
451 funs: [#Function<47.58486609/1 in Stream.map/2>, #Function<47.58486609/1 in Stream.map/2>]
452]>
453```
454
455### Task 1
456
457```elixir
458lines
459|> Stream.filter(&(Day5.Line.orientation(&1) != :diagonal))
460|> Stream.flat_map(&Day5.Line.points/1)
461|> Enum.frequencies()
462|> Enum.count(fn {_k, v} -> v > 1 end)
463```
464
465```output
4665197
467```
468
469### Task 2
470
471```elixir
472lines
473|> Stream.flat_map(&Day5.Line.points/1)
474|> Enum.frequencies()
475|> Enum.count(fn {_k, v} -> v > 1 end)
476```
477
478```output
47918605
480```
481
482## Day 6
483
484```elixir
485initial = for i <- 0..8, into: %{}, do: {i, 0}
486
487counts =
488 File.read!("day6.txt")
489 |> String.trim()
490 |> String.split(",")
491 |> Enum.map(&String.to_integer/1)
492 |> Enum.frequencies()
493 |> Map.merge(initial, fn _, a, _ -> a end)
494
495defmodule Day6 do
496 def next(%{0 => next} = population) do
497 1..8
498 |> Map.new(&{&1 - 1, population[&1]})
499 |> Map.merge(%{6 => next, 8 => next}, fn _, v1, v2 -> v1 + v2 end)
500 end
501end
502```
503
504```output
505{:module, Day6, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:next, 1}}
506```
507
508### Task 1
509
510```elixir
5111..80
512|> Enum.reduce(counts, fn _, acc -> Day6.next(acc) end)
513|> Map.values()
514|> Enum.sum()
515```
516
517```output
518343441
519```
520
521### Task 2
522
523```elixir
5241..256
525|> Enum.reduce(counts, fn _, acc -> Day6.next(acc) end)
526|> Map.values()
527|> Enum.sum()
528```
529
530```output
5311569108373832
532```
533
534## Day 7
535
536```elixir
537input =
538 File.read!("day7.txt")
539 |> String.trim()
540 |> String.split(",")
541 |> Enum.map(&String.to_integer/1)
542```
543
544```output
545[1101, 1, 29, 67, 1102, 0, 1, 65, 1008, 65, 35, 66, 1005, 66, 28, 1, 67, 65, 20, 4, 0, 1001, 65, 1,
546 65, 1106, 0, 8, 99, 35, 67, 101, 99, 105, 32, 110, 39, 101, 115, 116, 32, 112, 97, 115, 32, 117,
547 110, 101, 32, 105, ...]
548```
549
550### Task 1
551
552```elixir
553median = Enum.at(Enum.sort(input), div(length(input), 2))
554
555input
556|> Enum.map(&abs(&1 - median))
557|> Enum.sum()
558```
559
560```output
561336721
562```
563
564### Task 2
565
566```elixir
567arith_sum = fn n -> div(n * n + n, 2) end
568
569max = Enum.max(input)
570
571mean = Enum.sum(input) / length(input)
572
573[floor(mean), ceil(mean)]
574|> Enum.map(fn n ->
575 input
576 |> Enum.map(&arith_sum.(abs(&1 - n)))
577 |> Enum.sum()
578end)
579|> Enum.min()
580```
581
582```output
58391638945
584```
585
586## Day 8
587
588```elixir
589input =
590 File.stream!("day8.txt")
591 |> Stream.map(fn line ->
592 line
593 |> String.split(" | ")
594 |> Enum.map(fn part ->
595 part
596 |> String.trim()
597 |> String.split(" ")
598 |> Enum.map(fn disp ->
599 # Sort characters in each entry to simplify later work
600 disp
601 |> String.to_charlist()
602 |> Enum.sort()
603 |> List.to_string()
604 end)
605 end)
606 |> List.to_tuple()
607 end)
608```
609
610```output
611#Stream<[
612 enum: %File.Stream{
613 line_or_bytes: :line,
614 modes: [:raw, :read_ahead, :binary],
615 path: "day8.txt",
616 raw: true
617 },
618 funs: [#Function<47.58486609/1 in Stream.map/2>]
619]>
620```
621
622### Task 1
623
624We simply need to count all occurences of the values that have 2, 3, 4, or 7 highlighted
625segments.
626
627```elixir
628input
629|> Enum.map(fn {_, output} ->
630 Enum.count(output, &(byte_size(&1) in [2, 3, 4, 7]))
631end)
632|> Enum.sum()
633```
634
635```output
636390
637```
638
639### Task 2
640
641```elixir
642defmodule Day8.Task2 do
643 defp a --- b, do: MapSet.difference(a, b)
644
645 defp a +++ b, do: MapSet.union(a, b)
646
647 defp a <~> b, do: MapSet.intersection(a, b)
648
649 # 1. 7. 4. 2|3|5. 2|3|5. 2|3|5. 6|9|0. 6|9|0. 6|9|0. 8.
650 def deduce([cf, acf, bcdf, acdeg, acdfg, abdfg, abdefg, abcdfg, abcefg, abcdefg]) do
651 eg = abcdefg --- (acf +++ bcdf)
652 bd = bcdf --- cf
653 adg = acdeg <~> acdfg <~> abdfg
654 abfg = abdefg <~> abcdfg <~> abcefg
655 a = acf --- cf
656 b = abfg <~> bd
657 f = abfg <~> cf
658 g = adg <~> eg
659 d = adg <~> bd
660 c = cf --- f
661 e = eg --- g
662
663 [a, b, c, d, e, f, g] =
664 [a, b, c, d, e, f, g]
665 |> Enum.map(&extract/1)
666
667 [
668 # 0
669 [a, b, c, e, f, g],
670 # 1
671 [c, f],
672 # 2
673 [a, c, d, e, g],
674 # 3
675 [a, c, d, f, g],
676 # 4
677 [b, c, d, f],
678 # 5
679 [a, b, d, f, g],
680 # 6
681 [a, b, d, e, f, g],
682 # 7
683 [a, c, f],
684 # 8
685 [a, b, c, d, e, f, g],
686 # 9
687 [a, b, c, d, f, g]
688 ]
689 |> Enum.map(&List.to_string(Enum.sort(&1)))
690 |> Enum.with_index()
691 |> Map.new()
692 end
693
694 defp extract(a) do
695 # Just additional sanity check
696 [v] = MapSet.to_list(a)
697
698 v
699 end
700
701 def decode(matches, output) do
702 output
703 |> Enum.map(&matches[&1])
704 |> Integer.undigits()
705 end
706end
707
708input
709|> Enum.map(fn {input, output} ->
710 input
711 |> Enum.sort_by(&byte_size/1)
712 |> Enum.map(&MapSet.new(String.to_charlist(&1)))
713 |> Day8.Task2.deduce()
714 |> Day8.Task2.decode(output)
715end)
716|> Enum.sum()
717```
718
719```output
7201011785
721```
722
723## Day 9
724
725```elixir
726input =
727 File.read!("day9.txt")
728 |> String.split("\n", trim: true)
729 |> Enum.map(&String.to_charlist(String.trim(&1)))
730 |> Nx.tensor(names: [:y, :x])
731 |> Nx.subtract(?0)
732 |> Nx.add(1)
733
734{width, height} = shape = Nx.shape(input)
735```
736
737```output
738{100, 100}
739```
740
741### Task 1
742
743```elixir
744minima = fn padded, size, axis ->
745 shifted = Nx.slice_axis(padded, 0, size, axis)
746 x1 = Nx.less(input, shifted)
747
748 shifted = Nx.slice_axis(padded, 2, size, axis)
749 x2 = Nx.less(input, shifted)
750
751 Nx.logical_and(x1, x2)
752end
753
754padded = Nx.pad(input, 99, [{0, 0, 0}, {1, 1, 0}])
755
756x = minima.(padded, width, :x)
757
758padded = Nx.pad(input, 99, [{1, 1, 0}, {0, 0, 0}])
759
760y = minima.(padded, height, :y)
761
762minimas = Nx.logical_and(x, y)
763
764input
765|> Nx.multiply(minimas)
766|> Nx.sum()
767|> Nx.to_number()
768```
769
770```output
771452
772```
773
774### Task 2
775
776```elixir
777input
778|> Nx.equal(10)
779|> Nx.logical_not()
780|> Nx.select(Nx.iota(shape), 9999)
781|> Nx.to_flat_list()
782|> Enum.reject(&(&1 == 9999))
783|> Enum.map(fn point -> {div(point, width), rem(point, width)} end)
784|> Enum.reduce([], fn {y, x} = point, basins ->
785 basin_left = Enum.find_index(basins, &({y, x - 1} in &1))
786 basin_up = Enum.find_index(basins, &({y - 1, x} in &1))
787
788 case {basin_left, basin_up} do
789 {nil, nil} ->
790 [MapSet.new([point]) | basins]
791
792 {idx, nil} ->
793 List.update_at(basins, idx, &MapSet.put(&1, point))
794
795 {nil, idx} ->
796 List.update_at(basins, idx, &MapSet.put(&1, point))
797
798 {idx, idx} ->
799 List.update_at(basins, idx, &MapSet.put(&1, point))
800
801 {idx1, idx2} ->
802 {old, basins} = List.pop_at(basins, max(idx1, idx2))
803
804 List.update_at(basins, min(idx1, idx2), &(&1 |> MapSet.union(old) |> MapSet.put(point)))
805 end
806end)
807|> Enum.map(&MapSet.size/1)
808|> Enum.sort(:desc)
809|> Enum.take(3)
810|> Enum.reduce(&*/2)
811```
812
813```output
8141263735
815```
816
817## Day 10
818
819```elixir
820input =
821 File.stream!("day10.txt")
822 |> Stream.map(&String.trim/1)
823
824defmodule Day10 do
825 @parens %{?( => ?), ?[ => ?], ?< => ?>, ?{ => ?}}
826
827 def parse(input), do: parse(input, [])
828
829 defp parse(<<c>> <> rest, stack) when c in '([{<', do: parse(rest, [@parens[c] | stack])
830 defp parse(<<c>> <> rest, [c | stack]), do: parse(rest, stack)
831 defp parse(<<>>, []), do: :ok
832 defp parse(<<>>, rest), do: {:incomplete, rest}
833 defp parse(<<c>> <> _, _), do: {:unexpected, [c]}
834end
835```
836
837```output
838{:module, Day10, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:parse, 2}}
839```
840
841### Task 1
842
843```elixir
844points = %{
845 ?) => 3,
846 ?] => 57,
847 ?} => 1197,
848 ?> => 25137
849}
850
851input
852|> Enum.map(&Day10.parse/1)
853|> Enum.map(fn
854 {:unexpected, [c]} -> points[c]
855 _ -> 0
856end)
857|> Enum.sum()
858```
859
860```output
861288291
862```
863
864### Task 2
865
866```elixir
867points = %{
868 ?) => 1,
869 ?] => 2,
870 ?} => 3,
871 ?> => 4
872}
873
874median = fn list ->
875 sorted = Enum.sort(list)
876 middle = div(length(list), 2)
877
878 Enum.at(sorted, middle)
879end
880
881input
882|> Enum.map(&Day10.parse/1)
883|> Enum.flat_map(fn
884 {:incomplete, rest} ->
885 [
886 Enum.reduce(rest, 0, fn c, acc ->
887 acc * 5 + points[c]
888 end)
889 ]
890
891 _ ->
892 []
893end)
894|> median.()
895```
896
897```output
898820045242
899```
900
901## Day 11
902
903```elixir
904input =
905 File.read!("day11.txt")
906 |> String.split("\n")
907 |> Enum.map(&String.trim/1)
908 |> Enum.map(fn line ->
909 for <<c <- line>>, do: c - ?0
910 end)
911 |> Enum.with_index()
912 |> Enum.flat_map(fn {row, y} ->
913 for {v, x} <- Enum.with_index(row), do: {{x, y}, v}
914 end)
915 |> Map.new()
916
917defmodule Day11 do
918 def step(map) do
919 updated = Map.new(map, fn {k, v} -> {k, v + 1} end)
920
921 lightup(updated, 0)
922 end
923
924 @diffs for dx <- -1..1, dy <- -1..1, dx != 0 or dy != 0, do: {dx, dy}
925
926 def lightup(map, n) do
927 map
928 |> Enum.reduce({map, 0}, fn
929 {_, v}, acc when v < 10 ->
930 acc
931
932 {{x, y} = k, _}, {map, count} ->
933 new_map =
934 @diffs
935 |> Enum.reduce(map, fn {dx, dy}, acc ->
936 point = {x + dx, y + dy}
937
938 case Map.fetch(acc, point) do
939 {:ok, value} when value != 0 -> %{acc | point => value + 1}
940 _ -> acc
941 end
942 end)
943 |> Map.put(k, 0)
944
945 {new_map, count + 1}
946 end)
947 |> case do
948 {map, 0} -> {map, n}
949 {map, m} -> lightup(map, n + m)
950 end
951 end
952end
953```
954
955```output
956{:module, Day11, <<70, 79, 82, 49, 0, 0, 11, ...>>, {:lightup, 2}}
957```
958
959### Task 1
960
961```elixir
962draw = fn map ->
963 for x <- 0..9 do
964 IO.puts(for y <- 0..9, do: ?0 + map[{x, y}])
965 end
966
967 IO.puts("")
968end
969
9701..100
971|> Enum.reduce({input, 0}, fn _, {map, n} ->
972 {new_map, m} = Day11.step(map)
973 {new_map, n + m}
974end)
975|> elem(1)
976```
977
978```output
9791688
980```
981
982### Task 2
983
984```elixir
985Stream.unfold(1, &{&1, &1 + 1})
986|> Enum.reduce_while(input, fn idx, map ->
987 case Day11.step(map) do
988 {_, 100} -> {:halt, idx}
989 {next, _} -> {:cont, next}
990 end
991end)
992```
993
994```output
995403
996```
997
998## Day 12
999
1000```elixir
1001input =
1002 File.read!("day12.txt")
1003 |> String.split("\n")
1004 |> Enum.map(&String.split(&1, "-"))
1005
1006graph =
1007 Enum.reduce(input, %{}, fn [a, b], acc ->
1008 acc
1009 |> Map.update(a, [b], &[b | &1])
1010 |> Map.update(b, [a], &[a | &1])
1011 end)
1012
1013defmodule Day12 do
1014 def dfs(graph, start, finish), do: dfs(graph, start, finish, [start])
1015
1016 defp dfs(_graph, vertex, vertex, _visited), do: 1
1017
1018 defp dfs(graph, vertex, finish, visited) do
1019 (graph[vertex] -- visited)
1020 |> Enum.reduce(0, fn vertex, acc ->
1021 visited = if small?(vertex), do: [vertex | visited], else: visited
1022
1023 acc + dfs(graph, vertex, finish, visited)
1024 end)
1025 end
1026
1027 def dfs2(graph, start, finish), do: dfs2(graph, start, finish, %{start => :inf})
1028
1029 defp dfs2(_graph, vertex, vertex, _visited), do: 1
1030
1031 defp dfs2(graph, vertex, finish, visited) do
1032 (graph[vertex] -- keys(visited))
1033 |> Enum.reduce(0, fn vertex, acc ->
1034 visited = if small?(vertex), do: Map.update(visited, vertex, 1, &(&1 + 1)), else: visited
1035
1036 acc + dfs2(graph, vertex, finish, visited)
1037 end)
1038 end
1039
1040 defp keys(map) do
1041 if Enum.any?(map, fn {_, v} -> v == 2 end) do
1042 # there is already some vertex visited twice
1043 Map.keys(map)
1044 else
1045 for {k, v} <- map, v > 1, do: k
1046 end
1047 end
1048
1049 defp small?(<<c>> <> _), do: c in ?a..?z
1050end
1051```
1052
1053```output
1054{:module, Day12, <<70, 79, 82, 49, 0, 0, 15, ...>>, {:small?, 1}}
1055```
1056
1057### Task 1
1058
1059```elixir
1060Day12.dfs(graph, "start", "end")
1061```
1062
1063```output
10644167
1065```
1066
1067### Task 2
1068
1069```elixir
1070Day12.dfs2(graph, "start", "end")
1071```
1072
1073```output
107498441
1075```
1076
1077## Day 13
1078
1079```elixir
1080[input, folds] =
1081 File.read!("day13.txt")
1082 |> String.trim()
1083 |> String.split("\n\n")
1084
1085input =
1086 input
1087 |> String.split("\n")
1088 |> Enum.map(fn line ->
1089 [x, y] = String.split(line, ",")
1090
1091 {String.to_integer(x), String.to_integer(y)}
1092 end)
1093 |> MapSet.new()
1094
1095folds =
1096 folds
1097 |> String.split("\n")
1098 |> Enum.map(fn
1099 "fold along " <> <<c>> <> "=" <> rest ->
1100 {String.to_atom(<<c>>), String.to_integer(rest)}
1101 end)
1102
1103defmodule Day13 do
1104 def fold({orientation, pos}, set) do
1105 Enum.reduce(set, MapSet.new(), fn point, acc ->
1106 new_point = folded_coords(orientation, pos, point)
1107
1108 MapSet.put(acc, new_point)
1109 end)
1110 end
1111
1112 defp folded_coords(:x, col, {x, y}) when x > col, do: {abs(2 * col - x), y}
1113 defp folded_coords(:y, row, {x, y}) when y > row, do: {x, abs(2 * row - y)}
1114 defp folded_coords(_, _, point), do: point
1115
1116 def draw(set) do
1117 set
1118 |> Enum.group_by(&elem(&1, 1))
1119 |> Enum.sort()
1120 |> Enum.map(fn {_, points} ->
1121 points
1122 |> Enum.map(&elem(&1, 0))
1123 |> Enum.sort()
1124 |> Enum.chunk_every(2, 1)
1125 |> Enum.map(fn
1126 [a, b] -> b - a
1127 _ -> 0
1128 end)
1129 |> Enum.map(&String.pad_trailing("█", &1, " "))
1130 end)
1131 |> Enum.join("\n")
1132 end
1133end
1134```
1135
1136```output
1137{:module, Day13, <<70, 79, 82, 49, 0, 0, 13, ...>>, {:draw, 1}}
1138```
1139
1140### Task 1
1141
1142```elixir
1143Day13.fold(hd(folds), input) |> MapSet.size()
1144```
1145
1146```output
1147802
1148```
1149
1150### Task 2
1151
1152```elixir
1153Enum.reduce(folds, input, &Day13.fold/2)
1154|> Day13.draw()
1155|> IO.puts()
1156```
1157
1158```output
1159███ █ █ █ █ ████ ████ ██ █ █ ███
1160█ █ █ █ █ █ █ █ █ █ █ █ █ █
1161█ █ ██ ████ ███ █ █ █ █ ███
1162███ █ █ █ █ █ █ █ ██ █ █ █ █
1163█ █ █ █ █ █ █ █ █ █ █ █ █ █
1164█ █ █ █ █ █ █ ████ ███ ██ ███
1165```
1166
1167```output
1168:ok
1169```
1170
1171## Day 14
1172
1173```elixir
1174[polymer_raw, subs] =
1175 File.read!("day14.txt")
1176 |> String.split("\n\n")
1177
1178<<first, _::binary>> = polymer_raw
1179
1180polymer =
1181 {first,
1182 polymer_raw
1183 |> String.to_charlist()
1184 |> Enum.chunk_every(2, 1, :discard)
1185 |> Enum.frequencies()}
1186
1187subs =
1188 subs
1189 |> String.trim()
1190 |> String.split(["\n", " -> "])
1191 |> Enum.chunk_every(2)
1192 |> Map.new(fn [pair, <<new>>] -> {String.to_charlist(pair), new} end)
1193
1194defmodule Day14 do
1195 def expand({hd, polymer}, subs) do
1196 new =
1197 polymer
1198 |> Enum.reduce(%{}, fn {[a, b] = pair, count}, acc ->
1199 s = Map.fetch!(subs, pair)
1200
1201 acc
1202 |> Map.update([a, s], count, &(&1 + count))
1203 |> Map.update([s, b], count, &(&1 + count))
1204 end)
1205
1206 {hd, new}
1207 end
1208
1209 def expand_naive(polymer, subs) do
1210 polymer
1211 |> to_charlist()
1212 |> Enum.chunk_every(2, 1, :discard)
1213 |> Enum.flat_map(fn [a, b] = pair ->
1214 [a, subs[pair], b]
1215 end)
1216 |> List.to_string()
1217 end
1218
1219 def frequencies({hd, polymer}) do
1220 polymer
1221 |> Enum.reduce(%{hd => 1}, fn {[_, b], count}, acc ->
1222 Map.update(acc, b, count, &(&1 + count))
1223 end)
1224 end
1225end
1226```
1227
1228```output
1229{:module, Day14, <<70, 79, 82, 49, 0, 0, 13, ...>>, {:frequencies, 1}}
1230```
1231
1232### Task 1
1233
1234```elixir
1235{{_, min}, {_, max}} =
1236 1..10
1237 |> Enum.reduce(polymer, fn _, acc ->
1238 Day14.expand(acc, subs)
1239 end)
1240 |> Day14.frequencies()
1241 |> Enum.min_max_by(&elem(&1, 1))
1242
1243# 2768
1244max - min
1245```
1246
1247```output
12482768
1249```
1250
1251### Task 2
1252
1253```elixir
1254{{_, min}, {_, max}} =
1255 1..40
1256 |> Enum.reduce(polymer, fn _, acc ->
1257 Day14.expand(acc, subs)
1258 end)
1259 |> Day14.frequencies()
1260 |> Enum.min_max_by(&elem(&1, 1))
1261
1262max - min
1263```
1264
1265```output
12662914365137499
1267```
1268
1269## Day 15
1270
1271```elixir
1272input =
1273 File.read!("day15.txt")
1274 |> String.trim()
1275 |> String.split("\n")
1276 |> Enum.map(&String.to_charlist/1)
1277 |> Enum.with_index()
1278 |> Enum.flat_map(fn {row, y} ->
1279 row
1280 |> Enum.with_index()
1281 |> Enum.map(fn {v, x} -> {{x, y}, v - ?0} end)
1282 end)
1283 |> Map.new()
1284
1285{width, height} = Enum.max(Map.keys(input))
1286```
1287
1288```output
1289{99, 99}
1290```
1291
1292### Task 1
1293
1294```elixir
1295shortest_paths =
1296 for y <- height..0//-1,
1297 x <- width..0//-1,
1298 reduce: %{} do
1299 acc ->
1300 right = acc[{x + 1, y}]
1301 bottom = acc[{x, y + 1}]
1302
1303 value =
1304 case {right, bottom} do
1305 {nil, nil} -> input[{x, y}]
1306 _ -> input[{x, y}] + min(right, bottom)
1307 end
1308
1309 Map.put(acc, {x, y}, value)
1310 end
1311
1312shortest_paths[{0, 0}] - input[{0, 0}]
1313```
1314
1315```output
1316429
1317```
1318
1319### Task 2
1320
1321```elixir
1322defmodule Day15.Task2 do
1323 def expand_grid(board) do
1324 {width, height} = Enum.max(Map.keys(board))
1325
1326 board
1327 |> Enum.flat_map(fn {{x, y}, v} ->
1328 for rx <- 0..4, ry <- 0..4 do
1329 {{x + (width + 1) * rx, y + (height + 1) * ry}, rem(v - 1 + rx + ry, 9) + 1}
1330 end
1331 end)
1332 |> Map.new()
1333 end
1334
1335 def find_path(board, start, finish) do
1336 dists = :gb_sets.singleton({0, start})
1337
1338 find_path(board, finish, dists, MapSet.new())
1339 end
1340
1341 @surround for dx <- -1..1, dy <- -1..1, abs(dx) != abs(dy), do: {dx, dy}
1342
1343 def find_path(board, finish, dists, visited) do
1344 {{dist, {x, y} = curr}, dists} = :gb_sets.take_smallest(dists)
1345
1346 if curr == finish do
1347 dist
1348 else
1349 visited = MapSet.put(visited, curr)
1350
1351 dists =
1352 for {dx, dy} <- @surround,
1353 next = {x + dx, y + dy},
1354 next not in visited,
1355 is_map_key(board, next),
1356 alt = dist + board[next],
1357 reduce: dists do
1358 acc ->
1359 :gb_sets.add_element({alt, next}, acc)
1360 end
1361
1362 find_path(board, finish, dists, visited)
1363 end
1364 end
1365end
1366```
1367
1368```output
1369{:module, Day15.Task2, <<70, 79, 82, 49, 0, 0, 16, ...>>, {:find_path, 4}}
1370```
1371
1372```elixir
1373input
1374|> Day15.Task2.expand_grid()
1375|> Day15.Task2.find_path({0, 0}, {499, 499})
1376```
1377
1378```output
13792844
1380```
1381
1382## Day 16
1383
1384```elixir
1385defmodule Day16 do
1386 defmodule Packet do
1387 defstruct [:version, :type, :value]
1388 end
1389
1390 def decode(<<version::3, 4::3, rest::bitstring>>) do
1391 {value, rest} = literal(rest, 0)
1392
1393 {%Packet{type: 4, version: version, value: value}, rest}
1394 end
1395
1396 def decode(<<version::3, type::3, 0::1, length::15, rest::bitstring>>) do
1397 <<subpackets::bitstring-size(length), rest::bitstring>> = rest
1398
1399 {%Packet{type: type, version: version, value: decode_all(subpackets)}, rest}
1400 end
1401
1402 def decode(<<version::3, type::3, 1::1, length::11, rest::bitstring>>) do
1403 {value, rest} = Enum.map_reduce(1..length, rest, fn _, acc -> decode(acc) end)
1404
1405 {%Packet{type: type, version: version, value: value}, rest}
1406 end
1407
1408 def decode_all(input) do
1409 case decode(input) do
1410 {packet, <<>>} -> [packet]
1411 {packet, rest} -> [packet | decode_all(rest)]
1412 end
1413 end
1414
1415 defp literal(<<1::1, bits::4, rest::bitstring>>, acc) do
1416 literal(rest, acc * 0x10 + bits)
1417 end
1418
1419 defp literal(<<0::1, bits::4, rest::bitstring>>, acc) do
1420 {acc * 0x10 + bits, rest}
1421 end
1422end
1423
1424input =
1425 File.read!("day16.txt")
1426 |> String.trim()
1427 |> Base.decode16!()
1428 |> Day16.decode()
1429 |> elem(0)
1430```
1431
1432```output
1433%Day16.Packet{
1434 type: 0,
1435 value: [
1436 %Day16.Packet{
1437 type: 1,
1438 value: [
1439 %Day16.Packet{type: 4, value: 20, version: 6},
1440 %Day16.Packet{
1441 type: 6,
1442 value: [
1443 %Day16.Packet{type: 4, value: 14747, version: 1},
1444 %Day16.Packet{type: 4, value: 14747, version: 6}
1445 ],
1446 version: 2
1447 }
1448 ],
1449 version: 1
1450 },
1451 %Day16.Packet{
1452 type: 3,
1453 value: [
1454 %Day16.Packet{type: 4, value: 15, version: 5},
1455 %Day16.Packet{type: 4, value: 10, version: 6}
1456 ],
1457 version: 7
1458 },
1459 %Day16.Packet{
1460 type: 1,
1461 value: [
1462 %Day16.Packet{
1463 type: 7,
1464 value: [
1465 %Day16.Packet{type: 4, value: 2184, version: 1},
1466 %Day16.Packet{type: 4, value: 130250, version: 6}
1467 ],
1468 version: 6
1469 },
1470 %Day16.Packet{type: 4, value: 5442981, version: 4}
1471 ],
1472 version: 6
1473 },
1474 %Day16.Packet{type: 4, value: 8281083, version: 0},
1475 %Day16.Packet{
1476 type: 2,
1477 value: [
1478 %Day16.Packet{type: 4, value: 102, version: 5},
1479 %Day16.Packet{type: 4, value: 647125, version: 7}
1480 ],
1481 version: 1
1482 },
1483 %Day16.Packet{
1484 type: 1,
1485 value: [
1486 %Day16.Packet{type: 4, value: 178, version: 1},
1487 %Day16.Packet{type: 4, value: 176, version: 6}
1488 ],
1489 version: 0
1490 },
1491 %Day16.Packet{
1492 type: 1,
1493 value: [
1494 %Day16.Packet{
1495 type: 6,
1496 value: [
1497 %Day16.Packet{
1498 type: 0,
1499 value: [
1500 %Day16.Packet{type: 4, value: 13, version: 1},
1501 %Day16.Packet{type: 4, value: 8, version: 4},
1502 %Day16.Packet{type: 4, value: 4, version: 3}
1503 ],
1504 version: 2
1505 },
1506 %Day16.Packet{
1507 type: 0,
1508 value: [
1509 %Day16.Packet{type: 4, value: 7, version: 7},
1510 %Day16.Packet{type: 4, value: 11, version: 3},
1511 %Day16.Packet{type: 4, value: 14, version: 2}
1512 ],
1513 version: 4
1514 }
1515 ],
1516 version: 7
1517 },
1518 %Day16.Packet{type: 4, value: 2724, version: 0}
1519 ],
1520 version: 1
1521 },
1522 %Day16.Packet{type: 4, value: 9, version: 4},
1523 %Day16.Packet{
1524 type: 1,
1525 value: [
1526 %Day16.Packet{
1527 type: 5,
1528 value: [
1529 %Day16.Packet{type: 4, value: 7240238, version: 2},
1530 %Day16.Packet{type: 4, value: 233, version: 7}
1531 ],
1532 version: 1
1533 },
1534 %Day16.Packet{type: 4, value: 37, version: 6}
1535 ],
1536 version: 4
1537 },
1538 %Day16.Packet{type: 2, value: [%Day16.Packet{type: 4, value: 2, version: 5}], version: 5},
1539 %Day16.Packet{type: 4, value: 53749, version: 4},
1540 %Day16.Packet{type: 4, value: 11, version: 3},
1541 %Day16.Packet{
1542 type: 1,
1543 value: [
1544 %Day16.Packet{type: 4, value: 382979, version: 4},
1545 %Day16.Packet{
1546 type: 5,
1547 value: [
1548 %Day16.Packet{
1549 type: 0,
1550 value: [
1551 %Day16.Packet{type: 4, value: 15, version: 1},
1552 %Day16.Packet{type: 4, value: 10, version: 0},
1553 %Day16.Packet{type: 4, value: 2, version: 6}
1554 ],
1555 version: 5
1556 },
1557 %Day16.Packet{
1558 type: 0,
1559 value: [
1560 %Day16.Packet{type: 4, value: 4, version: 7},
1561 %Day16.Packet{type: 4, value: 7, version: 4},
1562 %Day16.Packet{type: 4, value: 2, version: 5}
1563 ],
1564 version: 1
1565 }
1566 ],
1567 version: 6
1568 }
1569 ],
1570 version: 2
1571 },
1572 %Day16.Packet{type: 4, value: 21251, version: 1},
1573 %Day16.Packet{
1574 type: 1,
1575 value: [
1576 %Day16.Packet{type: 4, value: 163, version: 6},
1577 %Day16.Packet{
1578 type: 5,
1579 value: [
1580 %Day16.Packet{type: 4, value: 59, version: 3},
1581 %Day16.Packet{type: 4, value: 836848134220, version: 1}
1582 ],
1583 version: 6
1584 }
1585 ],
1586 version: 2
1587 },
1588 %Day16.Packet{
1589 type: 2,
1590 value: [
1591 %Day16.Packet{
1592 type: 0,
1593 value: [
1594 %Day16.Packet{
1595 type: 0,
1596 value: [
1597 %Day16.Packet{
1598 type: 2,
1599 value: [
1600 %Day16.Packet{
1601 type: 2,
1602 value: [
1603 %Day16.Packet{
1604 type: 0,
1605 value: [
1606 %Day16.Packet{
1607 type: 3,
1608 value: [
1609 %Day16.Packet{
1610 type: 2,
1611 value: [
1612 %Day16.Packet{
1613 type: 2,
1614 value: [
1615 %Day16.Packet{
1616 type: 3,
1617 value: [%Day16.Packet{type: 0, value: [...], ...}],
1618 version: 0
1619 }
1620 ],
1621 version: 1
1622 }
1623 ],
1624 version: 1
1625 }
1626 ],
1627 version: 7
1628 }
1629 ],
1630 version: 0
1631 }
1632 ],
1633 version: 6
1634 }
1635 ],
1636 version: 2
1637 }
1638 ],
1639 version: 2
1640 }
1641 ],
1642 version: 6
1643 }
1644 ],
1645 version: 7
1646 },
1647 %Day16.Packet{type: 1, value: [%Day16.Packet{type: 4, value: 44, version: 4}], version: 7},
1648 %Day16.Packet{
1649 type: 1,
1650 value: [
1651 %Day16.Packet{type: 4, value: 255, version: 2},
1652 %Day16.Packet{type: 4, value: 91, version: 5},
1653 %Day16.Packet{type: 4, value: 176, version: 5},
1654 %Day16.Packet{type: 4, value: 23, version: 1}
1655 ],
1656 version: 7
1657 },
1658 %Day16.Packet{
1659 type: 3,
1660 value: [
1661 %Day16.Packet{type: 4, value: 11520, version: 4},
1662 %Day16.Packet{type: 4, value: 6069, version: 0},
1663 %Day16.Packet{type: 4, value: 1089149511401, version: 4},
1664 %Day16.Packet{type: 4, value: 158, version: 2},
1665 %Day16.Packet{type: 4, value: 620605, version: 0}
1666 ],
1667 version: 2
1668 },
1669 %Day16.Packet{
1670 type: 0,
1671 value: [
1672 %Day16.Packet{type: 4, value: 62788, version: 7},
1673 %Day16.Packet{type: 4, value: 9410622, version: 2},
1674 %Day16.Packet{type: 4, value: 15912821, version: 4}
1675 ],
1676 version: 4
1677 },
1678 %Day16.Packet{
1679 type: 1,
1680 value: [
1681 %Day16.Packet{type: 4, value: 22416, version: 5},
1682 %Day16.Packet{
1683 type: 5,
1684 value: [
1685 %Day16.Packet{type: 4, value: 246, version: 1},
1686 %Day16.Packet{type: 4, value: 246, version: 4}
1687 ],
1688 version: 2
1689 }
1690 ],
1691 version: 0
1692 },
1693 %Day16.Packet{type: 3, value: [%Day16.Packet{type: 4, value: 13008601, version: 5}], version: 0},
1694 %Day16.Packet{
1695 type: 0,
1696 value: [
1697 %Day16.Packet{
1698 type: 1,
1699 value: [
1700 %Day16.Packet{type: 4, value: 3, version: 4},
1701 %Day16.Packet{type: 4, value: 14, version: 1},
1702 %Day16.Packet{type: 4, value: 5, version: 0}
1703 ],
1704 version: 5
1705 },
1706 %Day16.Packet{
1707 type: 1,
1708 value: [
1709 %Day16.Packet{type: 4, value: 2, version: 1},
1710 %Day16.Packet{type: 4, value: 14, version: 1},
1711 %Day16.Packet{type: 4, value: 10, version: 1}
1712 ],
1713 version: 6
1714 },
1715 %Day16.Packet{
1716 type: 1,
1717 value: [
1718 %Day16.Packet{type: 4, value: 8, version: 3},
1719 %Day16.Packet{type: 4, value: 6, version: 6},
1720 %Day16.Packet{type: 4, value: 11, version: 0}
1721 ],
1722 version: 1
1723 }
1724 ],
1725 version: 5
1726 },
1727 %Day16.Packet{
1728 type: 1,
1729 value: [
1730 %Day16.Packet{type: 4, value: 32940592237, version: 2},
1731 %Day16.Packet{
1732 type: 5,
1733 value: [
1734 %Day16.Packet{type: 4, value: 100, version: 1},
1735 %Day16.Packet{type: 4, value: 1393232728, version: 2}
1736 ],
1737 version: 2
1738 }
1739 ],
1740 version: 0
1741 },
1742 %Day16.Packet{type: 4, value: 89, version: 3},
1743 %Day16.Packet{
1744 type: 2,
1745 value: [
1746 %Day16.Packet{type: 4, value: 204, version: 6},
1747 %Day16.Packet{type: 4, value: 260321821, version: 2},
1748 %Day16.Packet{type: 4, value: 225241983, version: 6}
1749 ],
1750 version: 0
1751 },
1752 %Day16.Packet{
1753 type: 0,
1754 value: [
1755 %Day16.Packet{type: 4, value: 960899, version: 3},
1756 %Day16.Packet{type: 4, value: 58997, version: 5},
1757 %Day16.Packet{type: 4, value: 54940, version: 6},
1758 %Day16.Packet{type: 4, value: 10974, version: 2},
1759 %Day16.Packet{type: 4, value: 882043, version: 2}
1760 ],
1761 version: 0
1762 },
1763 %Day16.Packet{
1764 type: 1,
1765 value: [
1766 %Day16.Packet{
1767 type: 6,
1768 value: [
1769 %Day16.Packet{type: 4, value: 35633017255, version: 4},
1770 %Day16.Packet{type: 4, value: 35633017255, version: 2}
1771 ],
1772 version: 3
1773 },
1774 %Day16.Packet{type: 4, value: 1359, version: 6}
1775 ],
1776 version: 6
1777 },
1778 %Day16.Packet{
1779 type: 1,
1780 value: [
1781 %Day16.Packet{type: 4, value: 92, version: 4},
1782 %Day16.Packet{type: 4, value: 38, version: 3},
1783 %Day16.Packet{type: 4, value: 160, version: 5},
1784 %Day16.Packet{type: 4, value: 111, version: 1},
1785 %Day16.Packet{type: 4, value: 64, version: 4}
1786 ],
1787 version: 4
1788 },
1789 %Day16.Packet{
1790 type: 0,
1791 value: [
1792 %Day16.Packet{type: 4, value: 2541, version: 3},
1793 %Day16.Packet{type: 4, value: 263947, version: 6},
1794 %Day16.Packet{type: 4, value: 7686705, version: 5},
1795 %Day16.Packet{type: 4, value: 31, version: 4}
1796 ],
1797 version: 2
1798 },
1799 %Day16.Packet{
1800 type: 1,
1801 value: [
1802 %Day16.Packet{
1803 type: 6,
1804 value: [
1805 %Day16.Packet{type: 4, value: 3193865, version: 1},
1806 %Day16.Packet{type: 4, value: 20223, version: 7}
1807 ],
1808 version: 2
1809 },
1810 %Day16.Packet{type: 4, value: 9328522, version: 5}
1811 ],
1812 version: 0
1813 },
1814 %Day16.Packet{
1815 type: 2,
1816 value: [
1817 %Day16.Packet{type: 4, value: 5, version: 4},
1818 %Day16.Packet{type: 4, value: 7, version: 3},
1819 %Day16.Packet{type: 4, value: 179420284, version: 4},
1820 %Day16.Packet{type: 4, value: 19890, version: 1},
1821 %Day16.Packet{type: 4, value: 2655, version: 0}
1822 ],
1823 version: 7
1824 },
1825 %Day16.Packet{
1826 type: 1,
1827 value: [
1828 %Day16.Packet{type: 4, value: 862089, version: 1},
1829 %Day16.Packet{
1830 type: 6,
1831 value: [
1832 %Day16.Packet{type: 4, value: 248, version: 3},
1833 %Day16.Packet{type: 4, value: 3286, version: 5}
1834 ],
1835 version: 3
1836 }
1837 ],
1838 version: 3
1839 },
1840 %Day16.Packet{
1841 type: 1,
1842 value: [
1843 %Day16.Packet{type: 4, value: 93, version: 6},
1844 %Day16.Packet{
1845 type: 5,
1846 value: [
1847 %Day16.Packet{type: 4, value: 4269, version: 6},
1848 %Day16.Packet{type: 4, value: 240, version: 3}
1849 ],
1850 version: 4
1851 }
1852 ],
1853 version: 5
1854 },
1855 %Day16.Packet{
1856 type: 3,
1857 value: [
1858 %Day16.Packet{type: 4, value: 2938, version: 6},
1859 %Day16.Packet{type: 4, value: 3, version: 6},
1860 %Day16.Packet{type: 4, value: 211, version: 7}
1861 ],
1862 version: 3
1863 },
1864 %Day16.Packet{
1865 type: 1,
1866 value: [
1867 %Day16.Packet{
1868 type: 7,
1869 value: [
1870 %Day16.Packet{type: 4, value: 159, version: 0},
1871 %Day16.Packet{type: 4, value: 159, version: 5}
1872 ],
1873 version: 0
1874 },
1875 %Day16.Packet{type: 4, value: 28, version: 1}
1876 ],
1877 version: 4
1878 },
1879 %Day16.Packet{type: 4, value: 84, version: 4},
1880 %Day16.Packet{
1881 type: 1,
1882 value: [
1883 %Day16.Packet{type: 4, value: 235, version: 4},
1884 %Day16.Packet{
1885 type: 6,
1886 value: [
1887 %Day16.Packet{type: 0, value: [%Day16.Packet{...}, ...], version: 4},
1888 %Day16.Packet{type: 0, value: [...], ...}
1889 ],
1890 version: 3
1891 }
1892 ],
1893 version: 6
1894 },
1895 %Day16.Packet{type: 4, value: 1425, version: 4},
1896 %Day16.Packet{
1897 type: 1,
1898 value: [
1899 %Day16.Packet{
1900 type: 7,
1901 value: [%Day16.Packet{type: 0, value: [...], ...}, %Day16.Packet{type: 0, ...}],
1902 version: 5
1903 },
1904 %Day16.Packet{type: 4, value: 13, version: 2}
1905 ],
1906 version: 2
1907 },
1908 %Day16.Packet{type: 0, value: [%Day16.Packet{type: 4, value: 3121, version: 6}], version: 5},
1909 %Day16.Packet{
1910 type: 1,
1911 value: [
1912 %Day16.Packet{type: 4, value: 51, version: 2},
1913 %Day16.Packet{type: 4, value: 61, ...},
1914 %Day16.Packet{type: 4, ...}
1915 ],
1916 version: 4
1917 },
1918 %Day16.Packet{
1919 type: 1,
1920 value: [%Day16.Packet{type: 4, value: 1393, ...}, %Day16.Packet{type: 5, ...}],
1921 version: 3
1922 },
1923 %Day16.Packet{type: 1, value: [%Day16.Packet{type: 7, ...}, %Day16.Packet{...}], version: 3},
1924 %Day16.Packet{type: 1, value: [%Day16.Packet{...}, ...], version: 7},
1925 %Day16.Packet{type: 3, value: [...], ...},
1926 %Day16.Packet{type: 2, ...},
1927 %Day16.Packet{...},
1928 ...
1929 ],
1930 version: 3
1931}
1932```
1933
1934### Task 1
1935
1936```elixir
1937defmodule Day16.Task1 do
1938 alias Day16.Packet
1939
1940 def sum(%Packet{type: 4, version: version}), do: version
1941
1942 def sum(%Packet{version: version, value: value}) do
1943 Enum.reduce(value, version, &(sum(&1) + &2))
1944 end
1945end
1946
1947Day16.Task1.sum(input)
1948```
1949
1950```output
1951949
1952```
1953
1954### Task 2
1955
1956```elixir
1957defmodule Day16.Task2 do
1958 alias Day16.Packet
1959
1960 def evaluate(%Packet{type: 0} = packet), do: reduce(packet, 0, &+/2)
1961 def evaluate(%Packet{type: 1} = packet), do: reduce(packet, 1, &*/2)
1962 def evaluate(%Packet{type: 2} = packet), do: reduce(packet, :inf, &min/2)
1963 def evaluate(%Packet{type: 3} = packet), do: reduce(packet, 0, &max/2)
1964
1965 def evaluate(%Packet{type: 4, value: value}), do: value
1966
1967 def evaluate(%Packet{type: 5} = packet), do: compare(packet, &>/2)
1968 def evaluate(%Packet{type: 6} = packet), do: compare(packet, &</2)
1969 def evaluate(%Packet{type: 7} = packet), do: compare(packet, &==/2)
1970
1971 defp reduce(%Packet{value: value}, initial, op) do
1972 Enum.reduce(value, initial, &op.(evaluate(&1), &2))
1973 end
1974
1975 defp compare(%Packet{value: [a, b]}, op) do
1976 if op.(evaluate(a), evaluate(b)), do: 1, else: 0
1977 end
1978end
1979
1980Day16.Task2.evaluate(input)
1981```
1982
1983```output
19841114600142730
1985```