parent
a38a87d74b
commit
7f07a27b68
@ -0,0 +1,7 @@ |
||||
## Advent of Code 2015 Day 7 ## |
||||
# |
||||
### This ruby script uses the Observer design pattern (see Design Patterns by Gamma, Helm, Johnson, and Vlissides), aka PubSub to solve the puzzle. The wires subscribe to the sources they need if they are not complete, and when that source is found, LitupNews will inform the wire and take appropriate action. ### |
||||
# |
||||
### The script runs through the input file only one time, making it very efficient. ### |
||||
# |
||||
### The code could be cleaned up a lot, and I left all of my debug puts statements in, which I used in conjunction with rdebug. ### |
@ -0,0 +1,212 @@ |
||||
# Start timer |
||||
start = Time.now |
||||
''' |
||||
Advent of Code 2015 Day 7 |
||||
Part 1 |
||||
https://adventofcode.com/2015/day/7 |
||||
''' |
||||
input = File.readlines("AoC_2015_07_input.txt", chomp: true) |
||||
|
||||
# What signal is provided to wire a? |
||||
|
||||
### |
||||
# Wire class |
||||
# Contains the value (signal) of the wire, if it is known |
||||
# Contains the operator (gate) that will be performed on the left side of the '->', if it exists |
||||
# Contains an operand (source1) or two (source2), if they exist |
||||
# Subscribes to listen for source wires, if they exist for this wire |
||||
### |
||||
class Wire |
||||
@identifier |
||||
@signal |
||||
@gate # A gate provides no signal until all its inputs have a signal |
||||
@source1 |
||||
@source2 |
||||
@news |
||||
attr_reader :identifier |
||||
|
||||
def initialize(line, news) |
||||
@news = news |
||||
circuit, @identifier = line.split(" -> ") |
||||
# If the circuit is a number, set signal and end initialization |
||||
# puts "initialize: Wire #{@identifier} Circuit: #{circuit}" |
||||
if is_integer?(circuit.chomp) |
||||
# puts "initialize: Wire #{@identifier} is a signal, setting signal to #{circuit} and publishing" |
||||
@signal = circuit.chomp.to_i |
||||
pub() |
||||
return |
||||
end |
||||
statement = circuit.split(" ") |
||||
@source1 = is_integer?(statement[0]) ? statement[0].to_i : statement[0] |
||||
@source2 = 'waffles' |
||||
case statement.length |
||||
when 3 |
||||
# puts "initialize: Wire #{@identifier} is a #{statement[1]} gate with two sources: #{statement[0]} and #{statement[2]}" |
||||
@gate = statement[1] |
||||
if is_integer?(statement[2]) |
||||
@source2 = statement[2].chomp.to_i |
||||
else |
||||
@source2 = statement[2] |
||||
sub(@source2) |
||||
end |
||||
sub(@source1) if !is_integer?(@source1) |
||||
return |
||||
when 2 |
||||
# puts "initialize: Wire #{@identifier} is a NOT gate with one source" |
||||
@gate = statement[0] |
||||
if is_integer?(statement[1]) |
||||
@source1 = statement[1].chomp.to_i |
||||
else |
||||
@source1 = statement[1] |
||||
sub(@source1) |
||||
end |
||||
return |
||||
when 1 |
||||
# puts "initialize: Wire #{@identifier} is a source with no gate" |
||||
sub(@source1) if !is_integer?(@source1) |
||||
return |
||||
end |
||||
calculate() |
||||
end |
||||
|
||||
def is_integer?(value) |
||||
if value.to_i.to_s == value || value.is_a?(Integer) |
||||
true |
||||
else |
||||
false |
||||
end |
||||
end |
||||
|
||||
def pub |
||||
# puts "pub: #{@identifier} publishing with signal #{@signal}" |
||||
@news.publish(@identifier, @signal) |
||||
end |
||||
|
||||
def sub(source) |
||||
# puts "sub: Wire #{@identifier} subscribing to source #{source}" |
||||
if source.to_i.to_s != source |
||||
# puts "sub: #{source} passed check, subscribing" |
||||
@news.subscribe(source, self) |
||||
else |
||||
# puts "sub: #{source} failed check, not subscribing" |
||||
end |
||||
end |
||||
|
||||
def update(source, signal) |
||||
# puts "update: Updating wire #{@identifier} with signal #{signal} from source #{source}" |
||||
if source == @source1 |
||||
@source1 = signal.to_i |
||||
@news.unsubscribe(source, self) |
||||
# puts "update: Wire #{self.identifier} unsubscribed from source1: #{source}." |
||||
elsif source == @source2 |
||||
@source2 = signal.to_i |
||||
@news.unsubscribe(source, self) |
||||
# puts "update: Wire #{self.identifier} unsubscribed from source2: #{source}." |
||||
end |
||||
# puts "update: wire #{@identifier} received signal #{signal} from #{source} and now has sources #{@source1} and #{@source2}" |
||||
calculate() |
||||
end |
||||
|
||||
def calculate |
||||
# If source1 is an integer, and if source2 is an integer or 'waffles' (meaning there is no source2), run calculation |
||||
# puts "calculate: Wire #{@identifier} is calculating on sources #{@source1} and #{@source2}" |
||||
# puts "calculate: @source1 is an integer: #{@source1.to_i.to_s == @source1}, @source2 is an integer: #{@source2.to_i.to_s == @source2}" |
||||
if (@source1.to_i.to_s == @source1 && (@source2.to_i.to_s == @source2 || @source2 == 'waffles')) || (@source1.is_a?(Integer) && (@source2.is_a?(Integer) || @source2 == 'waffles')) |
||||
# puts "calculate: Wire #{@identifier} has all sources, calculating with gate #{@gate} and source1 of: #{@source1} and source2 of: #{@source2}" |
||||
case @gate |
||||
when "AND" |
||||
@signal = @source1.to_i & @source2.to_i |
||||
when "OR" |
||||
@signal = @source1.to_i | @source2.to_i |
||||
when "LSHIFT" |
||||
@signal = @source1.to_i << @source2.to_i |
||||
when "RSHIFT" |
||||
@signal = @source1.to_i >> @source2.to_i |
||||
when "NOT" |
||||
@signal = ~@source1.to_i |
||||
else |
||||
@signal = @source1.to_i |
||||
end |
||||
# puts "calculate: Wire #{@identifier} calculated signal #{@signal}" |
||||
pub() |
||||
else |
||||
# puts "calculate: Wire #{@identifier} cannot calculate, sources are #{@source1} and #{@source2}" |
||||
end |
||||
end |
||||
|
||||
end |
||||
|
||||
class LitupNews |
||||
@signals |
||||
@subscribers |
||||
|
||||
def initialize |
||||
@signals = {} |
||||
@subscribers = {} |
||||
end |
||||
|
||||
def print_subscribers(identifier) |
||||
if @subscribers[identifier] |
||||
puts "publish: subscribers to #{identifier}:" |
||||
@subscribers[identifier].each do |subscriber| |
||||
puts "#{subscriber.identifier}" |
||||
end |
||||
end |
||||
end |
||||
|
||||
def print_signals |
||||
puts "print_signals: Signals:" |
||||
@signals.each do |identifier, signal| |
||||
puts "#{identifier}: #{signal}" |
||||
end |
||||
end |
||||
|
||||
def publish(identifier, signal) |
||||
@signals[identifier] = signal |
||||
# print_signals() |
||||
if @subscribers[identifier] |
||||
# Reverse order loop to avoid skipping subscribers |
||||
@subscribers[identifier].reverse_each do |subscriber| |
||||
# puts "publish: #{subscriber.identifier} receiving signal #{@signals[identifier]} from #{identifier} on publish" |
||||
subscriber.update(identifier, @signals[identifier]) |
||||
# puts "publish: Updated #{subscriber.identifier} with signal #{@signals[identifier]}" |
||||
end |
||||
end |
||||
# print_signals() |
||||
end |
||||
|
||||
def subscribe(source, wire) |
||||
if @signals[source] |
||||
# puts "subscribe: #{wire.identifier} receiving signal #{@signals[source]} from #{source} on a subscribe." |
||||
wire.update(source, @signals[source]) |
||||
else |
||||
if !@subscribers[source] |
||||
# puts "subscribe: Creating new subscriber array for #{source}..." |
||||
@subscribers[source] = [] |
||||
end |
||||
@subscribers[source].push(wire) |
||||
# puts "subscribe: #{wire.identifier} subscribed to #{source}." |
||||
end |
||||
# puts "Subscriber identifiers: #{@subscribers.keys}" |
||||
end |
||||
|
||||
def unsubscribe(source, wire) |
||||
if @subscribers[source] |
||||
@subscribers[source].delete(wire) |
||||
# puts "unsubscribe: #{wire.identifier} unsubscribed from #{source}" |
||||
end |
||||
end |
||||
|
||||
def printTarget |
||||
puts "Target: #{@signals['a']}" |
||||
end |
||||
end |
||||
|
||||
news = LitupNews.new |
||||
input.each do |line| |
||||
wire = Wire.new(line, news) |
||||
end |
||||
news.printTarget |
||||
|
||||
finish = Time.now |
||||
puts "Time taken: #{finish - start} seconds" |
Loading…
Reference in new issue