Added 2015/07

master
Josh McFarland 4 months ago
parent a38a87d74b
commit 7f07a27b68
  1. 7
      2015/07/README.md
  2. 212
      2015/07/litup.rb

@ -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…
Cancel
Save