From 2daf9159b43c229f9ea7aa13c9279c0670d077c9 Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Sun, 8 Dec 2019 21:16:04 -0800 Subject: [PATCH] 2019: Day 7 Part 1 --- 2019/day7/input | 1 + 2019/day7/intcode.rb | 223 +++++++++++++++++++++++++++++++++++++++++++ 2019/day7/part1.rb | 61 ++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 2019/day7/input create mode 100644 2019/day7/intcode.rb create mode 100644 2019/day7/part1.rb diff --git a/2019/day7/input b/2019/day7/input new file mode 100644 index 0000000..b7f4dc4 --- /dev/null +++ b/2019/day7/input @@ -0,0 +1 @@ +3,8,1001,8,10,8,105,1,0,0,21,34,55,68,93,106,187,268,349,430,99999,3,9,102,5,9,9,1001,9,2,9,4,9,99,3,9,1001,9,5,9,102,2,9,9,101,2,9,9,102,2,9,9,4,9,99,3,9,101,2,9,9,102,4,9,9,4,9,99,3,9,101,4,9,9,102,3,9,9,1001,9,2,9,102,4,9,9,1001,9,2,9,4,9,99,3,9,101,2,9,9,1002,9,5,9,4,9,99,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,99,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,2,9,9,4,9,99,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,99,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,99 diff --git a/2019/day7/intcode.rb b/2019/day7/intcode.rb new file mode 100644 index 0000000..c0fd23e --- /dev/null +++ b/2019/day7/intcode.rb @@ -0,0 +1,223 @@ +# frozen_string_literal: true + +require 'forwardable' + +module Intcode + def self.read(filename) + parse File.read(filename) + end + + def self.parse(input) + parsed = input + .gsub(/^#.*$/, '') + .gsub('\s', '') + .split(',') + .map(&:to_i) + Program.new parsed + end + + class Program + def initialize(mem) + @initial_state = mem + end + + def as_memory + Memory.new(@initial_state.clone) + end + + def run(&user_input) + Computer.new(as_memory).run(&user_input) + end + end + + class Memory + extend Forwardable + + def_delegator :@cells, :[] + def_delegator :@cells, :[]= + + def initialize(cells) + @cells = cells + end + + def to_s + batch_size = 10 + s = String.new + @cells.each_slice(batch_size).each_with_index do |batch, idx| + prefix = idx * batch_size + s << "#{prefix}: #{batch.join(' ')}\n" + end + s + end + + def [](idx) + raise ArgumentError, "index out of range: #{idx}" if idx.negative? || idx >= @cells.length + + @cells[idx] + end + + def []=(idx, val) + raise ArgumentError, "index out of range: #{idx}" if idx.negative? || idx >= @cells.length + + @cells[idx] = val + end + end + + class Computer + def initialize(mem, &block) + @mem = mem + @ip = 0 + @input_block = block + end + + def on_output(&block) + @output_block = block + end + + def run + loop do + return if tick == :done_executing + end + end + + def tick + case operation(@mem[@ip]) + when 1 + add! + when 2 + mul! + when 3 + input!(&@input_block) + when 4 + output!(&@output_block) + when 5 + branch_if_not_zero! + when 6 + branch_if_zero! + when 7 + less_than! + when 8 + equal! + when 99 + :done_executing + else + raise StandardError, "unexpected opcode: #{opcode}" + end + end + + private + + def add! + mode = modes(@mem[@ip]) + a = read(@ip + 1, mode[0]) + b = read(@ip + 2, mode[1]) + r = @mem[@ip + 3] + raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + + @mem[r] = a + b + @ip += 4 + end + + def mul! + mode = modes(@mem[@ip]) + a = read(@ip + 1, mode[0]) + b = read(@ip + 2, mode[1]) + r = @mem[@ip + 3] + raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + + @mem[r] = a * b + @ip += 4 + end + + def input!(&user_input) + mode = modes(@mem[@ip]) + r = @mem[@ip + 1] + raise StandardError, "unexpected mode: #{mode[0]}" if mode[0] != 0 + + @mem[r] = input(&user_input) + @ip += 2 + end + + def output!(&user_output) + mode = modes(@mem[@ip]) + output = read(@ip + 1, mode[0]) + user_output.call output if block_given? + @ip += 2 + end + + def branch_if_not_zero! + mode = modes(@mem[@ip]) + a = read(@ip + 1, mode[0]) + b = read(@ip + 2, mode[1]) + @ip = a.zero? ? @ip + 3 : b + end + + def branch_if_zero! + mode = modes(@mem[@ip]) + a = read(@ip + 1, mode[0]) + b = read(@ip + 2, mode[1]) + @ip = a.zero? ? b : @ip + 3 + end + + def less_than! + mode = modes(@mem[@ip]) + a = read(@ip + 1, mode[0]) + b = read(@ip + 2, mode[1]) + r = @mem[@ip + 3] + raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + + @mem[r] = a < b ? 1 : 0 + @ip += 4 + end + + def equal! + mode = modes(@mem[@ip]) + a = read(@ip + 1, mode[0]) + b = read(@ip + 2, mode[1]) + r = @mem[@ip + 3] + raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + + @mem[r] = a == b ? 1 : 0 + @ip += 4 + end + + def input + if block_given? + inp = yield + raise "Invalid input: #{inp}" unless inp.is_a?(Numeric) + + return inp + end + + print 'input> ' + gets.to_i + end + + def operation(cell) + (cell % 100) + end + + def modes(cell) + m = Hash.new(0) + (cell / 100).truncate.digits.each_with_index do |d, i| + m[i] = d + end + m + end + + def param(val, mode) + case mode + when 0 + @mem[val] + when 1 + val + else + raise StandardError, "unexpected mode: #{mode}" + end + end + + def read(addr, mode) + param(@mem[addr], mode) + end + end +end diff --git a/2019/day7/part1.rb b/2019/day7/part1.rb new file mode 100644 index 0000000..02cb85d --- /dev/null +++ b/2019/day7/part1.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require_relative 'intcode' + +def inputter(queue) + idx = 0 + proc do + input = queue[idx] + idx += 1 + input + end +end + +class Amplifier + def initialize(memory, phase) + @memory = memory + @phase = phase + end + + def amplify(input) + next_input = inputter([@phase, input]) + computer = Intcode::Computer.new(@memory) do + inp = next_input.call + # print 'input> ' + # puts inp + inp + end + output = nil + computer.on_output do |out| + output = out + end + computer.run + output + end +end + +def thrust(program, phases) + a = Amplifier.new(program.as_memory, phases[0]).amplify(0) + b = Amplifier.new(program.as_memory, phases[1]).amplify(a) + c = Amplifier.new(program.as_memory, phases[2]).amplify(b) + d = Amplifier.new(program.as_memory, phases[3]).amplify(c) + e = Amplifier.new(program.as_memory, phases[4]).amplify(d) + e +end + +program = Intcode.read(File.join(__dir__, 'input')) + +phase_space = [0, 1, 2, 3, 4].permutation(5) + +max_thrust = -Float::INFINITY +max_phases = nil +phase_space.each do |phases| + t = thrust(program, phases) + next unless t > max_thrust + + max_thrust = t + max_phases = phases +end + +pp max_phases +puts max_thrust