diff --git a/README.org b/README.org index c051b81..c9cc7fb 100644 --- a/README.org +++ b/README.org @@ -18,4 +18,5 @@ Day 6: [[https://livebook.dev/run?url=https%3A%2F%2Fgit.datarift.nl%2Ferwin%2Fao Day 7: [[https://livebook.dev/run?url=https%3A%2F%2Fgit.datarift.nl%2Ferwin%2Faoc2022%2Fraw%2Fbranch%2Fmain%2Fday7.livemd][Run in Livebook]] +Day 8: [[https://livebook.dev/run?url=https%3A%2F%2Fgit.datarift.nl%2Ferwin%2Faoc2022%2Fraw%2Fbranch%2Fmain%2Fday8.livemd][Run in Livebook]] diff --git a/day8.livemd b/day8.livemd new file mode 100644 index 0000000..178199e --- /dev/null +++ b/day8.livemd @@ -0,0 +1,276 @@ +# AOC 2022 - Day 8 + +```elixir +Mix.install( + [ + {:req, "~> 0.3.3"}, + {:nx, "~> 0.4.1"}, + {:exla, "~> 0.4.1"} + ], + config: [nx: [default_backend: EXLA.Backend]] +) +``` + +## Puzzle description + +[Day 8: Treetop Tree House](https://adventofcode.com/2022/day/8). + +## Input + +```elixir +defmodule Load do + def input do + aoc_session = System.fetch_env!("LB_AOC_SESSION") + input_url = "https://adventofcode.com/2022/day/8/input" + Req.get!(input_url, headers: [cookie: "session=#{aoc_session}"]).body + end +end +``` + +## Solution + +```elixir +defmodule Util do + def process(input) do + input + |> String.split("\n", trim: true) + |> Enum.map(fn row -> + row + |> String.split("", trim: true) + |> Enum.map(&String.to_integer/1) + end) + end + + def process2(input) do + input + |> String.split() + |> Enum.with_index() + |> Enum.flat_map(&parse_line/1) + |> Map.new(fn {v, k} -> {k, v} end) + end + + defp parse_line({string, row}) do + string + |> String.graphemes() + |> Enum.with_index() + |> Enum.map(fn {char, column} -> + {String.to_integer(char), {row + 1, column + 1}} + end) + end + + def count_visible(line) do + line + |> Enum.with_index() + |> Enum.map_reduce(0, fn {h, n}, max -> + cond do + n == 0 -> + {true, h} + + h <= max -> + {false, max} + + true -> + {true, h} + end + end) + |> elem(0) + end + + def transpose(v) do + v + |> List.zip() + |> Enum.map(&Tuple.to_list/1) + end + + def score(trees, row, column, rows, columns) do + left_score(trees, row, column, rows, columns) * + right_score(trees, row, column, rows, columns) * + up_score(trees, row, column, rows, columns) * + down_score(trees, row, column, rows, columns) + end + + defp left_score(trees, row, column, _rows, _columns) do + list = + for c <- (column - 1)..1 do + trees[{row, c}] + end + |> Enum.with_index() + + len = length(list) + + list + |> Enum.reduce_while(1, fn {height, position}, score -> + if trees[{row, column}] > height and position + 1 != len, + do: {:cont, score + 1}, + else: {:halt, score} + end) + end + + defp right_score(trees, row, column, _rows, columns) do + list = + for c <- (column + 1)..columns do + trees[{row, c}] + end + |> Enum.with_index() + + len = length(list) + + list + |> Enum.reduce_while(1, fn {height, position}, score -> + if trees[{row, column}] > height and position + 1 != len, + do: {:cont, score + 1}, + else: {:halt, score} + end) + end + + defp up_score(trees, row, column, _rows, _columns) do + list = + for r <- (row - 1)..1 do + trees[{r, column}] + end + |> Enum.with_index() + + len = length(list) + + list + |> Enum.reduce_while(1, fn {height, position}, score -> + if trees[{row, column}] > height and position + 1 != len, + do: {:cont, score + 1}, + else: {:halt, score} + end) + end + + defp down_score(trees, row, column, rows, _columns) do + list = + for r <- (row + 1)..rows do + trees[{r, column}] + end + |> Enum.with_index() + + len = length(list) + + list + |> Enum.reduce_while(1, fn {height, position}, score -> + if trees[{row, column}] > height and position + 1 != len, + do: {:cont, score + 1}, + else: {:halt, score} + end) + end +end +``` + +```elixir +defmodule Part1 do + def run(input) do + trees = + input + |> Util.process() + + {trees, horizontal} = + trees + |> Enum.map_reduce([], fn line, acc -> + visible = + [ + Util.count_visible(line), + line |> Enum.reverse() |> Util.count_visible() |> Enum.reverse() + ] + |> Enum.zip() + |> Enum.map(&(elem(&1, 0) or elem(&1, 1))) + + {line, [visible | acc]} + end) + |> then(fn {trees, horizontal} -> + {trees, Enum.reverse(horizontal)} + end) + + {_, vertical} = + trees + |> Util.transpose() + |> Enum.map_reduce([], fn col, acc -> + visible = + [ + Util.count_visible(col), + col |> Enum.reverse() |> Util.count_visible() |> Enum.reverse() + ] + |> Enum.zip() + |> Enum.map(&(elem(&1, 0) or elem(&1, 1))) + + {col, [visible | acc]} + end) + |> then(fn {trees, vertical} -> + {trees, Enum.reverse(vertical) |> Util.transpose()} + end) + + horizontal + |> Enum.zip(vertical) + |> Enum.map(fn {h, v} -> + h + |> Enum.zip(v) + |> Enum.map(&(elem(&1, 0) or elem(&1, 1))) + end) + |> Enum.map(fn line -> + Enum.count(line, & &1) + end) + |> Enum.sum() + end +end + +defmodule Part2 do + def run(input) do + trees = Util.process2(input) + + {rows, columns} = + trees + |> Map.keys() + |> Enum.max() + + for i <- 2..(rows - 1), j <- 2..(columns - 1) do + {i, j, Util.score(trees, i, j, rows, columns)} + end + |> Enum.max_by(fn {_, _, x} -> x end) + |> elem(2) + end +end + +ExUnit.start(autorun: false) + +defmodule Test do + use ExUnit.Case, async: true + @example_input ~s(30373 +25512 +65332 +33549 +35390) + @input Load.input() + + test "it loads the input" do + assert String.length(@input) > 0 + end + + test "part 1 example" do + assert Part1.run(@example_input) === 21 + end + + test "part 1" do + assert Part1.run(@input) === 1816 + end + + test "part 2 example" do + assert Part2.run(@example_input) === 8 + end + + test "part 2" do + assert Part2.run(@input) === 0 + end +end + +ExUnit.run() + +# [ +#    [1, 1, 1, 1, 1], +#    [1, 1, 1, 0, 1], +#    [1, 1, 0, 1, 1], +#    [1, 0, 1, 0, 1], +#    [1, 1, 1, 1, 1] +#  ] +``` diff --git a/devshell.toml b/devshell.toml index 73d6c22..abc625d 100644 --- a/devshell.toml +++ b/devshell.toml @@ -4,6 +4,7 @@ packages = [ "libnotify", "inotify-tools", "gnumake", + "gcc", "taplo", ] @@ -55,3 +56,8 @@ value = "9091" name = "mix" package = "elixir_1_14" help = "mix" + +[[commands]] +name = "livebook" +command = "livebook $@" +help = "livebook" diff --git a/flake.lock b/flake.lock index 1ae3442..dba12be 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ ] }, "locked": { - "lastModified": 1667210711, - "narHash": "sha256-IoErjXZAkzYWHEpQqwu/DeRNJGFdR7X2OGbkhMqMrpw=", + "lastModified": 1671489820, + "narHash": "sha256-qoei5HDJ8psd1YUPD7DhbHdhLIT9L2nadscp4Qk37uk=", "owner": "numtide", "repo": "devshell", - "rev": "96a9dd12b8a447840cc246e17a47b81a4268bba7", + "rev": "5aa3a8039c68b4bf869327446590f4cdf90bb634", "type": "github" }, "original": { @@ -56,11 +56,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1670597555, - "narHash": "sha256-/k939P2S2246G6K5fyvC0U2IWvULhb4ZJg9K7ZxsX+k=", + "lastModified": 1672350804, + "narHash": "sha256-jo6zkiCabUBn3ObuKXHGqqORUMH27gYDIFFfLq5P4wg=", "owner": "nixos", "repo": "nixpkgs", - "rev": "2dea0f4c2d6e4603f54b2c56c22367e77869490c", + "rev": "677ed08a50931e38382dbef01cba08a8f7eac8f6", "type": "github" }, "original": {