-
-
Save Sentynel/5f98fcd38b88ba83058eeacc33d5412d to your computer and use it in GitHub Desktop.
This file contains 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
defmodule AoC do | |
def add({x1, y1}, {x2, y2}) do {x1+x2, y1+y2} end | |
def parse(content) do | |
String.split(content, "\n") | |
|> Enum.with_index(-1) | |
|> Enum.reduce({[], {0,0}}, fn {line, y}, acc -> | |
String.codepoints(line) | |
|> Enum.with_index(-1) | |
|> Enum.reduce(acc, fn {c, x}, {blizzards, {maxx, maxy}} -> | |
# this means that 0 is the smallest valid coordinate | |
# and maxx-1 is the largest, etc | |
# so mods work properly | |
maxx = max(x, maxx) | |
maxy = max(y, maxy) | |
blizzards = case c do | |
"." -> blizzards | |
"#" -> blizzards | |
"^" -> [{{x, y}, :north} | blizzards] | |
">" -> [{{x, y}, :east} | blizzards] | |
"v" -> [{{x, y}, :south} | blizzards] | |
"<" -> [{{x, y}, :west} | blizzards] | |
end | |
{blizzards, {maxx, maxy}} | |
end) | |
end) | |
end | |
def get_vec(dir) do | |
case dir do | |
:north -> {0,-1} | |
:south -> {0,1} | |
:east -> {1,0} | |
:west -> {-1,0} | |
end | |
end | |
def update_blizzard({{x, y}, dir}, {maxx, maxy}) do | |
{xd, yd} = get_vec(dir) | |
{newx, newy} = {Integer.mod(x+xd, maxx), Integer.mod(y+yd, maxy)} | |
{{newx, newy}, dir} | |
end | |
def update_position(pos, acc, blizset, {maxx, maxy}) do | |
[pos | Enum.map([:north, :south, :east, :west], &(add(pos, get_vec(&1))))] | |
|> Enum.filter(fn {x, y} -> | |
# special case start, end points | |
{x, y} == {0, -1} or {x, y} == {maxx-1, maxy} or ( | |
x >= 0 and x < maxx and y >= 0 and y < maxy | |
) end) | |
|> Enum.filter(&(not MapSet.member?(blizset, &1))) | |
|> MapSet.new() | |
|> MapSet.union(acc) | |
end | |
def run_step(n, {maxx, maxy}, dpos, {blizzards, positions}) do | |
new_blizzards = Enum.map(blizzards, &(update_blizzard(&1, {maxx, maxy}))) | |
blizset = Enum.map(new_blizzards, &(elem(&1, 0))) | |
|> MapSet.new() | |
new_positions = Enum.reduce(positions, MapSet.new(), | |
&(update_position(&1, &2, blizset, {maxx, maxy})) | |
) | |
if MapSet.member?(new_positions, dpos) do | |
{:halt, {n+1, new_blizzards}} | |
else | |
{:cont, {new_blizzards, new_positions}} | |
end | |
end | |
def get_to(ipos, dpos, blizzards, size) do | |
Stream.iterate(0, &(&1 + 1)) | |
|> Enum.reduce_while({blizzards, MapSet.new([ipos])}, &(run_step(&1, size, dpos, &2))) | |
end | |
def pt(content, part) do | |
{blizzards, {maxx, maxy}} = parse(content) | |
ipos = {0, -1} | |
dpos = {maxx-1, maxy} | |
if part == :pt1 do | |
{t, _blizzards} = get_to(ipos, dpos, blizzards, {maxx, maxy}) | |
t | |
else | |
{t1, blizzards} = get_to(ipos, dpos, blizzards, {maxx, maxy}) | |
{t2, blizzards} = get_to(dpos, ipos, blizzards, {maxx, maxy}) | |
{t3, _blizzards} = get_to(ipos, dpos, blizzards, {maxx, maxy}) | |
t1 + t2 + t3 | |
end | |
end | |
end | |
content = String.trim(File.read!("input.txt")) | |
#content = String.trim(File.read!("input_test.txt")) | |
s = AoC.pt(content, :pt1) | |
IO.puts(s) | |
s = AoC.pt(content, :pt2) | |
IO.puts(s) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment