For a second year I did some exercises from advent of code.
Like last year I’ve started several days after the official beginning to avoid the feel of being part of a competition or to have a fixed schedule.
Most of the exercises have the same qualities I appreciated in last year ones: not very long and don’t require external knowledge, and especially no knowledge of specific algorithms.
And same for the things I disliked:
-
The writing theme that add flavor but makes some problems harder to understand.
-
Exercises that feels like busywork when you have known about their problem domain, for example if you already read things about compilers, roguelike development and git, you already know how to solve most of the cases.
-
Exercises with edge cases that are not tested in the provided samples so it makes harder to check if things work as expected.
I’ve stopped at problem 2 of day 18 because I’ve lost my interest in it, and I prefer to stop before it feels too much like work.
If you have comments or question about these please contact me.
Day 01
# @param [Integer] mass
def fuel(mass)
(mass.to_f / 3.0).floor - 2
end
assert_equal(2, fuel(12))
assert_equal(2, fuel(14))
assert_equal(654, fuel(1969))
assert_equal(33583, fuel(100756))
result = 0
File.foreach('input.txt') do |line|
result += fuel(line.strip.to_i)
end
p result
# @param [Integer] mass
def fuel(mass)
f = ((mass.to_f / 3.0).floor).to_i - 2
if f > 0
f + fuel(f)
else
0
end
end
assert_equal(2, fuel(14))
assert_equal(966, fuel(1969))
assert_equal(50346, fuel(100756))
result = 0
File.foreach('input.txt') do |line|
result += fuel(line.strip.to_i)
end
p result
Day 02
def interpret_instruction(memory, instruction_pointer)
opcode = memory[instruction_pointer]
case opcode
when 1 # add
memory[memory[instruction_pointer + 3]] =
(memory[memory[instruction_pointer + 1]] || 0) +
(memory[memory[instruction_pointer + 2]] || 0)
4
when 2 # multiply
memory[memory[instruction_pointer + 3]] =
(memory[memory[instruction_pointer + 1]] || 0) *
(memory[memory[instruction_pointer + 2]] || 0)
4
when 99
nil
else
raise opcode.to_s
end
end
def interpret_program(memory)
instruction_pointer = 0
while (delta = interpret_instruction(memory, instruction_pointer))
instruction_pointer += delta
end
memory
end
def interpret_instruction(memory, instruction_pointer)
opcode = memory[instruction_pointer]
case opcode
when 1 # add
memory[memory[instruction_pointer + 3]] =
(memory[memory[instruction_pointer + 1]] || 0) +
(memory[memory[instruction_pointer + 2]] || 0)
4
when 2 # multiply
memory[memory[instruction_pointer + 3]] =
(memory[memory[instruction_pointer + 1]] || 0) *
(memory[memory[instruction_pointer + 2]] || 0)
4
when 99
nil
else
raise opcode.to_s
end
end
def interpret_program(memory)
instruction_pointer = 0
while (delta = interpret_instruction(memory, instruction_pointer))
instruction_pointer += delta
end
memory
end
Day 03
def draw_path(grid, path, wire)
current_position = {line: 0, column: 0}
path.split(',').each do |path_fragment|
path_fragment_direction = path_fragment[0]
case path_fragment_direction
when 'R'
direction = {line: 0, column: 1}
when 'L'
direction = {line: 0, column: -1}
when 'U'
direction = {line: 1, column: 0}
when 'D'
direction = {line: -1, column: 0}
else
raise path_fragment_direction
end
number_of_steps = path_fragment[1..-1].to_i
number_of_steps.times do
current_position[:line] += +direction[:line]
current_position[:column] += +direction[:column]
grid[current_position[:line]][current_position[:column]] << wire
end
end
end
def process_graph(pathes)
grid = Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = [] } }
draw_path(grid, pathes[0], 'A')
draw_path(grid, pathes[1], 'B')
distance = 1
while true
distance.downto(0) do |line|
column = distance - line
if grid[line][column].uniq.length == 2
return distance
end
if grid[line][-column].uniq.length == 2
return distance
end
if grid[-line][column].uniq.length == 2
return distance
end
if grid[-line][-column].uniq.length == 2
return distance
end
end
distance += 1
end
end
def draw_path(grid, path, wire_index)
current_position = {line: 0, column: 0}
distance = 0
path.split(',').each do |path_fragment|
path_fragment_direction = path_fragment[0]
case path_fragment_direction
when 'R'
direction = {line: 0, column: 1}
when 'L'
direction = {line: 0, column: -1}
when 'U'
direction = {line: 1, column: 0}
when 'D'
direction = {line: -1, column: 0}
else
raise path_fragment_direction
end
number_of_steps = path_fragment[1..-1].to_i
number_of_steps.times do
distance += 1
current_position[:line] += +direction[:line]
current_position[:column] += +direction[:column]
position = grid[current_position[:line]][current_position[:column]]
if (!position[wire_index]) || (position[wire_index] > distance)
position[wire_index] = distance
end
end
end
end
def process_graph(pathes)
grid = Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = Array.new(2, nil) } }
draw_path(grid, pathes[0], 0)
draw_path(grid, pathes[1], 1)
minimum_distance = -1
grid.values.each do |v1|
v1.values.each do |v2|
if v2[0] && v2[1]
distance = v2[0] + v2[1]
if (minimum_distance == -1) || (distance < minimum_distance)
minimum_distance = distance
end
end
end
end
minimum_distance
end
Day 04
NUMBER_OF_DIGITS = 6
# @param [Integer] current_number
# @param [Integer] digit_index
# @param [Integer] last_number
def next_digit(current_number, digit_index, last_number, &block)
last_number.upto(9) do |digit|
n = current_number + (digit * (10 ** (5 - digit_index)))
if digit_index == (NUMBER_OF_DIGITS - 1)
yield(n)
else
next_digit(n, digit_index + 1, digit, &block)
end
end
end
result = 0
next_digit(0, 0, 0) do |candidate|
if(candidate >= 246515) && (candidate <= 739105) && (candidate.to_s.split(//).uniq.count < NUMBER_OF_DIGITS)
p candidate
result += 1
end
end
p ''
p result
NUMBER_OF_DIGITS = 6
# @param [Integer] current_number
# @param [Integer] digit_index
# @param [Integer] last_number
def next_digit(current_number, digit_index, last_number, &block)
last_number.upto(9) do |digit|
n = current_number + (digit * (10 ** (5 - digit_index)))
if digit_index == (NUMBER_OF_DIGITS - 1)
yield(n)
else
next_digit(n, digit_index + 1, digit, &block)
end
end
end
result = 0
next_digit(0, 0, 0) do |candidate|
if (candidate >= 246515) && (candidate <= 739105)
digit_counts = Hash.new(0)
candidate.to_s.split(//).each do |n|
digit_counts[n] += 1
end
if digit_counts.values.include? 2
p candidate
result += 1
end
end
end
p ''
p result
Day 05
# @param [Array<Integer>] memory
# @param [Integer] instruction_pointer
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(memory, instruction_pointer, instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
p " mode for #{parameter_index} is #{(instruction_mode == '0') ? 'position' : 'immediate'}"
parameter_value = memory[instruction_pointer + parameter_index + 1]
p " parameter for #{parameter_index} is #{parameter_value}"
case instruction_mode
when '0' # position
memory[parameter_value]
when '1' # immediate
parameter_value
else
raise instruction_mode
end
end
# @param [Array<Integer>] memory
# @param [Integer] instruction_pointer
# @param [Array<Integer>] io
# @return [Integer|nil]
def interpret_instruction(memory, instruction_pointer, io)
instruction = sprintf("%05d", memory[instruction_pointer])
p ''
p "instruction #{instruction}"
opcode = instruction[-2..-1]
case opcode
when '01' # add
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
p "add #{a} & #{b} to #{c}"
memory[c] = a + b
4
when '02' # multiply
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
p "multiply #{a} & #{b} to #{c}"
memory[c] = a * b
4
when '03' # input
a = memory[instruction_pointer + 1]
p "input at #{a}"
memory[a] = io.shift
2
when '04' # output
a = value(memory, instruction_pointer, instruction, 0)
p "output from #{a}"
io << a
2
when '99'
nil
else
raise opcode
end
end
# @param [Array<Integer>] memory
# @param [Array<Integer>] io
def interpret_program(memory, io)
instruction_pointer = 0
while (delta = interpret_instruction(memory, instruction_pointer, io))
instruction_pointer += delta
end
memory
end
# @param [Array<Integer>] memory
# @param [Integer] instruction_pointer
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(memory, instruction_pointer, instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory[instruction_pointer + parameter_index + 1]
case instruction_mode
when '0' # position
memory[parameter_value]
when '1' # immediate
parameter_value
else
raise instruction_mode
end
end
def print_instruction(
memory,
instruction_pointer,
instruction,
opcode,
number_of_params)
STDOUT << "#{opcode} #{instruction} #{memory[instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
# @param [Array<Integer>] memory
# @param [Integer] instruction_pointer
# @param [Array<Integer>] io
# @return [Integer|nil]
def interpret_instruction(memory, instruction_pointer, io)
instruction = sprintf("%05d", memory[instruction_pointer])
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(memory, instruction_pointer, instruction, 'add', 3)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
STDOUT << "Store #{a} + #{b} at #{c}\n"
memory[c] = a + b
instruction_pointer + 4
when '02' # multiply
print_instruction(memory, instruction_pointer, instruction, 'multiply', 3)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
STDOUT << "Store #{a} * #{b} at #{c}\n"
memory[c] = a * b
instruction_pointer + 4
when '03' # input
print_instruction(memory, instruction_pointer, instruction, 'input', 1)
a = memory[instruction_pointer + 1]
STDOUT << "Input at #{a}\n"
memory[a] = io.shift
instruction_pointer + 2
when '04' # output
print_instruction(memory, instruction_pointer, instruction, 'output', 1)
a = value(memory, instruction_pointer, instruction, 0)
STDOUT << "Output #{a}\n"
io << a
instruction_pointer + 2
when '05' # jump-if-true
print_instruction(memory, instruction_pointer, instruction, 'jump-if-true', 2)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
if a != 0
STDOUT << "Set instruction pointer to #{b} (#{a}, #{b})\n"
b
else
STDOUT << "Do nothing\n"
instruction_pointer + 3
end
when '06' # jump-if-false
print_instruction(memory, instruction_pointer, instruction, 'jump-if-false', 2)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
if a == 0
STDOUT << "Set instruction pointer to #{b} (#{a}, #{b})\n"
b
else
STDOUT << "Do nothing\n"
instruction_pointer + 3
end
when '07' # less-than
print_instruction(memory, instruction_pointer, instruction, 'less-than', 3)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
value = (a < b) ? 1 : 0
STDOUT << "Set #{value} at #{c} (#{a} < #{b})\n"
memory[c] = value
instruction_pointer + 4
when '08' # equals
print_instruction(memory, instruction_pointer, instruction, 'equals', 3)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
value = (a == b) ? 1 : 0
STDOUT << "Set #{value} at #{c} (#{a} == #{b})\n"
memory[c] = value
instruction_pointer + 4
when '99'
print_instruction(memory, instruction_pointer, instruction, 'exit', 0)
STDOUT << "Exit\n"
nil
else
raise opcode
end
end
# @param [Array<Integer>] memory
# @param [Array<Integer>] io
# @return [Array<Integer>]
def interpret_program(memory, io)
STDOUT << "\n"
instruction_pointer = 0
while (instruction_pointer = interpret_instruction(memory, instruction_pointer, io))
end
io
end
Day 06
def distance_to_com(parents, distance_to_center, element)
if distance_to_center.key?(element)
distance_to_center[element]
else
distance = distance_to_com(parents, distance_to_center, parents[element]) + 1
distance_to_center[element] = distance
distance
end
end
def calculate(file)
parents = {}
File.foreach(file) do |line|
orbit = line.strip.split(')')
parents[orbit[1]] = orbit[0]
end
total = 0
distance_to_center = {'COM' => 0}
parents.keys.each do |element|
total += distance_to_com(parents, distance_to_center, element)
end
total
end
def path_to_com(parents, element)
result = []
current_element = parents[element]
until current_element == 'COM'
result << current_element
current_element = parents[current_element]
end
result
end
def calculate(file)
parents = {}
File.foreach(file) do |line|
orbit = line.strip.split(')')
parents[orbit[1]] = orbit[0]
end
you_path = path_to_com(parents, 'YOU')
san_path = path_to_com(parents, 'SAN')
(you_path - san_path).length + (san_path - you_path).length
end
Day 07
# @param [Array<Integer>] memory
# @param [Integer] instruction_pointer
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(memory, instruction_pointer, instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory[instruction_pointer + parameter_index + 1]
case instruction_mode
when '0' # position
memory[parameter_value]
when '1' # immediate
parameter_value
else
raise instruction_mode
end
end
def print_instruction(
memory,
instruction_pointer,
instruction,
opcode,
number_of_params)
STDOUT << "#{opcode} #{instruction} #{memory[instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
# @param [Array<Integer>] memory
# @param [Integer] instruction_pointer
# @param [Array<Integer>] io
# @return [Integer|nil]
def interpret_instruction(memory, instruction_pointer, io)
instruction = sprintf("%05d", memory[instruction_pointer])
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(memory, instruction_pointer, instruction, 'add', 3)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
STDOUT << "Store #{a} + #{b} at #{c}\n"
memory[c] = a + b
instruction_pointer + 4
when '02' # multiply
print_instruction(memory, instruction_pointer, instruction, 'multiply', 3)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
STDOUT << "Store #{a} * #{b} at #{c}\n"
memory[c] = a * b
instruction_pointer + 4
when '03' # input
print_instruction(memory, instruction_pointer, instruction, 'input', 1)
a = memory[instruction_pointer + 1]
STDOUT << "Input at #{a}\n"
memory[a] = io.shift
instruction_pointer + 2
when '04' # output
print_instruction(memory, instruction_pointer, instruction, 'output', 1)
a = value(memory, instruction_pointer, instruction, 0)
STDOUT << "Output #{a}\n"
io << a
instruction_pointer + 2
when '05' # jump-if-true
print_instruction(memory, instruction_pointer, instruction, 'jump-if-true', 2)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
if a != 0
STDOUT << "Set instruction pointer to #{b} (#{a}, #{b})\n"
b
else
STDOUT << "Do nothing\n"
instruction_pointer + 3
end
when '06' # jump-if-false
print_instruction(memory, instruction_pointer, instruction, 'jump-if-false', 2)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
if a == 0
STDOUT << "Set instruction pointer to #{b} (#{a}, #{b})\n"
b
else
STDOUT << "Do nothing\n"
instruction_pointer + 3
end
when '07' # less-than
print_instruction(memory, instruction_pointer, instruction, 'less-than', 3)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
value = (a < b) ? 1 : 0
STDOUT << "Set #{value} at #{c} (#{a} < #{b})\n"
memory[c] = value
instruction_pointer + 4
when '08' # equals
print_instruction(memory, instruction_pointer, instruction, 'equals', 3)
a = value(memory, instruction_pointer, instruction, 0)
b = value(memory, instruction_pointer, instruction, 1)
c = memory[instruction_pointer + 3]
value = (a == b) ? 1 : 0
STDOUT << "Set #{value} at #{c} (#{a} == #{b})\n"
memory[c] = value
instruction_pointer + 4
when '99'
print_instruction(memory, instruction_pointer, instruction, 'exit', 0)
STDOUT << "Exit\n"
nil
else
raise opcode
end
end
# @param [Array<Integer>] memory
# @param [Array<Integer>] io
# @return [Array<Integer>]
def interpret_program(memory, io)
STDOUT << "\n"
instruction_pointer = 0
while (instruction_pointer = interpret_instruction(memory, instruction_pointer, io))
end
io
end
# @param [Array<Integer>] memory
# @param [Integer] phase_setting
# @param [Integer] input_signal
def run_program_on_amplifier(memory, phase_setting, input_signal)
io = [phase_setting, input_signal]
interpret_program(memory.dup, io)
io[0]
end
# @param [Array<Integer>] memory
# @param [Array<Integer>] phase_settings
def test_combination(memory, phase_settings)
o1 = run_program_on_amplifier(memory, phase_settings[0], 0)
o2 = run_program_on_amplifier(memory, phase_settings[1], o1)
o3 = run_program_on_amplifier(memory, phase_settings[2], o2)
o4 = run_program_on_amplifier(memory, phase_settings[3], o3)
o5 = run_program_on_amplifier(memory, phase_settings[4], o4)
o5
end
POSSIBLE_SETTINGS = [0, 1, 2, 3, 4]
# @param [Array<Integer>] memory
# @return [Hash]
def max_combination(memory)
max_thruster = 0
max_sequence = []
POSSIBLE_SETTINGS.each do |s1|
(POSSIBLE_SETTINGS - [s1]).each do |s2|
(POSSIBLE_SETTINGS - [s1, s2]).each do |s3|
(POSSIBLE_SETTINGS - [s1, s2, s3]).each do |s4|
(POSSIBLE_SETTINGS - [s1, s2, s3, s4]).each do |s5|
phase_settings = [s1, s2, s3, s4, s5]
result = test_combination(memory, phase_settings)
if result > max_thruster
max_thruster = result
max_sequence = phase_settings
end
end
end
end
end
end
{max_thruster: max_thruster, max_sequence: max_sequence}
end
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Integer] index
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(index, memory, io_in)
@index = index
@memory = memory
@io_in = io_in
@instruction_pointer = 0
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier #{@index} with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", @memory[@instruction_pointer])
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = @memory[@instruction_pointer + 3]
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = @memory[@instruction_pointer + 3]
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = @memory[@instruction_pointer + 1]
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = @memory[@instruction_pointer + 3]
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = @memory[@instruction_pointer + 3]
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = @memory[@instruction_pointer + parameter_index + 1]
case instruction_mode
when '0' # position
@memory[parameter_value]
when '1' # immediate
parameter_value
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
# @param [Array<Integer>] memory
# @param [Array<Integer>] phase_settings
# @return [Integer]
def test_combination(memory, phase_settings)
amplifiers = [
Amplifier.new(0, memory.dup, [phase_settings[0]]),
Amplifier.new(1, memory.dup, [phase_settings[1]]),
Amplifier.new(2, memory.dup, [phase_settings[2]]),
Amplifier.new(3, memory.dup, [phase_settings[3]]),
Amplifier.new(4, memory.dup, [phase_settings[4]])
]
io = [0]
current_amplifier_index = 0
last_thruster_signal = nil
while amplifiers[current_amplifier_index].status != Amplifier::STATUS_EXIT
io = amplifiers[current_amplifier_index].resume(io)
if current_amplifier_index == 4
last_thruster_signal = io.first
current_amplifier_index = 0
if LOG_INFO
STDOUT << "\n"
end
else
current_amplifier_index += 1
end
end
last_thruster_signal
end
POSSIBLE_SETTINGS = [5, 6, 7, 8, 9]
# @param [Array<Integer>] memory
# @return [Hash]
def max_combination(memory)
max_thruster = 0
max_sequence = []
POSSIBLE_SETTINGS.each do |s1|
(POSSIBLE_SETTINGS - [s1]).each do |s2|
(POSSIBLE_SETTINGS - [s1, s2]).each do |s3|
(POSSIBLE_SETTINGS - [s1, s2, s3]).each do |s4|
(POSSIBLE_SETTINGS - [s1, s2, s3, s4]).each do |s5|
phase_settings = [s1, s2, s3, s4, s5]
result = test_combination(memory, phase_settings)
if LOG_INFO
STDOUT << "\n"
end
if result > max_thruster
max_thruster = result
max_sequence = phase_settings
end
end
end
end
end
end
{max_thruster: max_thruster, max_sequence: max_sequence}
end
Day 08
INPUT = IO.read('input.txt')
LAYER_SIZE = 25 * 6
NUMBER_OF_LAYERS = INPUT.length / LAYER_SIZE
min_zeroes = LAYER_SIZE
min_zeroes_value = -1
0.upto(NUMBER_OF_LAYERS - 1) do |layer_index|
layer = INPUT[(LAYER_SIZE * layer_index), LAYER_SIZE]
zeroes = layer.count('0')
if zeroes < min_zeroes
min_zeroes = zeroes
min_zeroes_value = layer.count('1') * layer.count('2')
end
end
p min_zeroes_value
INPUT = IO.read('input.txt')
LAYER_SIZE = 25 * 6
NUMBER_OF_LAYERS = INPUT.length / LAYER_SIZE
TRANSPARENT_COLOR = '2'
def calculate_pixel(position)
current_layer_index = 0
while current_layer_index < NUMBER_OF_LAYERS
value = INPUT[(current_layer_index * LAYER_SIZE) + position]
STDOUT << "pos #{position} index #{current_layer_index} value #{value}\n"
if value != TRANSPARENT_COLOR
return value
end
current_layer_index += 1
end
TRANSPARENT_COLOR
end
result = []
0.upto(LAYER_SIZE - 1) do |position|
result << calculate_pixel(position)
end
0.upto(5) do |line_index|
STDOUT << "#{result[(line_index * 25), 25].join('').gsub('0', ' ').gsub('1', '#')}\n"
end
Day 09
LOG_INFO = true
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Integer] index
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(index, memory, io_in)
@index = index
@memory = memory
@io_in = io_in
@instruction_pointer = 0
@relative_base = 0
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier #{@index} with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
def run_amplifier(memory, io_in = [])
Amplifier.new(
0,
memory,
io_in)
.resume([])
end
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Integer] index
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(index, memory, io_in)
@index = index
@memory = memory
@io_in = io_in
@instruction_pointer = 0
@relative_base = 0
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier #{@index} with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
def run_amplifier(memory, io_in = [])
Amplifier.new(
0,
memory,
io_in)
.resume([])
end
Day 10
require 'set'
ASTEROID_CHAR = '#'
# @param [String] field
# @return [Hash]
def process(field)
splitted_field = field.split("\n")
lines_nb = splitted_field.length
columns_nb = splitted_field[0].length
asteroids = []
0.upto(lines_nb - 1) do |line|
0.upto(columns_nb - 1) do |column|
if splitted_field[line][column] == ASTEROID_CHAR
asteroids << {column: column, line: line}
end
end
end
p asteroids
max_asteroid_position = nil
max_asteroid_number = 0
asteroids.each_with_index do |from_asteroid, from_index|
STDOUT << "from #{from_asteroid}\n"
asteroids_with_delta_positive = Set.new
asteroids_with_delta_negative = Set.new
above = false
under = false
asteroids.each_with_index do |to_asteroid, to_index|
if from_index != to_index
STDOUT << " to #{to_asteroid} "
delta_column = from_asteroid[:column] - to_asteroid[:column]
delta_line = from_asteroid[:line] - to_asteroid[:line]
if delta_column > 0
STDOUT << "#{Rational(delta_line, delta_column)}\n"
asteroids_with_delta_positive << Rational(delta_line, delta_column)
elsif delta_column < 0
STDOUT << "#{Rational(delta_line, delta_column)}\n"
asteroids_with_delta_negative << Rational(delta_line, delta_column)
elsif delta_line > 0
STDOUT << "under\n"
above = true
else
STDOUT << "above\n"
under = true
end
end
end
visible_number = asteroids_with_delta_positive.length + asteroids_with_delta_negative.length + (above ? 1 : 0) + (under ? 1 : 0)
STDOUT << "Can see #{visible_number} #{asteroids_with_delta_positive} #{asteroids_with_delta_negative} #{above} #{under}\n\n"
if visible_number > max_asteroid_number
max_asteroid_number = visible_number
max_asteroid_position = from_asteroid
end
end
max_asteroid_position.merge({visible: max_asteroid_number})
end
require 'set'
ASTEROID_CHAR = '#'
# @param [String] field
# @return [Array]
def process(field, station_column, station_line)
splitted_field = field.split("\n")
lines_nb = splitted_field.length
columns_nb = splitted_field[0].length
number_of_asteroids = 0
left_quadrant = Hash.new { |hash, key| hash[key] = [] }
right_quadrant = Hash.new { |hash, key| hash[key] = [] }
above = []
under = []
0.upto(lines_nb - 1) do |line|
0.upto(columns_nb - 1) do |column|
if splitted_field[line][column] == ASTEROID_CHAR
if (line != station_line) || (column != station_column)
number_of_asteroids += 1
asteroid = {
column: column,
line: line,
distance: ((line - station_line) ** 2) + ((column - station_column) ** 2)
}
delta_column = station_column - column
delta_line = station_line - line
if delta_column > 0
#STDOUT << "#{Rational(delta_line, delta_column)}\n"
left_quadrant[Rational(delta_line, delta_column)] << asteroid
elsif delta_column < 0
#STDOUT << "#{Rational(delta_line, delta_column)}\n"
right_quadrant[Rational(delta_line, delta_column)] << asteroid
elsif delta_line > 0
#STDOUT << "under\n"
above << asteroid
else
#STDOUT << "above\n"
under << asteroid
end
end
end
end
end
(right_quadrant.values + left_quadrant.values + [above, under]).each do |asteriods|
asteriods.sort_by!{|a| a[:distance]}
end
result = []
quadrants = [
{0 => above},
right_quadrant,
{0 => under},
left_quadrant
]
p above
p right_quadrant
p under
p left_quadrant
while result.length < number_of_asteroids
quadrants.each do |quadrant|
quadrant.keys.sort.each do |angle|
asteroids = quadrant[angle]
result << asteroids.shift
if asteroids.empty?
quadrant.delete(angle)
end
end
end
end
result
end
Day 11
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(memory, io_in)
@memory = memory
@io_in = io_in
@instruction_pointer = 0
@relative_base = 0
@status = :can_continue
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
COLOR_BLACK = 0
COLOR_WHITE = 1
current_line = 0
current_column = 0
current_direction = :up
hull = Hash.new { |hash, key| hash[key] = {} }
memory = IO.read('input.txt').split(',').map(&:to_i)
amplifier = Amplifier.new(
memory,
[])
until amplifier.status == Amplifier::STATUS_EXIT
out = amplifier.resume([hull[current_line][current_column] || COLOR_BLACK])
amplifier.status == Amplifier::STATUS_NEED_INPUT
hull[current_line][current_column] = out[0]
case out[1]
when 0
current_direction = {
up: :left,
left: :down,
down: :right,
right: :up
}[current_direction]
when 1
current_direction = {
up: :right,
right: :down,
down: :left,
left: :up
}[current_direction]
else
raise out[1].to_s
end
case current_direction
when :up
current_line -= 1
when :down
current_line += 1
when :right
current_column += 1
when :left
current_column -= 1
else
raise current_direction
end
end
p hull.values.collect{|v| v.length}.sum
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(memory, io_in)
@memory = memory
@io_in = io_in
@instruction_pointer = 0
@relative_base = 0
@status = :can_continue
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
COLOR_BLACK = 0
COLOR_WHITE = 1
current_line = 0
current_column = 0
current_direction = :up
hull = Hash.new { |hash, key| hash[key] = {} }
hull[0][0] = COLOR_WHITE
memory = IO.read('input.txt').split(',').map(&:to_i)
amplifier = Amplifier.new(
memory,
[])
until amplifier.status == Amplifier::STATUS_EXIT
out = amplifier.resume([hull[current_line][current_column] || COLOR_BLACK])
amplifier.status == Amplifier::STATUS_NEED_INPUT
hull[current_line][current_column] = out[0]
case out[1]
when 0
current_direction = {
up: :left,
left: :down,
down: :right,
right: :up
}[current_direction]
when 1
current_direction = {
up: :right,
right: :down,
down: :left,
left: :up
}[current_direction]
else
raise out[1].to_s
end
case current_direction
when :up
current_line -= 1
when :down
current_line += 1
when :right
current_column += 1
when :left
current_column -= 1
else
raise current_direction
end
end
min_line = hull.keys.min
max_line = hull.keys.max
min_column = hull.values.collect{|v| v.keys.min}.min
max_column = hull.values.collect{|v| v.keys.max}.max
min_line.upto(max_line) do |line|
min_column.upto(max_column) do |column|
STDOUT << (hull[line][column] || COLOR_BLACK)
end
STDOUT << "\n"
end
Day 12
MOON_REGEX = /\A<x=(?<x>[-\d]+), y=(?<y>[-\d]+), z=(?<z>[-\d]+)>\z/
def process(content, steps)
moons = content.split("\n").collect do |l|
m = MOON_REGEX.match(l)
{
pos: [m['x'].to_i, m['y'].to_i, m['z'].to_i],
vel: [0, 0, 0]
}
end
steps.times do
p moons
moons.each_with_index do |from_moon, from_i|
moons.each_with_index do |to_moon, to_i|
if from_i != to_i
0.upto(2) do |coordinate_index|
if from_moon[:pos][coordinate_index] != to_moon[:pos][coordinate_index]
delta = (to_moon[:pos][coordinate_index] > from_moon[:pos][coordinate_index]) ? 1 : -1
from_moon[:vel][coordinate_index] += delta
end
end
end
end
end
moons.each do |moon|
0.upto(2) do |coordinate_index|
moon[:pos][coordinate_index] += moon[:vel][coordinate_index]
end
end
end
p moons
moons.collect do |moon|
moon[:pos].collect(&:abs).sum * moon[:vel].collect(&:abs).sum
end.sum
end
MOON_REGEX = /\A<x=(?<x>[-\d]+), y=(?<y>[-\d]+), z=(?<z>[-\d]+)>\z/
def process(content)
moons = content.split("\n").collect do |l|
m = MOON_REGEX.match(l)
[
m['x'].to_i, 0,
m['y'].to_i, 0,
m['z'].to_i, 0
]
end
steps = [
{},
{},
{}
]
steps[0][moons.collect { |m| [m[0], m[1]] }.join('|')] = true
steps[1][moons.collect { |m| [m[2], m[3]] }.join('|')] = true
steps[2][moons.collect { |m| [m[4], m[5]] }.join('|')] = true
cycles = [nil, nil, nil]
while cycles.include?(nil) do
moons = moons.collect { |m| m.dup }
moons.each_with_index do |from_moon, from_i|
moons.each_with_index do |to_moon, to_i|
if from_i != to_i
0.upto(2) do |coordinate_index|
if from_moon[2 * coordinate_index] != to_moon[2 * coordinate_index]
delta = (to_moon[2 * coordinate_index] > from_moon[2 * coordinate_index]) ? 1 : -1
from_moon[(2 * coordinate_index) + 1] += delta
end
end
end
end
end
moons.each do |moon|
0.upto(2) do |coordinate_index|
moon[2 * coordinate_index] += moon[(2 * coordinate_index) + 1]
end
end
0.upto(2) do |coordinate_index|
unless cycles[coordinate_index]
snapshot = moons.collect { |m| [m[2 * coordinate_index], m[(2 * coordinate_index) + 1]] }.join('|')
if steps[coordinate_index].key?(snapshot)
p "Cycle detected for #{coordinate_index}"
cycles[coordinate_index] = steps[coordinate_index].length
else
steps[coordinate_index][snapshot] = true
end
end
end
# p cycles
end
cycles[0].lcm(cycles[1]).lcm(cycles[2])
end
Day 13
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(memory, io_in)
@memory = memory
@io_in = io_in
@instruction_pointer = 0
@relative_base = 0
@status = :can_continue
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
COLOR_BLACK = 0
COLOR_WHITE = 1
current_line = 0
current_column = 0
current_direction = :up
hull = Hash.new { |hash, key| hash[key] = {} }
hull[0][0] = COLOR_WHITE
memory = IO.read('input.txt').split(',').map(&:to_i)
amplifier = Amplifier.new(
memory,
[])
until amplifier.status == Amplifier::STATUS_EXIT
out = amplifier.resume([hull[current_line][current_column] || COLOR_BLACK])
amplifier.status == Amplifier::STATUS_NEED_INPUT
hull[current_line][current_column] = out[0]
case out[1]
when 0
current_direction = {
up: :left,
left: :down,
down: :right,
right: :up
}[current_direction]
when 1
current_direction = {
up: :right,
right: :down,
down: :left,
left: :up
}[current_direction]
else
raise out[1].to_s
end
case current_direction
when :up
current_line -= 1
when :down
current_line += 1
when :right
current_column += 1
when :left
current_column -= 1
else
raise current_direction
end
end
screen = []
memory = IO.read('input.txt').split(',').map(&:to_i)
amplifier = Amplifier.new(
memory,
[])
output = amplifier.resume([])
0.upto((output.length / 3) - 1) do |i|
x = output[3 * i]
y = output[(3 * i) + 1]
id = output[(3 * i) + 2]
unless screen[y]
screen[y] = []
end
screen[y][x] = id
end
STDOUT << (screen.collect{|l| l.join('')}.join("\n") + "\n")
p screen.collect{|l| l.count(2)}.sum
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(memory, io_in)
@memory = memory
@io_in = io_in
@instruction_pointer = 0
@relative_base = 0
@status = :can_continue
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
COLOR_BLACK = 0
COLOR_WHITE = 1
current_line = 0
current_column = 0
current_direction = :up
hull = Hash.new { |hash, key| hash[key] = {} }
hull[0][0] = COLOR_WHITE
memory = IO.read('input.txt').split(',').map(&:to_i)
amplifier = Amplifier.new(
memory,
[])
until amplifier.status == Amplifier::STATUS_EXIT
out = amplifier.resume([hull[current_line][current_column] || COLOR_BLACK])
amplifier.status == Amplifier::STATUS_NEED_INPUT
hull[current_line][current_column] = out[0]
case out[1]
when 0
current_direction = {
up: :left,
left: :down,
down: :right,
right: :up
}[current_direction]
when 1
current_direction = {
up: :right,
right: :down,
down: :left,
left: :up
}[current_direction]
else
raise out[1].to_s
end
case current_direction
when :up
current_line -= 1
when :down
current_line += 1
when :right
current_column += 1
when :left
current_column -= 1
else
raise current_direction
end
end
def read_char
begin
system("stty raw -echo")
str = STDIN.getc
ensure
system("stty -raw echo")
end
str
end
def print_info(screen, score)
0.upto((output.length / 3) - 1) do |i|
x = output[3 * i]
y = output[(3 * i) + 1]
id = output[(3 * i) + 2]
if (x == -1) && (y == 0)
score = id
else
unless screen[y]
screen[y] = []
end
screen[y][x] = id
end
end
STDOUT << "#{screen.collect { |l| l.join('') }.join("\n")}\n#{score}\n"
end
memory = IO.read('input.txt').split(',').map(&:to_i)
memory[0] = 2
amplifier = Amplifier.new(
memory,
[])
screen = []
current_score = -1
max_score = -1
next_direction = 0
while true
output = amplifier.resume([next_direction])
0.upto((output.length / 3) - 1) do |i|
x = output[3 * i]
y = output[(3 * i) + 1]
id = output[(3 * i) + 2]
if (x == -1) && (y == 0)
current_score = id
if current_score > max_score
max_score = current_score
end
else
unless screen[y]
screen[y] = []
end
screen[y][x] = id
end
end
STDOUT << "\e[H\e[2J"
STDOUT << "#{screen.collect { |l| l.join('').gsub('0', ' ') }.join("\n")}\n\n#{current_score}\n#{max_score}"
ball_column = screen.find { |l| l.include?(4) }.index(4)
paddle_column = screen.find { |l| l.include?(3) }.index(3)
if ball_column < paddle_column
next_direction = -1
elsif paddle_column < ball_column
next_direction = 1
else
next_direction = 0
end
sleep(0.01)
end
Day 14
ELEMENT_REGEX = /\A\s*(?<quantity>\d+) (?<element>[A-Z]+)\s*\z/
def parse_element(s)
m = ELEMENT_REGEX.match(s)
{
element: m['element'],
quantity: m['quantity'].to_i
}
end
FUEL = 'FUEL'
ORE = 'ORE'
def find_rank(element, recipes)
recipe = recipes[element]
recipe[:rank] = (recipe[:requirements].keys.collect do |required_element|
if required_element == ORE
0
else
find_rank(required_element, recipes)
end
end.max + 1)
end
# @param [String] input_recipes
# @return [Integer]
def process(input_recipes)
p ''
recipes = {}
input_recipes.split("\n").each do |input_recipe|
parts = input_recipe.split('=>')
result = parse_element(parts[1])
requirements_array = parts[0].split(',').collect { |e| parse_element(e) }
requirements_hash = {}
requirements_array.each do |r|
requirements_hash[r[:element]] = r[:quantity]
end
recipes[result[:element]] = {
quantity: result[:quantity],
requirements: requirements_hash
}
end
recipes.keys.each do |element|
find_rank(element, recipes)
end
p recipes
elements_requirements = recipes.delete(FUEL)[:requirements]
ore_requirements = 0
until elements_requirements.empty?
p "Currently #{elements_requirements.length} requirements #{elements_requirements}"
required_element = elements_requirements.keys.sort_by { |element| recipes[element][:rank] }.last
required_quantity = elements_requirements.delete(required_element)
p "Need #{required_quantity} of #{required_element}"
recipe = recipes.delete(required_element)
p "Recipe #{recipe}"
recipe_quantity = (required_quantity.to_f / recipe[:quantity].to_f).ceil
p "Recipes will be run #{recipe_quantity} times"
recipe[:requirements].each_pair do |el, quant|
required_quant = recipe_quantity * quant
p "Require #{required_quant} of #{el}"
if el == ORE
ore_requirements += required_quant
else
elements_requirements[el] = (elements_requirements[el] || 0) + required_quant
end
end
end
ore_requirements
end
ELEMENT_REGEX = /\A\s*(?<quantity>\d+) (?<element>[A-Z]+)\s*\z/
def parse_element(s)
m = ELEMENT_REGEX.match(s)
{
element: m['element'],
quantity: m['quantity'].to_i
}
end
FUEL = 'FUEL'
ORE = 'ORE'
def find_rank(element, recipes)
recipe = recipes[element]
recipe[:rank] = (recipe[:requirements].keys.collect do |required_element|
if required_element == ORE
0
else
find_rank(required_element, recipes)
end
end.max + 1)
end
# @param [String] input_recipes
def prepare_recipes(input_recipes)
recipes = {}
input_recipes.split("\n").each do |input_recipe|
parts = input_recipe.split('=>')
result = parse_element(parts[1])
requirements_array = parts[0].split(',').collect { |e| parse_element(e) }
requirements_hash = {}
requirements_array.each do |r|
requirements_hash[r[:element]] = r[:quantity]
end
recipes[result[:element]] = {
quantity: result[:quantity],
requirements: requirements_hash
}
end
recipes.keys.each do |element|
find_rank(element, recipes)
end
recipes
end
# @return [Integer]
def process(recipes, number_of_fuel)
elements_requirements = {}
recipes[FUEL][:requirements].each_pair do |k, v|
elements_requirements[k] = v * number_of_fuel
end
ore_requirements = 0
until elements_requirements.empty?
required_element = elements_requirements.keys.sort_by { |element| recipes[element][:rank] }.last
required_quantity = elements_requirements.delete(required_element)
recipe = recipes[required_element]
recipe_quantity = (required_quantity.to_f / recipe[:quantity].to_f).ceil
recipe[:requirements].each_pair do |el, quant|
required_quant = recipe_quantity * quant
if el == ORE
ore_requirements += required_quant
else
elements_requirements[el] = (elements_requirements[el] || 0) + required_quant
end
end
end
ore_requirements
end
TOTAL_ORE = 1000000000000
def process2(input_recipes)
recipes = prepare_recipes(input_recipes)
current_max = 1
current_min = current_max
while process(recipes, current_max) <= TOTAL_ORE
current_min = current_max
current_max = current_max * 2
end
p '!'
p current_max
while current_max != (current_min + 1)
new_value = (current_min + current_max) / 2
p new_value
ore = process(recipes, new_value)
if ore < TOTAL_ORE
current_min = new_value
p "Under"
else
current_max = new_value
p "Above"
end
end
current_min
end
Day 15
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(memory, io_in)
@memory = memory
@io_in = io_in
@instruction_pointer = 0
@relative_base = 0
@status = :can_continue
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
MOVEMENT_NORTH = 1
MOVEMENT_SOUTH = 2
MOVEMENT_EAST = 3
MOVEMENT_WEST = 4
RESULT_WALL = 0
RESULT_MOVED = 1
RESULT_FOUND = 2
memory = IO.read('input.txt').split(',').map(&:to_i)
places_to_try = []
map = Hash.new { |hash, key| hash[key] = {} }
places_to_try << {
path: [MOVEMENT_NORTH],
target_line: -1,
target_column: 0
}
places_to_try << {
path: [MOVEMENT_SOUTH],
target_line: 1,
target_column: 0
}
places_to_try << {
path: [MOVEMENT_EAST],
target_line: 0,
target_column: 1
}
places_to_try << {
path: [MOVEMENT_WEST],
target_line: 0,
target_column: -1
}
def draw_map(map)
min_line = map.keys.min
max_line = map.keys.max
min_column = map.values.collect{|v| v.keys.min}.compact.min
max_column = map.values.collect{|v| v.keys.max}.compact.max
min_line.upto(max_line) do |line|
min_column.upto(max_column) do |column|
STDOUT << (map[line][column] || ' ')
end
STDOUT << "\n"
end
end
until places_to_try.empty? do
place_to_try = places_to_try.shift
p place_to_try
unless map[place_to_try[:target_line]].key?(place_to_try[:target_column])
amplifier = Amplifier.new(memory.dup, place_to_try[:path].dup)
output = amplifier.resume([])
p output
status = output.last
map[place_to_try[:target_line]][place_to_try[:target_column]] = status
draw_map(map)
case status
when RESULT_FOUND
p "#{place_to_try[:path].length} #{place_to_try[:path]}"
exit
when RESULT_MOVED
[
{direction: MOVEMENT_NORTH, delta_line: -1, delta_column: 0},
{direction: MOVEMENT_SOUTH, delta_line: 1, delta_column: 0},
{direction: MOVEMENT_EAST, delta_line: 0, delta_column: 1},
{direction: MOVEMENT_WEST, delta_line: 0, delta_column: -1}
].each do |possible_move|
target_line = place_to_try[:target_line] + possible_move[:delta_line]
target_column = place_to_try[:target_column] + possible_move[:delta_column]
unless map[target_line].key?(target_column)
p "Will try (#{target_line}, #{target_column})"
places_to_try << {
path: place_to_try[:path] + [possible_move[:direction]],
target_line: target_line,
target_column: target_column
}
end
end
end
end
end
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(memory, io_in, instruction_pointer = 0, relative_base = 0, status = :can_continue)
@memory = memory
@io_in = io_in
@instruction_pointer = instruction_pointer
@relative_base = relative_base
@status = status
end
def dup
Amplifier.new(
@memory.dup,
@io_in.dup,
@instruction_pointer,
@relative_base,
@status
)
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
MOVEMENT_NORTH = 1
MOVEMENT_SOUTH = 2
MOVEMENT_EAST = 3
MOVEMENT_WEST = 4
RESULT_WALL = 0
RESULT_MOVED = 1
RESULT_FOUND = 2
memory = IO.read('input.txt').split(',').map(&:to_i)
places_to_try = []
map = Hash.new { |hash, key| hash[key] = {} }
initial_amplifier = Amplifier.new(memory.dup, [])
places_to_try << {
path: [MOVEMENT_NORTH],
target_line: -1,
target_column: 0,
amplifier: initial_amplifier
}
places_to_try << {
path: [MOVEMENT_SOUTH],
target_line: 1,
target_column: 0,
amplifier: initial_amplifier
}
places_to_try << {
path: [MOVEMENT_EAST],
target_line: 0,
target_column: 1,
amplifier: initial_amplifier
}
places_to_try << {
path: [MOVEMENT_WEST],
target_line: 0,
target_column: -1,
amplifier: initial_amplifier
}
def draw_map(map)
min_line = map.keys.min
max_line = map.keys.max
min_column = map.values.collect { |v| v.keys.min }.compact.min
max_column = map.values.collect { |v| v.keys.max }.compact.max
min_line.upto(max_line) do |line|
min_column.upto(max_column) do |column|
if map[line].key?(column)
v = map[line][column]
if v < 0
STDOUT << 'O'
else
STDOUT << ['#', '.', '2'][v]
end
else
STDOUT << '#'
end
end
STDOUT << "\n"
end
end
max_path = 0
oxygen_system_line = nil
oxygen_system_column = nil
until places_to_try.empty? do
place_to_try = places_to_try.shift
unless map[place_to_try[:target_line]].key?(place_to_try[:target_column])
amplifier = place_to_try[:amplifier].dup
output = amplifier.resume([place_to_try[:path].last])
status = output.last
map[place_to_try[:target_line]][place_to_try[:target_column]] = status
case status
when RESULT_WALL
else
if (status == RESULT_FOUND)
oxygen_system_line = place_to_try[:target_line]
oxygen_system_column = place_to_try[:target_column]
end
if place_to_try[:path].length > max_path
max_path = place_to_try[:path].length
end
[
{direction: MOVEMENT_NORTH, delta_line: -1, delta_column: 0},
{direction: MOVEMENT_SOUTH, delta_line: 1, delta_column: 0},
{direction: MOVEMENT_EAST, delta_line: 0, delta_column: 1},
{direction: MOVEMENT_WEST, delta_line: 0, delta_column: -1}
].each do |possible_move|
target_line = place_to_try[:target_line] + possible_move[:delta_line]
target_column = place_to_try[:target_column] + possible_move[:delta_column]
unless map[target_line].key?(target_column)
places_to_try << {
path: place_to_try[:path] + [possible_move[:direction]],
target_line: target_line,
target_column: target_column,
amplifier: amplifier
}
end
end
end
end
end
draw_map(map)
map[oxygen_system_line][oxygen_system_column] = -1
draw_map(map)
number_of_minutes = 0
while true
number_of_minutes += 1
at_least_one_location_filled = false
map.each_pair do |target_line, line_locations|
line_locations.each do |target_column, location_value|
if location_value == RESULT_MOVED
if ((map[target_line - 1][target_column] == - number_of_minutes) ||
(map[target_line + 1][target_column] == - number_of_minutes) ||
(map[target_line][target_column - 1] == - number_of_minutes) ||
(map[target_line][target_column + 1] == - number_of_minutes)
)
map[target_line][target_column] = - number_of_minutes - 1
at_least_one_location_filled = true
end
end
end
end
p ""
p number_of_minutes
draw_map(map)
unless at_least_one_location_filled
p number_of_minutes - 1
exit
end
end
Day 16
BASE_PATTERN = [0, 1, 0, -1]
def calculate_factor(current_index, target_index)
# For the element at index n, the pattern length is 4 * (target_index + 1)
pattern_size = 4 * (current_index + 1)
# Detect if we are on the first pattern instance (because it's shorter by one)
if target_index < pattern_size
# We're on the first pattern instance
index_on_pattern = (target_index + 1) % pattern_size
else
# We're not on the first pattern instance
rebased_index = target_index - (pattern_size - 1)
index_on_pattern = rebased_index % pattern_size
end
BASE_PATTERN[index_on_pattern / (current_index + 1)]
end
# @param [Integer] number_of_phases
# @param [Array<Integer>] input
# @return [Array<Integer>]
def calculate(input, number_of_phases)
number_of_phases.times do
output = Array.new(input.length, nil)
0.upto(input.length - 1) do |current_index|
current_output_value = 0
0.upto(input.length - 1) do |target_index|
factor = calculate_factor(current_index, target_index)
current_input_value = input[target_index]
current_output_value += factor * current_input_value
end
output[current_index] = current_output_value.abs % 10
end
input = output
end
input
end
BASE_PATTERN = [0, 1, 0, -1]
def calculate_factor(current_index, target_index)
# For the element at index n, the pattern length is 4 * (target_index + 1)
pattern_size = 4 * (current_index + 1)
# Detect if we are on the first pattern instance (because it's shorter by one)
if target_index < pattern_size
# We're on the first pattern instance
index_on_pattern = (target_index + 1) % pattern_size
else
# We're not on the first pattern instance
rebased_index = target_index - (pattern_size - 1)
index_on_pattern = rebased_index % pattern_size
end
BASE_PATTERN[index_on_pattern / (current_index + 1)]
end
# @param [String] s_input
# @return [String]
def calculate(s_input)
delta = s_input[0, 7].to_i
input = (s_input.split('').map(&:to_i) * 10000)[delta..-1]
100.times do
value = 0
(input.length - 1).downto(0) do |index|
value += input[index]
value = value.abs % 10
input[index] = value
end
end
input[0, 8].join('')
end
Day 17
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(memory, io_in, instruction_pointer = 0, relative_base = 0, status = :can_continue)
@memory = memory
@io_in = io_in
@instruction_pointer = instruction_pointer
@relative_base = relative_base
@status = status
end
def dup
Amplifier.new(
@memory.dup,
@io_in.dup,
@instruction_pointer,
@relative_base,
@status
)
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
amplifier = Amplifier.new(IO.read('input.txt').split(',').map(&:to_i), [])
output = amplifier.resume([])
map = output.map(&:chr).join("").split("\n")
map_width = map.first.length
map_height = map.length
total = 0
1.upto(map_width - 2) do |column|
1.upto(map_height - 2) do |line|
if (map[line][column] == '#') &&
(map[line - 1][column] == '#') &&
(map[line + 1][column] == '#') &&
(map[line][column - 1] == '#') &&
(map[line][column + 1] == '#')
p "#{line}, #{column} is an intersection"
total += (line * column)
end
end
end
p total
LOG_INFO = false
class Amplifier
STATUS_EXIT = :exit
STATUS_NEED_INPUT = :need_input
STATUS_CAN_CONTINUE = :can_continue
attr_reader :status
# @param [Array<Integer>] memory
# @param [Array<Integer>] io_in
def initialize(memory, io_in, instruction_pointer = 0, relative_base = 0, status = :can_continue)
@memory = memory
@io_in = io_in
@instruction_pointer = instruction_pointer
@relative_base = relative_base
@status = status
end
def dup
Amplifier.new(
@memory.dup,
@io_in.dup,
@instruction_pointer,
@relative_base,
@status
)
end
# @param [Array<Integer>] input
# @return [Array<Integer>|nil]
def resume(input)
io_out = []
@io_in.concat(input)
if LOG_INFO
STDOUT << "\nResume amplifier with pointer at #{@instruction_pointer} with input #{@io_in}\n"
end
while true
@status = interpret_instruction(io_out)
case status
when STATUS_NEED_INPUT
if LOG_INFO
STDOUT << "Input needed, output is #{io_out} and pointer at #{@instruction_pointer}\n"
end
return io_out
when STATUS_EXIT
if LOG_INFO
STDOUT << "Amplifier stopped\n"
end
return io_out
when STATUS_CAN_CONTINUE
else
raise result
end
end
end
private
# @param [Integer] index
# @return [Integer]
def memory(index)
@memory[index] || 0
end
# @param [Array<Integer>] io_out
# @return [Symbol] STATUS_EXIT, STATUS_NEED_INPUT or STATUS_CAN_CONTINUE
def interpret_instruction(io_out)
instruction = sprintf("%05d", memory(@instruction_pointer))
opcode = instruction[-2..-1]
case opcode
when '01' # add
print_instruction(instruction, 'add', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} + #{b}) at #{c}")
@memory[c] = a + b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '02' # multiply
print_instruction(instruction, 'multiply', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
log_instruction("Store (#{a} * #{b}) at #{c}")
@memory[c] = a * b
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '03' # input
print_instruction(instruction, 'input', 1)
if @io_in.empty?
return STATUS_NEED_INPUT
end
a = s_value(instruction, 0)
log_instruction("Store input at #{a}")
@memory[a] = @io_in.shift
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '04' # output
print_instruction(instruction, 'output', 1)
a = value(instruction, 0)
log_instruction("Output #{a}")
io_out << a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '05' # jump-if-true
print_instruction(instruction, 'jump-if-true', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a != 0
log_instruction("Set instruction pointer to #{b} (#{a} != 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} == 0)")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '06' # jump-if-false
print_instruction(instruction, 'jump-if-false', 2)
a = value(instruction, 0)
b = value(instruction, 1)
if a == 0
log_instruction("Set instruction pointer to #{b} (#{a} == 0)")
@instruction_pointer = b
else
log_instruction("Do nothing (#{a} != #{0})")
@instruction_pointer += 3
end
STATUS_CAN_CONTINUE
when '07' # less-than
print_instruction(instruction, 'less-than', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a < b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} < #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '08' # equals
print_instruction(instruction, 'equals', 3)
a = value(instruction, 0)
b = value(instruction, 1)
c = s_value(instruction, 2)
value = (a == b) ? 1 : 0
log_instruction("Set #{value} at #{c} (#{a} == #{b})")
@memory[c] = value
@instruction_pointer += 4
STATUS_CAN_CONTINUE
when '09' # adjust-relative-base
print_instruction(instruction, 'adjust-relative-base', 1)
a = value(instruction, 0)
log_instruction("Adjust relative base #{@relative_base} + #{a}")
@relative_base += a
@instruction_pointer += 2
STATUS_CAN_CONTINUE
when '99'
print_instruction(instruction, 'exit', 0)
log_instruction("Exit")
STATUS_EXIT
else
raise opcode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def s_value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
parameter_value
when '1' # position
parameter_value
when '2' # relative
parameter_value + @relative_base
else
raise instruction_mode
end
end
# @param [String] instruction
# @param [Integer] parameter_index
# @return [Integer]
def value(instruction, parameter_index)
instruction_mode = instruction[-(3 + parameter_index)]
parameter_value = memory(@instruction_pointer + parameter_index + 1)
case instruction_mode
when '0' # position
memory(parameter_value)
when '1' # immediate
parameter_value
when '2' # relative
memory(parameter_value + @relative_base)
else
raise instruction_mode
end
end
def print_instruction(
instruction,
opcode,
number_of_params)
if LOG_INFO
STDOUT << "#{opcode} #{instruction} #{@memory[@instruction_pointer + 1, number_of_params].join(', ')}".ljust(40)
end
end
def log_instruction(message)
if LOG_INFO
STDOUT << "#{message}\n"
end
end
end
amplifier = Amplifier.new(IO.read('input.txt').split(',').map(&:to_i), [])
output = amplifier.resume([])
map = output.map(&:chr).join("").split("\n")
map_width = map.first.length
map_height = map.length
number_of_intersections = 0
1.upto(map_width - 2) do |column|
1.upto(map_height - 2) do |line|
if (map[line][column] == '#') &&
(map[line - 1][column] == '#') &&
(map[line + 1][column] == '#') &&
(map[line][column - 1] == '#') &&
(map[line][column + 1] == '#')
p "#{line}, #{column} is an intersection"
# map[line][column] = 'x'
number_of_intersections += 0
end
end
end
robot_line = map.index { |l| l.include?('^') }
robot_column = map[robot_line].index('^')
number_of_standard_scaffolds = map.collect { |l| l.count('#') }.sum - number_of_intersections
DIRECTION_NORTH = 'N'
DIRECTION_SOUTH = 'S'
DIRECTION_EAST = 'E'
DIRECTION_WEST = 'W'
DELTA_LINE = {
DIRECTION_NORTH => -1,
DIRECTION_SOUTH => 1,
DIRECTION_EAST => 0,
DIRECTION_WEST => 0,
}
DELTA_COLUMN = {
DIRECTION_NORTH => 0,
DIRECTION_SOUTH => 0,
DIRECTION_EAST => 1,
DIRECTION_WEST => -1,
}
DELTA_LINE_TURNING_RIGHT = {
DIRECTION_NORTH => 0,
DIRECTION_SOUTH => 0,
DIRECTION_EAST => 1,
DIRECTION_WEST => -1,
}
DELTA_COLUMN_TURNING_RIGHT = {
DIRECTION_NORTH => 1,
DIRECTION_SOUTH => -1,
DIRECTION_EAST => 0,
DIRECTION_WEST => 0,
}
DELTA_LINE_TURNING_LEFT = {
DIRECTION_NORTH => 0,
DIRECTION_SOUTH => 0,
DIRECTION_EAST => -1,
DIRECTION_WEST => 1,
}
DELTA_COLUMN_TURNING_LEFT = {
DIRECTION_NORTH => -1,
DIRECTION_SOUTH => 1,
DIRECTION_EAST => 0,
DIRECTION_WEST => 0,
}
DIRECTION_TURNING_LEFT = {
DIRECTION_NORTH => DIRECTION_WEST,
DIRECTION_SOUTH => DIRECTION_EAST,
DIRECTION_EAST => DIRECTION_NORTH,
DIRECTION_WEST => DIRECTION_SOUTH,
}
DIRECTION_TURNING_RIGHT = {
DIRECTION_NORTH => DIRECTION_EAST,
DIRECTION_SOUTH => DIRECTION_WEST,
DIRECTION_EAST => DIRECTION_SOUTH,
DIRECTION_WEST => DIRECTION_NORTH,
}
def can_go_on?(line, column, map_height, map_width, map)
(line >= 0) && (column >= 0) && (line < map_height) && (column < map_width) && (map[line][column] == '#')
end
def find_path(map, robot_line, robot_column, map_height, map_with)
current_direction = DIRECTION_NORTH
current_path = []
current_line = robot_line
current_column = robot_column
while true
p "(#{current_line}, #{current_column}) going #{current_direction}"
target_line = current_line + DELTA_LINE[current_direction]
target_column = current_column + DELTA_COLUMN[current_direction]
if can_go_on?(target_line, target_column, map_height, map_with, map)
current_path[-1] += 1
current_line = target_line
current_column = target_column
else
target_line = current_line + DELTA_LINE_TURNING_LEFT[current_direction]
target_column = current_column + DELTA_COLUMN_TURNING_LEFT[current_direction]
if can_go_on?(target_line, target_column, map_height, map_with, map)
p "Turning left"
current_path << 'L'
current_path << 1
current_line = target_line
current_column = target_column
current_direction = DIRECTION_TURNING_LEFT[current_direction]
else
target_line = current_line + DELTA_LINE_TURNING_RIGHT[current_direction]
target_column = current_column + DELTA_COLUMN_TURNING_RIGHT[current_direction]
if can_go_on?(target_line, target_column, map_height, map_with, map)
p "Turning right"
current_path << 'R'
current_path << 1
current_line = target_line
current_column = target_column
current_direction = DIRECTION_TURNING_RIGHT[current_direction]
else
p "Stuck at (#{current_line}, #{current_column}) !"
return current_path
end
end
end
end
end
find_path(map, robot_line, robot_column, map_height, map_width)
def format_code(code)
result = []
code.each_with_index do |c, index|
if index != 0
result << ','.ord
end
result.concat(c.to_s.chars.map(&:ord))
end
result + [10]
end
# I did the calculation by hand as it's not too difficult
main_routine = format_code(['A', 'B', 'A', 'B', 'C', 'C', 'B', 'A', 'B', 'C'])
function_a = format_code(['L', 12, 'L', 10, 'R', 8, 'L', 12])
function_b = format_code(['R', 8, 'R', 10, 'R', 12])
function_c = format_code(['L', 10, 'R', 12, 'R', 8])
memory = IO.read('input.txt').split(',').map(&:to_i)
memory[0] = 2
program = main_routine + function_a + function_b + function_c + ['n'.ord, 10]
amplifier = Amplifier.new(memory, program)
p amplifier.resume([]).last
Day 18
require 'set'
# @param [String] map
def process(map)
number_of_keys = map.count('a-z')
all_keys_mask = 0
1.upto(number_of_keys) do |key_number|
all_keys_mask += (1 << (key_number - 1))
end
map = map.split("\n")
map_width = map.first.length
map_height = map.length
# All positions already visited
# each contain a Set with the list of key combinations
visited_positions = Hash.new { |hash, key| hash[key] = Set.new }
entrance_line = map.index { |l| l.include?('@') }
entrance_column = map[entrance_line].index('@')
map[entrance_line][entrance_column] = '.'
positions_to_explore = []
positions_to_explore << {
line: entrance_line,
column: entrance_column,
keys: 0,
number_of_steps: 0
}
until positions_to_explore.empty?
position_to_explore = positions_to_explore.shift
[{delta_line: -1, delta_column: 0},
{delta_line: 1, delta_column: 0},
{delta_line: 0, delta_column: 1},
{delta_line: 0, delta_column: -1}].each do |direction|
target_line = position_to_explore[:line] + direction[:delta_line]
target_column = position_to_explore[:column] + direction[:delta_column]
target_keys = position_to_explore[:keys]
target_map_item = map[target_line][target_column]
# p "Trying (#{target_line}, #{target_column}) with content [#{target_map_item}] and keys [#{target_keys}]"
if target_map_item == '#'
# p "Wall"
next
end
target_map_item_ascii = target_map_item.ord
if (target_map_item_ascii >= 97) && (target_map_item_ascii <= 122)
# p "Key"
key_index = target_map_item_ascii - 97
if (target_keys & (1 << key_index)) == 0
# p "Fetch the key"
target_keys += (1 << key_index)
if target_keys == all_keys_mask
# p "Found all the keys !"
return position_to_explore[:number_of_steps] + 1
end
end
elsif (target_map_item_ascii >= 65) && (target_map_item_ascii <= 90)
require_key_index = target_map_item_ascii - 65
unless (target_keys & (1 << require_key_index)) != 0
# p "Miss the key"
next
end
end
unless visited_positions[(target_line * map_width) + target_column].add?(target_keys)
next
end
positions_to_explore << {
line: target_line,
column: target_column,
keys: target_keys,
number_of_steps: position_to_explore[:number_of_steps] + 1
}
end
end
end