Created
March 26, 2019 13:31
-
-
Save marekciupak/9ab9a2d69a6f3dd6ca9b895642c61b2e to your computer and use it in GitHub Desktop.
Day 18 - Advent of Code 2018
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://adventofcode.com/2018/day/18 | |
defmodule Day18 do | |
def parse_input(input) do | |
input | |
|> String.split("\n", trim: true) | |
|> Enum.reduce({%{}, 0}, fn line, {area, y} -> {parse_next_line(area, line, y), y + 1} end) | |
|> elem(0) | |
end | |
defp parse_next_line(area, line, y) do | |
line | |
|> String.codepoints() | |
|> Enum.reduce({area, 0}, fn acre, {area, x} -> {parse_next_acre(area, acre, y, x), x + 1} end) | |
|> elem(0) | |
end | |
defp parse_next_acre(area, acre, y, x) do | |
Map.put(area, {y, x}, parse_acre(acre)) | |
end | |
defp parse_acre("."), do: :open | |
defp parse_acre("#"), do: :lumberyard | |
defp parse_acre("|"), do: :trees | |
def call(input, n) do | |
input | |
|> parse_input | |
|> next_n_areas(n) | |
|> calculate_resource_value() | |
end | |
def next_n_areas(_area, _n, prevs \\ []) | |
def next_n_areas(area, n, prevs) when n > 0 do | |
case Enum.find_index(prevs, fn prev -> area == prev end) do | |
nil -> | |
next_n_areas(next_area(area), n - 1, [area | prevs]) | |
index -> | |
next_n_areas(area, rem(n, index + 1), []) | |
end | |
end | |
def next_n_areas(area, 0, _prevs), do: area | |
def next_area(area) do | |
area | |
|> Enum.reduce(%{}, fn {point, acre}, acc -> | |
Map.put(acc, point, next_acre(area, point, acre)) | |
end) | |
end | |
defp next_acre(area, point, :open) do | |
point | |
|> adjacent_points | |
|> Enum.reduce_while(0, fn point, acc -> | |
case Map.get(area, point) do | |
:trees -> {if(acc == 2, do: :halt, else: :cont), acc + 1} | |
_ -> {:cont, acc} | |
end | |
end) | |
|> case do | |
3 -> :trees | |
_ -> :open | |
end | |
end | |
defp next_acre(area, point, :trees) do | |
point | |
|> adjacent_points | |
|> Enum.reduce_while(3, fn point, acc -> | |
case {Map.get(area, point), acc} do | |
{:lumberyard, 1} -> {:halt, acc - 1} | |
{:lumberyard, _} -> {:cont, acc - 1} | |
_ -> {:cont, acc} | |
end | |
end) | |
|> case do | |
0 -> :lumberyard | |
_ -> :trees | |
end | |
end | |
defp next_acre(area, point, :lumberyard) do | |
point | |
|> adjacent_points | |
|> Enum.reduce_while({false, false}, fn point, acc = {_has_lumberyard, _has_trees} -> | |
case {Map.get(area, point), acc} do | |
{:lumberyard, {_, true}} -> {:halt, {true, true}} | |
{:lumberyard, {_, false}} -> {:cont, {true, false}} | |
{:trees, {true, _}} -> {:halt, {true, true}} | |
{:trees, {false, _}} -> {:cont, {false, true}} | |
_ -> {:cont, acc} | |
end | |
end) | |
|> case do | |
{true, true} -> :lumberyard | |
_ -> :open | |
end | |
end | |
defp calculate_resource_value(area) do | |
n_lumberyard = Enum.count(area, fn {_k, v} -> v == :lumberyard end) | |
n_trees = Enum.count(area, fn {_k, v} -> v == :trees end) | |
n_lumberyard * n_trees | |
end | |
defp adjacent_points({y, x}) do | |
[ | |
{y - 1, x - 1}, | |
{y - 1, x}, | |
{y - 1, x + 1}, | |
{y, x - 1}, | |
{y, x + 1}, | |
{y + 1, x - 1}, | |
{y + 1, x}, | |
{y + 1, x + 1} | |
] | |
end | |
end | |
defmodule Day18Test do | |
use ExUnit.Case | |
test "parses the input and returns an area" do | |
assert Day18.parse_input(".#\n||") == %{ | |
{0, 0} => :open, | |
{0, 1} => :lumberyard, | |
{1, 0} => :trees, | |
{1, 1} => :trees | |
} | |
end | |
test "calculates the area in next minute" do | |
prev = | |
Day18.parse_input(""" | |
.#.#...|#. | |
.....#|##| | |
.|..|...#. | |
..|#.....# | |
#.#|||#|#| | |
...#.||... | |
.|....|... | |
||...#|.#| | |
|.||||..|. | |
...#.|..|. | |
""") | |
next = | |
Day18.parse_input(""" | |
.......##. | |
......|### | |
.|..|...#. | |
..|#||...# | |
..##||.|#| | |
...#||||.. | |
||...|||.. | |
|||||.||.| | |
|||||||||| | |
....||..|. | |
""") | |
assert Day18.next_area(prev) == next | |
end | |
test "calculates the area in 10 minutes" do | |
prev = | |
Day18.parse_input(""" | |
.#.#...|#. | |
.....#|##| | |
.|..|...#. | |
..|#.....# | |
#.#|||#|#| | |
...#.||... | |
.|....|... | |
||...#|.#| | |
|.||||..|. | |
...#.|..|. | |
""") | |
next = | |
Day18.parse_input(""" | |
.||##..... | |
||###..... | |
||##...... | |
|##.....## | |
|##.....## | |
|##....##| | |
||##.####| | |
||#####||| | |
||||#||||| | |
|||||||||| | |
""") | |
assert Day18.next_n_areas(prev, 10) == next | |
end | |
test "calculates the total resource value of the lumber collection area after 10 minutes" do | |
assert """ | |
.#.#...|#. | |
.....#|##| | |
.|..|...#. | |
..|#.....# | |
#.#|||#|#| | |
...#.||... | |
.|....|... | |
||...#|.#| | |
|.||||..|. | |
...#.|..|. | |
""" | |
|> Day18.call(10) == 1147 | |
end | |
test "part 1" do | |
assert File.read!("input.txt") |> Day18.call(10) == 536_370 | |
end | |
test "part 2" do | |
assert File.read!("input.txt") |> Day18.call(1_000_000_000) == 190_512 | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment