Advent of code day 14
Mix.install([
{:kino, "~> 0.5.0"}
])
Setup input
example = Kino.Input.textarea("Please paste your input example:")
input = Kino.Input.textarea("Please paste your real input:")
defmodule Robot do
@max_row 103
@max_col 101
defstruct [:current_position, :velocity]
def position_after_seconds(
%__MODULE__{current_position: cp, velocity: v} = robot,
seconds \\ 1
) do
{row, col} = cp
{vr, vc} = v
row = Integer.mod(row + vr * seconds, @max_row)
col = Integer.mod(col + vc * seconds, @max_col)
%{robot | current_position: {row, col}}
end
def height do
@max_row
end
def width do
@max_col
end
end
parsed =
example
|> Kino.Input.read()
|> String.split("\n", trim: true)
|> Enum.map(fn line ->
[[col], [row], [vc], [vr]] = Regex.scan(~r/-?\d+(\.\d+)?/, line)
{{String.to_integer(row), String.to_integer(col)}, {String.to_integer(vr), String.to_integer(vc)}}
end)
robots =
parsed
|> Enum.map(fn {position, velocity} ->
%Robot{
current_position: position,
velocity: velocity
}
end)
Part 01
robots
|> Enum.reduce(%{q1: 0, q2: 0, q3: 0, q4: 0}, fn robot, acc ->
robot = Robot.position_after_seconds(robot, 100)
row_mid = div(Robot.height(), 2)
col_mid = div(Robot.width(), 2)
case robot.current_position do
{row, col} when row < row_mid and col < col_mid -> Map.update(acc, :q1, 0, &(&1 + 1))
{row, col} when row < row_mid and col > col_mid -> Map.update(acc, :q2, 0, &(&1 + 1))
{row, col} when row > row_mid and col < col_mid -> Map.update(acc, :q3, 0, &(&1 + 1))
{row, col} when row > row_mid and col > col_mid -> Map.update(acc, :q4, 0, &(&1 + 1))
_ -> acc
end
end)
|> Map.values()
|> Enum.product()
defmodule GridPrinter do
def print_grid(map) do
# Extract all the coordinates
coordinates = Map.keys(map)
# Determine the grid bounds
{min_x, max_x} = Enum.min_max(Enum.map(coordinates, fn {x, _y} -> x end))
{min_y, max_y} = Enum.min_max(Enum.map(coordinates, fn {_x, y} -> y end))
# Build the grid as a string
for y <- min_y..max_y do
for x <- min_x..max_x do
# Get the value or a default space
Map.get(map, {x, y}, " ")
end
# Join the row into a string
|> Enum.join("")
end
# Join all rows with a newline
|> Enum.join("\n")
# Print the grid
|> IO.puts()
end
def as_string(map) do
# Extract all the coordinates
coordinates = Map.keys(map)
# Determine the grid bounds
{min_x, max_x} = Enum.min_max(Enum.map(coordinates, fn {x, _y} -> x end))
{min_y, max_y} = Enum.min_max(Enum.map(coordinates, fn {_x, y} -> y end))
# Build the grid as a string
for y <- min_y..max_y do
for x <- min_x..max_x do
# Get the value or a default space
Map.get(map, {x, y}, " ")
end
# Join the row into a string
|> Enum.join("")
end
# Join all rows with a newline
|> Enum.join("\n")
end
end
Part 02
rows = Robot.height() - 1
cols = Robot.width() - 1
grid =
for l <- 0..rows, c <- 0..cols, into: %{} do
{{l, c}, "."}
end
{iteration, set} =
0
|> Stream.iterate(&(&1 + 1))
|> Enum.reduce_while({grid, MapSet.new()}, fn iteration, {grid, set} ->
robots = Enum.map(robots, fn robot -> Robot.position_after_seconds(robot, iteration) end)
picture =
Enum.reduce(robots, grid, fn robot, grid ->
Map.put(grid, robot.current_position, "#")
end)
{:ok, pid} = File.open("/advent/tree.txt", [:append])
IO.binwrite(pid, "#{iteration}\n" <> GridPrinter.as_string(picture) <> "\n\n")
cond do
MapSet.member?(set, picture) ->
{:halt, {iteration, set}}
true ->
set = MapSet.put(set, picture)
{:cont, {grid, set}}
end
end)