Skip to content

Instantly share code, notes, and snippets.

@Sentynel
Created December 24, 2022 13:16
Show Gist options
  • Save Sentynel/5f98fcd38b88ba83058eeacc33d5412d to your computer and use it in GitHub Desktop.
Save Sentynel/5f98fcd38b88ba83058eeacc33d5412d to your computer and use it in GitHub Desktop.
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