Code for the Advent of Code event
aoc
advent-of-code
1#!/usr/bin/env ruby
2# frozen_string_literal: true
3
4class CPU
5 attr_reader :recovered_frequency, :send_count
6
7 def initialize(send_queue = nil, recv_queue = nil)
8 @send_queue = send_queue
9 @recv_queue = recv_queue
10 @registers = Hash.new 0
11 @pc = 0
12 @send_count = 0
13 end
14
15 def load(program)
16 @program = program.map(&:split)
17 self
18 end
19
20 def run_part1
21 step while @pc >= 0 && @pc < @program.size && !recovered_frequency
22 self
23 end
24
25 def run_part2
26 @wait = false
27 step while @pc >= 0 && @pc < @program.size && !@wait
28 end
29
30 def step
31 instruction = @program[@pc]
32 send(*instruction)
33 @pc += 1
34 end
35
36 def snd(freq)
37 freq = resolve(freq)
38 @last_frequency = freq
39 return unless @send_queue
40 @send_queue.enq freq
41 @send_count += 1
42 end
43
44 def set(reg, value)
45 @registers[reg] = resolve(value)
46 end
47
48 def add(reg, value)
49 @registers[reg] += resolve(value)
50 end
51
52 def mul(reg, value)
53 @registers[reg] *= resolve(value)
54 end
55
56 def mod(reg, value)
57 @registers[reg] %= resolve(value)
58 end
59
60 def rcv(value)
61 @recovered_frequency = @last_frequency unless resolve(value) == 0
62 return if @recv_queue.nil?
63 if @recv_queue.empty?
64 @pc -= 1
65 @wait = true
66 return
67 end
68 @registers[value] = @recv_queue.deq
69 end
70
71 def jgz(value, offset)
72 return if resolve(value) <= 0
73 @pc += resolve(offset) - 1
74 end
75
76 private
77
78 def resolve(value)
79 n = value.to_i
80 n.to_s == value ? n : @registers[value]
81 end
82end
83
84PROGRAM = ARGF.readlines
85
86puts CPU.new.load(PROGRAM).run_part1.recovered_frequency
87
88a_to_b = Queue.new
89b_to_a = Queue.new
90
91a = CPU.new(a_to_b, b_to_a).load(PROGRAM)
92a.set 'p', '0'
93
94b = CPU.new(b_to_a, a_to_b).load(PROGRAM)
95b.set 'p', '1'
96
97a.run_part2
98b.run_part2
99
100while !a_to_b.empty? || !b_to_a.empty?
101 a.run_part2
102 b.run_part2
103end
104
105puts b.send_count