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]) 16``` 17 18```output 19:ok 20``` 21 22## Day 1 23 24### Load input 25 26```elixir 27stream = 28 File.stream!("day1.txt") 29 |> Stream.map(&String.to_integer(String.trim(&1))) 30``` 31 32```output 33#Stream<[ 34 enum: %File.Stream{ 35 line_or_bytes: :line, 36 modes: [:raw, :read_ahead, :binary], 37 path: "day1.txt", 38 raw: true 39 }, 40 funs: [#Function<47.58486609/1 in Stream.map/2>] 41]> 42``` 43 44### Task 1 45 46<!-- livebook:{"break_markdown":true} --> 47 48Compute count of consecutive increases 49 50```elixir 51stream 52|> Stream.chunk_every(2, 1, :discard) 53|> Enum.count(fn [a, b] -> a < b end) 54``` 55 56```output 571688 58``` 59 60### Task 2 61 62<!-- livebook:{"break_markdown":true} --> 63 64Compute count of consecutive increases of sums of trigrams. 65 66However we can notice, that if we have list like: 67 68$$ 69[a, b, c, d] 70$$ 71 72Then when we want to compare consecutive trigrams then we compare: 73 74$$ 75a + b + c < b + c + d \\ 76a < d 77$$ 78 79So we can traverse each 4 elements and then just compare first and last one 80instead of summing and then traversing it again. 81 82```elixir 83stream 84|> Stream.chunk_every(4, 1, :discard) 85|> Enum.count(fn [a, _, _, b] -> a < b end) 86``` 87 88```output 891728 90``` 91 92## Day 2 93 94### Load input 95 96We do parsing there, as it will help us with the latter tasks. Pattern matching 97is the simplest approach there, as input is in form of: 98 99``` 100forward 10 101up 20 102down 30 103``` 104 105We need to `trim/1` input to make sure that the last newline will not interrupt 106`String.to_integer/1` calls. 107 108```elixir 109stream = 110 File.stream!("day2.txt") 111 |> Stream.map(fn input -> 112 case String.trim(input) do 113 "forward " <> n -> {:forward, String.to_integer(n)} 114 "up " <> n -> {:up, String.to_integer(n)} 115 "down " <> n -> {:down, String.to_integer(n)} 116 end 117 end) 118``` 119 120```output 121#Stream<[ 122 enum: %File.Stream{ 123 line_or_bytes: :line, 124 modes: [:raw, :read_ahead, :binary], 125 path: "day2.txt", 126 raw: true 127 }, 128 funs: [#Function<47.58486609/1 in Stream.map/2>] 129]> 130``` 131 132### Task 1 133 134```elixir 135{h, d} = 136 stream 137 |> Enum.reduce({0, 0}, fn 138 {:forward, n}, {h, d} -> {h + n, d} 139 {:up, n}, {h, d} -> {h, d - n} 140 {:down, n}, {h, d} -> {h, d + n} 141 end) 142 143h * d 144``` 145 146```output 1471499229 148``` 149 150### Task 2 151 152```elixir 153{h, d, _} = 154 stream 155 |> Enum.reduce({0, 0, 0}, fn 156 {:forward, n}, {h, d, a} -> {h + n, d + a * n, a} 157 {:up, n}, {h, d, a} -> {h, d, a - n} 158 {:down, n}, {h, d, a} -> {h, d, a + n} 159 end) 160 161h * d 162``` 163 164```output 1651340836560 166``` 167 168## Day 3 169 170### Input 171 172```elixir 173stream = 174 File.stream!("day3.txt") 175 |> Enum.map(&String.trim/1) 176 |> Enum.map(&String.to_charlist/1) 177 178defmodule Day3 do 179 def count(list) do 180 Enum.reduce(list, List.duplicate(0, 12), fn input, acc -> 181 for {value, counter} <- Enum.zip(input, acc) do 182 case value do 183 ?1 -> counter + 1 184 ?0 -> counter 185 end 186 end 187 end) 188 end 189end 190``` 191 192```output 193{:module, Day3, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:count, 1}} 194``` 195 196### Task 1 197 198```elixir 199half = div(length(stream), 2) 200 201{a, b} = 202 stream 203 |> Day3.count() 204 |> Enum.reduce({0, 0}, fn elem, {a, b} -> 205 if elem > half do 206 {a * 2 + 1, b * 2} 207 else 208 {a * 2, b * 2 + 1} 209 end 210 end) 211 212a * b 213``` 214 215```output 2163847100 217``` 218 219### Task 2 220 221```elixir 222defmodule Day3.Task2 do 223 def reduce(list, cb), do: reduce(list, 0, cb) 224 225 defp reduce([elem], _, _), do: elem 226 227 defp reduce(list, at, cb) do 228 counts = Day3.count(list) 229 230 half = div(length(list), 2) 231 count = Enum.at(counts, at) 232 233 bit = 234 cond do 235 count == half and cb.(count + 1, half) -> ?1 236 count != half and cb.(count, half) -> ?1 237 true -> ?0 238 end 239 240 reduce(Enum.filter(list, &(Enum.at(&1, at) == bit)), at + 1, cb) 241 end 242end 243 244co2 = List.to_integer(Day3.Task2.reduce(stream, &</2), 2) 245o2 = List.to_integer(Day3.Task2.reduce(stream, &>/2), 2) 246 247co2 * o2 248``` 249 250```output 2514105235 252``` 253 254## Day 4 255 256### Input 257 258This time it is a little bit more convoluted, as there are 2 parts of the input. 259Fortunately we can easily disect the parts via pattern matching. 260 261Technically the conversion to the numbers is not needed, but it does no harm 262and provides additional layer of safety against some whitespace characters left there 263and here. 264 265The `Day4.win/2` function is manually unrolled, as it is easier to write than some 266random jumping in the list. 267 268<!-- livebook:{"disable_formatting":true} --> 269 270```elixir 271[numbers | bingos] = 272 File.read!("day4.txt") 273 |> String.split("\n\n", trim: true) 274 275numbers = 276 numbers 277 |> String.trim() 278 |> String.split(",") 279 |> Enum.map(&String.to_integer/1) 280 281bingos = 282 bingos 283 |> Enum.map(fn bingo -> 284 bingo 285 |> String.split(~r/\s+/, trim: true) 286 |> Enum.map(&String.to_integer/1) 287 end) 288 289defmodule Day4 do 290 def win( 291 [ 292 a1, a2, a3, a4, a5, 293 b1, b2, b3, b4, b5, 294 c1, c2, c3, c4, c5, 295 d1, d2, d3, d4, d5, 296 e1, e2, e3, e4, e5 297 ], 298 nums 299 ) do 300 # Rows 301 all_in([a1, a2, a3, a4, a5], nums) or 302 all_in([b1, b3, b3, b4, b5], nums) or 303 all_in([c1, c2, c3, c4, c5], nums) or 304 all_in([d1, d2, d3, d4, d5], nums) or 305 all_in([e1, e2, e3, e4, e5], nums) or 306 # Columns 307 all_in([a1, b1, c1, d1, e1], nums) or 308 all_in([a2, b2, c2, d2, e2], nums) or 309 all_in([a3, b3, c3, d3, e3], nums) or 310 all_in([a4, b4, c4, d4, e4], nums) or 311 all_in([a5, b5, c5, d5, e5], nums) 312 end 313 314 def not_matched(bingo, nums) do 315 Enum.reject(bingo, &(&1 in nums)) 316 end 317 318 defp all_in(list, nums) do 319 Enum.all?(list, &(&1 in nums)) 320 end 321end 322``` 323 324```output 325{:module, Day4, <<70, 79, 82, 49, 0, 0, 15, ...>>, {:all_in, 2}} 326``` 327 328### Task 1 329 330We simply traverse the `numbers` list aggregating the numbers (order doesn't really matter, 331here we aggregate them in reverse order to speedup the code). When we have enough numbers 332that any of the `bingos` is winning one, then we halt the reduction and return computed 333result. 334 335```elixir 336numbers 337|> Enum.reduce_while([], fn elem, acc -> 338 matches = [elem | acc] 339 340 case Enum.find(bingos, &Day4.win(&1, matches)) do 341 nil -> {:cont, matches} 342 bingo -> {:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem} 343 end 344end) 345``` 346 347```output 34834506 349``` 350 351### Task 2 352 353```elixir 354numbers 355|> Enum.reduce_while({bingos, []}, fn elem, {bingos, acc} -> 356 matches = [elem | acc] 357 358 case bingos do 359 [bingo] -> 360 if Day4.win(bingo, matches) do 361 {:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem} 362 else 363 {:cont, {bingos, matches}} 364 end 365 366 _ -> 367 {:cont, {Enum.reject(bingos, &Day4.win(&1, matches)), matches}} 368 end 369end) 370``` 371 372```output 3737686 374``` 375 376## Day 5 377 378```elixir 379defmodule Day5 do 380 defmodule Point do 381 defstruct [:x, :y] 382 383 def parse(input) do 384 [x, y] = String.split(input, ",") 385 386 %__MODULE__{x: String.to_integer(x), y: String.to_integer(y)} 387 end 388 end 389 390 defmodule Line do 391 defstruct [:start, :finish] 392 393 def new(a, b) do 394 {start, finish} = 395 cond do 396 a.x < b.x -> {a, b} 397 a.y < b.y -> {a, b} 398 true -> {b, a} 399 end 400 401 %__MODULE__{start: start, finish: finish} 402 end 403 404 def horizontal?(a), do: a.start.y == a.finish.y 405 def vertical?(a), do: a.start.x == a.finish.x 406 407 def points(a) do 408 case {sign(a.finish.x - a.start.x), sign(a.finish.y - a.start.y)} do 409 {0, dy} -> for y <- a.start.y..a.finish.y//dy, do: {a.start.x, y} 410 {dx, 0} -> for x <- a.start.x..a.finish.x//dx, do: {x, a.start.y} 411 {dx, dy} -> Enum.zip(a.start.x..a.finish.x//dx, a.start.y..a.finish.y//dy) 412 end 413 end 414 415 def orientation(a) do 416 cond do 417 horizontal?(a) -> :horizontal 418 vertical?(a) -> :vertical 419 true -> :diagonal 420 end 421 end 422 423 defp sign(0), do: 0 424 defp sign(x) when x < 0, do: -1 425 defp sign(x) when x > 0, do: 1 426 end 427end 428 429lines = 430 File.stream!("day5.txt") 431 |> Stream.map(&String.trim/1) 432 |> Stream.map(fn input -> 433 [a, b] = String.split(input, " -> ") 434 435 pa = Day5.Point.parse(a) 436 pb = Day5.Point.parse(b) 437 438 Day5.Line.new(pa, pb) 439 end) 440``` 441 442```output 443#Stream<[ 444 enum: %File.Stream{ 445 line_or_bytes: :line, 446 modes: [:raw, :read_ahead, :binary], 447 path: "day5.txt", 448 raw: true 449 }, 450 funs: [#Function<47.58486609/1 in Stream.map/2>, #Function<47.58486609/1 in Stream.map/2>] 451]> 452``` 453 454### Task 1 455 456```elixir 457lines 458|> Stream.filter(&(Day5.Line.orientation(&1) != :diagonal)) 459|> Stream.flat_map(&Day5.Line.points/1) 460|> Enum.frequencies() 461|> Enum.count(fn {_k, v} -> v > 1 end) 462``` 463 464```output 4655197 466``` 467 468### Task 2 469 470```elixir 471lines 472|> Stream.flat_map(&Day5.Line.points/1) 473|> Enum.frequencies() 474|> Enum.count(fn {_k, v} -> v > 1 end) 475``` 476 477```output 47818605 479``` 480 481## Day 6 482 483```elixir 484initial = for i <- 0..8, into: %{}, do: {i, 0} 485 486counts = 487 File.read!("day6.txt") 488 |> String.trim() 489 |> String.split(",") 490 |> Enum.map(&String.to_integer/1) 491 |> Enum.frequencies() 492 |> Map.merge(initial, fn _, a, _ -> a end) 493 494defmodule Day6 do 495 def next(%{0 => next} = population) do 496 1..8 497 |> Map.new(&{&1 - 1, population[&1]}) 498 |> Map.merge(%{6 => next, 8 => next}, fn _, v1, v2 -> v1 + v2 end) 499 end 500end 501``` 502 503```output 504{:module, Day6, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:next, 1}} 505``` 506 507### Task 1 508 509```elixir 5101..80 511|> Enum.reduce(counts, fn _, acc -> Day6.next(acc) end) 512|> Map.values() 513|> Enum.sum() 514``` 515 516```output 517343441 518``` 519 520### Task 2 521 522```elixir 5231..256 524|> Enum.reduce(counts, fn _, acc -> Day6.next(acc) end) 525|> Map.values() 526|> Enum.sum() 527``` 528 529```output 5301569108373832 531``` 532 533## Day 7 534 535```elixir 536input = 537 File.read!("day7.txt") 538 |> String.trim() 539 |> String.split(",") 540 |> Enum.map(&String.to_integer/1) 541``` 542 543```output 544[1101, 1, 29, 67, 1102, 0, 1, 65, 1008, 65, 35, 66, 1005, 66, 28, 1, 67, 65, 20, 4, 0, 1001, 65, 1, 545 65, 1106, 0, 8, 99, 35, 67, 101, 99, 105, 32, 110, 39, 101, 115, 116, 32, 112, 97, 115, 32, 117, 546 110, 101, 32, 105, ...] 547``` 548 549### Task 1 550 551```elixir 552mean = Enum.at(Enum.sort(input), div(length(input), 2)) 553 554input 555|> Enum.map(&abs(&1 - mean)) 556|> Enum.sum() 557``` 558 559```output 560336721 561``` 562 563### Task 2 564 565```elixir 566arith_sum = fn n -> div(n * n + n, 2) end 567 568max = Enum.max(input) 569 5700..max 571|> Enum.reduce(:infinity, fn n, acc -> 572 sum = 573 input 574 |> Enum.map(&arith_sum.(abs(&1 - n))) 575 |> Enum.sum() 576 577 if sum < acc, do: sum, else: acc 578end) 579``` 580 581```output 58291638945 583``` 584 585## Day 8 586 587```elixir 588input = 589 File.stream!("day8.txt") 590 |> Stream.map(fn line -> 591 line 592 |> String.split(" | ") 593 |> Enum.map(fn part -> 594 part 595 |> String.trim() 596 |> String.split(" ") 597 |> Enum.map(fn disp -> 598 disp 599 |> String.to_charlist() 600 |> Enum.sort() 601 |> List.to_string() 602 end) 603 end) 604 |> List.to_tuple() 605 end) 606``` 607 608```output 609#Stream<[ 610 enum: %File.Stream{ 611 line_or_bytes: :line, 612 modes: [:raw, :read_ahead, :binary], 613 path: "day8.txt", 614 raw: true 615 }, 616 funs: [#Function<47.58486609/1 in Stream.map/2>] 617]> 618``` 619 620```elixir 621input 622|> Enum.map(fn {_, output} -> 623 Enum.count(output, &(byte_size(&1) in [2, 3, 4, 7])) 624end) 625|> Enum.sum() 626``` 627 628```output 629390 630``` 631 632```elixir 633defmodule Day8.Task2 do 634 def a --- b do 635 MapSet.difference(a, b) 636 end 637 638 def a +++ b do 639 MapSet.union(a, b) 640 end 641 642 def a <~> b do 643 MapSet.intersection(a, b) 644 end 645 646 def a <|> b do 647 (a +++ b) --- (a <~> b) 648 end 649 650 # 1. 7. 4. 2|3|5. 2|3|5. 2|3|5. 6|9|0. 6|9|0. 6|9|0. 8. 651 def deduce({cf, acf, bcdf, acdeg, acdfg, abdfg, abdefg, abcdfg, abcefg, abcdefg}) do 652 a = acf --- cf 653 eg = abcdefg --- (acf +++ bcdf) 654 bd = bcdf --- cf 655 abfg = abdefg <|> abcdfg <|> abcefg 656 b = abfg <~> bd 657 f = abfg <~> cf 658 g = abfg --- (a +++ b +++ f) 659 d = bd --- b 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 |> List.to_tuple() 667 668 [ 669 # 0 670 [a, b, c, e, f, g], 671 # 1 672 [c, f], 673 # 2 674 [a, c, d, e, g], 675 # 3 676 [a, c, d, f, g], 677 # 4 678 [b, c, d, f], 679 # 5 680 [a, b, d, f, g], 681 # 6 682 [a, b, d, e, f, g], 683 # 7 684 [a, c, f], 685 # 8 686 [a, b, c, d, e, f, g], 687 # 9 688 [a, b, c, d, f, g] 689 ] 690 |> Enum.map(&List.to_string(Enum.sort(&1))) 691 |> Enum.with_index() 692 |> Map.new() 693 end 694 695 defp extract(a) do 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 |> List.to_tuple() 714 |> Day8.Task2.deduce() 715 |> Day8.Task2.decode(output) 716end) 717|> Enum.sum() 718``` 719 720```output 721warning: variable "abdfg" is unused (if the variable is not meant to be used, prefix it with an underscore) 722 solutions.livemd#cell:19: Day8.Task2.deduce/1 723 724warning: variable "acdeg" is unused (if the variable is not meant to be used, prefix it with an underscore) 725 solutions.livemd#cell:19: Day8.Task2.deduce/1 726 727warning: variable "acdfg" is unused (if the variable is not meant to be used, prefix it with an underscore) 728 solutions.livemd#cell:19: Day8.Task2.deduce/1 729 730``` 731 732```output 7331011785 734```