Last active
December 7, 2015 17:28
-
-
Save wconrad/8d26680ea42ad8b045e2 to your computer and use it in GitHub Desktop.
Advent of code, day 7.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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