Skip to content

Instantly share code, notes, and snippets.

@sasa1977
Last active December 23, 2017 12:53
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 sasa1977/e5449390ae0cbeef2399222e32d8139f to your computer and use it in GitHub Desktop.
Save sasa1977/e5449390ae0cbeef2399222e32d8139f to your computer and use it in GitHub Desktop.
defmodule Day23 do
defmodule Machine.Core do
def new(instructions), do:
%{
registers: %{},
position: 0,
instructions: instructions |> Enum.to_list() |> :array.from_list(fixed: true)
}
def done?(machine), do:
machine.position < 0 or machine.position >= :array.size(machine.instructions)
def current_instruction(machine), do:
:array.get(machine.position, machine.instructions)
def interpret(machine, {:set, [name, arg]}), do:
machine |> update_register(name, &%{&1 | value: value(machine, arg)}) |> next_instruction()
def interpret(machine, {:add, [name, arg]}), do:
machine |> update_register(name, &%{&1 | value: &1.value + value(machine, arg)}) |> next_instruction()
def interpret(machine, {:sub, [name, arg]}), do:
machine |> update_register(name, &%{&1 | value: &1.value - value(machine, arg)}) |> next_instruction()
def interpret(machine, {:mul, [name, arg]}), do:
machine |> update_register(name, &%{&1 | value: &1.value * value(machine, arg)}) |> next_instruction()
def interpret(machine, {:mod, [name, arg]}), do:
machine |> update_register(name, &%{&1 | value: rem(&1.value, value(machine, arg))}) |> next_instruction()
def interpret(machine, {:jgz, [x, y]}) do
if value(machine, x) > 0, do: jump(machine, value(machine, y)), else: next_instruction(machine)
end
def interpret(machine, {:jnz, [x, y]}) do
if value(machine, x) != 0, do: jump(machine, value(machine, y)), else: next_instruction(machine)
end
def update_register(machine, name, updater), do:
put_in(machine.registers[name], updater.(register(machine, name)))
def value(_machine, value) when is_integer(value), do: value
def value(machine, name) when is_binary(name), do: register(machine, name).value
def register(machine, name), do: Map.get(machine.registers, name, %{value: 0})
def next_instruction(machine), do: jump(machine, 1)
defp jump(machine, offset), do: update_in(machine.position, &(&1 + offset))
end
defmodule Machine.Part1 do
alias Day23.Machine
def new(instructions), do:
instructions |> Machine.Core.new() |> Map.merge(%{mul_count: 0})
def done?(machine), do: Machine.Core.done?(machine)
def output(machine), do: machine.mul_count
def next(machine) do
{command, _} = instruction = Machine.Core.current_instruction(machine)
mul_count = if command == :mul, do: machine.mul_count + 1, else: machine.mul_count
Machine.Core.interpret(%{machine | mul_count: mul_count}, instruction)
end
end
def part1(), do:
run_machine(Day23.Machine.Part1)
def part2(), do:
0..1000 |> Stream.map(&(108_400 + &1 * 17)) |> Stream.reject(&prime?/1) |> Enum.count()
defp prime?(2), do: true
defp prime?(n) when n > 2, do: not Enum.any?(2..trunc(Float.ceil(:math.sqrt(n))), &rem(n, &1) == 0)
defp run_machine(machine_mod), do:
instructions()
|> machine_mod.new()
|> Stream.iterate(&machine_mod.next/1)
|> Stream.drop_while(&(not machine_mod.done?(&1)))
|> Enum.take(1)
|> hd()
|> machine_mod.output()
defp instructions(), do:
"input.txt"
|> File.stream!()
|> Stream.map(&String.trim/1)
|> Enum.map(&parse_instruction/1)
defp parse_instruction(instruction) do
[instruction | args] = String.split(instruction, " ")
{String.to_atom(instruction), Enum.map(args, &parse_arg/1)}
end
defp parse_arg(arg) do
case Integer.parse(arg) do
:error -> arg
{value, ""} -> value
end
end
end
Day23.part1() |> IO.inspect()
Day23.part2() |> IO.inspect()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment