Mix.install([
{:kino, "~> 0.8.0"}
])
input = Kino.Input.textarea("Input:")
input = Kino.Input.read(input) |> String.trim() |> String.split("\n")
# Used in map_reduce, takes instruction and state, returns
# list (of intermediate states) and new state.
execute = fn
"noop", {clock, reg} ->
# noop takes 1 cycle, leaves the register alone.
{[{clock + 1, reg}], {clock + 1, reg}}
"addx " <> value, {clock, reg} ->
value = String.to_integer(value)
# addx takes 2 cycles without changing the register.
# register is changed at the _end_ of the 2nd cycle.
{[{clock + 1, reg}, {clock + 2, reg}], {clock + 2, reg + value}}
end
{states, result} = Enum.map_reduce(input, {0, 1}, execute)
states = List.flatten(states)
part1 =
states
|> Enum.drop(19)
|> Enum.take_every(40)
|> Enum.take(6)
|> Enum.map(fn {clock, reg} -> clock * reg end)
|> Enum.sum()
# For each clock tick, if the register is at pixel - 1, pixel or pixel +1, then draw the pixel.
pixels =
Enum.reduce(states, "", fn
{clock, reg}, acc when reg >= rem(clock, 40) - 2 and reg <= rem(clock, 40) ->
# IO.puts("During cycle #{clock}, sprite is at #{reg}, draw pixel")
acc <> "#"
{clock, reg}, acc ->
# IO.puts("During cycle #{clock}, sprite is at #{reg}")
acc <> "."
end)
display = fn pixels ->
String.slice(pixels, 0..39) <>
"\n" <>
String.slice(pixels, 40..79) <>
"\n" <>
String.slice(pixels, 80..119) <>
"\n" <>
String.slice(pixels, 120..159) <>
"\n" <>
String.slice(pixels, 160..199) <>
"\n" <>
String.slice(pixels, 200..239) <> "\n"
end
screen = display.(pixels)
Kino.Markdown.new("```\n" <> screen <> "\n```\n")