Skip to content

Instantly share code, notes, and snippets.

@marekciupak
Created March 26, 2019 13:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marekciupak/9ab9a2d69a6f3dd6ca9b895642c61b2e to your computer and use it in GitHub Desktop.
Save marekciupak/9ab9a2d69a6f3dd6ca9b895642c61b2e to your computer and use it in GitHub Desktop.
Day 18 - Advent of Code 2018
# 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