Skip to content

Instantly share code, notes, and snippets.

@wconrad
Last active December 7, 2015 17:28
Show Gist options
  • Save wconrad/8d26680ea42ad8b045e2 to your computer and use it in GitHub Desktop.
Save wconrad/8d26680ea42ad8b045e2 to your computer and use it in GitHub Desktop.
Advent of code, day 7.
#!/usr/bin/env ruby
# http://adventofcode.com/day/7
class Operation
attr_reader :source
def initialize(source:, inputs:, opcode:, output:)
@source = source
@inputs = inputs
@opcode = opcode
@output = output
end
def has_all_inputs?
input_values.all?
end
def call
@output.call(@opcode.call(*input_values))
end
def to_s
@source
end
private
def input_values
@inputs.map(&:call)
end
end
class Parser
def initialize(wires)
@wires = wires
end
def parse_lines(lines)
lines.map do |line|
parse_line(line)
end
end
private
def parse_line(line)
case line
when /^(\S+) -> (\S+)$/
assignment(line, *$~.captures)
when /^(\S+) (\S+) -> (\S+)$/
unary_op(line, *$~.captures)
when /^(\S+) (\S+) (\S+) -> (\S+)$/
binary_op(line, *$~.captures)
else
raise "Syntax error at #{line.inspect}"
end
end
UNARY_OPCODES = {
"NOT" => ->(a) { ~a }
}
BINARY_OPCODES = {
"AND" => ->(a, b) { a & b },
"OR" => ->(a, b) { a | b },
"RSHIFT" => ->(a, b) { a >> b },
"LSHIFT" => ->(a, b) { a << b },
}
def assignment(line, a_name, x_name)
Operation.new(
source: line,
inputs: [input(a_name)],
opcode: ->(n) { n },
output: output(x_name),
)
end
def unary_op(line, op_name, a_name, x_name)
Operation.new(
source: line,
inputs: [input(a_name)],
opcode: UNARY_OPCODES.fetch(op_name),
output: output(x_name),
)
end
def binary_op(line, a_name, op_name, b_name, x_name)
Operation.new(
source: line,
inputs: [input(a_name), input(b_name)],
opcode: BINARY_OPCODES.fetch(op_name),
output: output(x_name),
)
end
def input(s)
if s =~ /^\d+$/
-> { Integer(s) }
else
-> { @wires[s] }
end
end
def output(x_name)
->(x) { @wires[x_name] = x }
end
end
class Engine
def initialize(operations)
@operations = operations
end
def call
while !done?
step
end
end
private
def done?
@operations.empty?
end
def step
ready, rest = @operations.partition(&:has_all_inputs?)
if ready.empty?
puts "No operations are ready:"
puts @operations
raise "Stalled"
end
ready.each(&:call)
@operations = rest
end
end
wires = {}
parser = Parser.new(wires)
input = File.read("input")
operations = parser.parse_lines(input.lines)
# part 1
Engine.new(operations).call
p wires.fetch("a")
# part 2
wires.replace("b" => wires.fetch("a"))
operations = operations.reject do |op|
op.source =~ / -> b$/
end
Engine.new(operations).call
p wires.fetch("a")
#!/usr/bin/env ruby
# http://adventofcode.com/day/7
require "forwardable"
require "stringio"
class Compiler
extend Forwardable
def self.escape(s)
if s =~ /^\d+$/
s
else
"f_#{s}"
end
end
def_delegator self, :escape
def initialize(input, output)
@input = input
@output = output
end
def call
@output.puts "class Runtime"
@input.lines.map do |line|
compile_line(line)
end
@output.puts "end"
@output.puts
@output.puts "puts Runtime.new.#{escape('a')}"
end
private
def compile_line(line)
case line
when /^(\S+) -> (\S+)$/
assignment(line, *$~.captures)
when /^(\S+) (\S+) -> (\S+)$/
unary_op(line, *$~.captures)
when /^(\S+) (\S+) (\S+) -> (\S+)$/
binary_op(line, *$~.captures)
else
raise "Syntax error at #{line.inspect}"
end
end
UNARY_OPCODES = {
"NOT" => "~",
}
BINARY_OPCODES = {
"AND" => "&",
"OR" => "|",
"RSHIFT" => ">>",
"LSHIFT" => "<<",
}
def assignment(line, a_name, x_name)
expression = escape(a_name)
define_function(line, x_name, expression)
end
def unary_op(line, op_name, a_name, x_name)
opcode = UNARY_OPCODES.fetch(op_name)
expression = "#{opcode}#{escape(a_name)}"
define_function(line, x_name, expression)
end
def binary_op(line, a_name, op_name, b_name, x_name)
opcode = BINARY_OPCODES.fetch(op_name)
expression = "#{escape(a_name)} #{opcode} #{escape(b_name)}"
define_function(line, x_name, expression)
end
private
def define_function(line, name, expression)
@output.puts
@output.puts " # #{line}"
@output.puts " def #{escape(name)}"
@output.puts " @#{escape(name)} ||= #{expression}"
@output.puts " end"
end
end
# part 1
input = File.read("input")
output = StringIO.new
compiler = Compiler.new(input, output)
compiler.call
eval(output.string)
# part 2
input = File.read("input")
orig_input = input.dup
input.gsub!(/^14146 -> b$/, "956 -> b")
output = StringIO.new
compiler = Compiler.new(input, output)
compiler.call
eval(output.string)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment