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```elixir
842points = %{
843 ?) => 3,
844 ?] => 57,
845 ?} => 1197,
846 ?> => 25137
847}
848
849input
850|> Enum.map(&Day10.parse/1)
851|> Enum.map(fn
852 {:unexpected, [c]} -> points[c]
853 _ -> 0
854end)
855|> Enum.sum()
856```
857
858```output
859288291
860```
861
862```elixir
863points = %{
864 ?) => 1,
865 ?] => 2,
866 ?} => 3,
867 ?> => 4
868}
869
870median = fn list ->
871 sorted = Enum.sort(list)
872 middle = div(length(list), 2)
873
874 Enum.at(sorted, middle)
875end
876
877input
878|> Enum.map(&Day10.parse/1)
879|> Enum.flat_map(fn
880 {:incomplete, rest} ->
881 [
882 Enum.reduce(rest, 0, fn c, acc ->
883 acc * 5 + points[c]
884 end)
885 ]
886
887 _ ->
888 []
889end)
890|> median.()
891```
892
893```output
894820045242
895```
896
897## Day 11
898
899```elixir
900input =
901 File.read!("day11.txt")
902 |> String.split("\n")
903 |> Enum.map(&String.trim/1)
904 |> Enum.map(fn line ->
905 for <<c <- line>>, do: c - ?0
906 end)
907 |> Enum.with_index()
908 |> Enum.flat_map(fn {row, y} ->
909 for {v, x} <- Enum.with_index(row), do: {{x, y}, v}
910 end)
911 |> Map.new()
912
913defmodule Day11 do
914 def step(map) do
915 updated = Map.new(map, fn {k, v} -> {k, v + 1} end)
916
917 lightup(updated, 0)
918 end
919
920 @diffs for dx <- -1..1, dy <- -1..1, dx != 0 or dy != 0, do: {dx, dy}
921
922 IO.inspect(@diffs)
923
924 def lightup(map, n) do
925 map
926 |> Enum.reduce({map, 0}, fn
927 {_, v}, acc when v < 10 ->
928 acc
929
930 {{x, y} = k, _}, {map, count} ->
931 new_map =
932 @diffs
933 |> Enum.reduce(map, fn {dx, dy}, acc ->
934 point = {x + dx, y + dy}
935
936 case Map.fetch(acc, point) do
937 {:ok, value} when value != 0 -> %{acc | point => value + 1}
938 _ -> acc
939 end
940 end)
941 |> Map.put(k, 0)
942
943 {new_map, count + 1}
944 end)
945 |> case do
946 {map, 0} -> {map, n}
947 {map, m} -> lightup(map, n + m)
948 end
949 end
950end
951```
952
953```output
954[{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}]
955```
956
957```output
958{:module, Day11, <<70, 79, 82, 49, 0, 0, 11, ...>>, {:lightup, 2}}
959```
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```elixir
983Stream.unfold(1, &{&1, &1 + 1})
984|> Enum.reduce_while(input, fn idx, map ->
985 case Day11.step(map) do
986 {_, 100} -> {:halt, idx}
987 {next, _} -> {:cont, next}
988 end
989end)
990```
991
992```output
993403
994```
995
996## Day 12
997
998```elixir
999input =
1000 File.read!("day12.txt")
1001 |> String.split("\n")
1002 |> Enum.map(&String.split(&1, "-"))
1003
1004graph =
1005 Enum.reduce(input, %{}, fn [a, b], acc ->
1006 acc
1007 |> Map.update(a, [b], &[b | &1])
1008 |> Map.update(b, [a], &[a | &1])
1009 end)
1010
1011defmodule Day12 do
1012 def dfs(graph, start, finish), do: dfs(graph, start, finish, [start])
1013
1014 defp dfs(_graph, vertex, vertex, _visited), do: 1
1015
1016 defp dfs(graph, vertex, finish, visited) do
1017 (graph[vertex] -- visited)
1018 |> Enum.reduce(0, fn vertex, acc ->
1019 visited = if small?(vertex), do: [vertex | visited], else: visited
1020
1021 acc + dfs(graph, vertex, finish, visited)
1022 end)
1023 end
1024
1025 def dfs2(graph, start, finish), do: dfs2(graph, start, finish, %{start => :inf})
1026
1027 defp dfs2(_graph, vertex, vertex, _visited), do: 1
1028
1029 defp dfs2(graph, vertex, finish, visited) do
1030 (graph[vertex] -- keys(visited))
1031 |> Enum.reduce(0, fn vertex, acc ->
1032 visited = if small?(vertex), do: Map.update(visited, vertex, 1, &(&1 + 1)), else: visited
1033
1034 acc + dfs2(graph, vertex, finish, visited)
1035 end)
1036 end
1037
1038 defp keys(map) do
1039 if Enum.any?(map, fn {_, v} -> v == 2 end) do
1040 # there is already some vertex visited twice
1041 Map.keys(map)
1042 else
1043 for {k, v} <- map, v > 1, do: k
1044 end
1045 end
1046
1047 defp small?(<<c>> <> _), do: c in ?a..?z
1048end
1049```
1050
1051```output
1052{:module, Day12, <<70, 79, 82, 49, 0, 0, 15, ...>>, {:small?, 1}}
1053```
1054
1055```elixir
1056Day12.dfs(graph, "start", "end")
1057```
1058
1059```output
10604167
1061```
1062
1063```elixir
1064Day12.dfs2(graph, "start", "end")
1065```
1066
1067```output
106898441
1069```
1070
1071## Day 13
1072
1073```elixir
1074[input, folds] =
1075 File.read!("day13.txt")
1076 |> String.trim()
1077 |> String.split("\n\n")
1078
1079input =
1080 input
1081 |> String.split("\n")
1082 |> Enum.map(fn line ->
1083 [x, y] = String.split(line, ",")
1084
1085 {String.to_integer(x), String.to_integer(y)}
1086 end)
1087 |> MapSet.new()
1088
1089folds =
1090 folds
1091 |> String.split("\n")
1092 |> Enum.map(fn
1093 "fold along " <> <<c>> <> "=" <> rest ->
1094 {String.to_atom(<<c>>), String.to_integer(rest)}
1095 end)
1096
1097defmodule Day13 do
1098 def fold({orientation, pos}, set) do
1099 Enum.reduce(set, MapSet.new(), fn point, acc ->
1100 new_point = folded_coords(orientation, pos, point)
1101
1102 MapSet.put(acc, new_point)
1103 end)
1104 end
1105
1106 defp folded_coords(:x, col, {x, y}) when x > col, do: {abs(2 * col - x), y}
1107 defp folded_coords(:y, row, {x, y}) when y > row, do: {x, abs(2 * row - y)}
1108 defp folded_coords(_, _, point), do: point
1109
1110 def draw(set) do
1111 set
1112 |> Enum.group_by(&elem(&1, 1))
1113 |> Enum.sort()
1114 |> Enum.map(fn {_, points} ->
1115 points
1116 |> Enum.map(&elem(&1, 0))
1117 |> Enum.sort()
1118 |> Enum.chunk_every(2, 1)
1119 |> Enum.map(fn
1120 [a, b] -> b - a
1121 _ -> 0
1122 end)
1123 |> Enum.map(&String.pad_trailing("█", &1, " "))
1124 end)
1125 |> Enum.join("\n")
1126 end
1127end
1128```
1129
1130```output
1131{:module, Day13, <<70, 79, 82, 49, 0, 0, 13, ...>>, {:draw, 1}}
1132```
1133
1134```elixir
1135Day13.fold(hd(folds), input) |> MapSet.size()
1136```
1137
1138```output
1139802
1140```
1141
1142```elixir
1143Enum.reduce(folds, input, &Day13.fold/2)
1144|> Day13.draw()
1145|> IO.puts()
1146```
1147
1148```output
1149███ █ █ █ █ ████ ████ ██ █ █ ███
1150█ █ █ █ █ █ █ █ █ █ █ █ █ █
1151█ █ ██ ████ ███ █ █ █ █ ███
1152███ █ █ █ █ █ █ █ ██ █ █ █ █
1153█ █ █ █ █ █ █ █ █ █ █ █ █ █
1154█ █ █ █ █ █ █ ████ ███ ██ ███
1155```
1156
1157```output
1158:ok
1159```
1160
1161## Day 14