Last active
December 21, 2016 15:45
-
-
Save Rodel30/2d6a7dc847ca6192748b033fa0bc19ad to your computer and use it in GitHub Desktop.
Advent of Code 2016
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def calc_distance(instruction_block) | |
instructions = instruction_block.split(', ') | |
location = { x: 0, y: 0 } | |
directions = [ [ :y, :+ ], [ :x, :+ ], [ :y, :- ], [ :x, :- ] ] | |
direction = 0 | |
instructions.each do |i| | |
turn = i[0] | |
distance = i[1..-1].to_i | |
direction = turn == 'L' ? direction.pred : direction.next | |
effect = directions[direction%4] | |
location[effect[0]] = location[effect[0]].public_send(effect[1], distance) | |
end | |
location[:x].abs + location[:y].abs | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def calc_distance_to_first_repeat(instruction_block) | |
instructions = instruction_block.split(', ') | |
location = { x: 0, y: 0 } | |
directions = [ [ :y, :next ], [ :x, :next ], [ :y, :pred ], [ :x, :pred ] ] | |
direction = 0 | |
locations = [{ x:0, y:0 }] | |
found_location = false | |
instructions.each do |i| | |
turn = i[0] | |
distance = i[1..-1].to_i | |
direction = turn == 'L' ? direction.pred : direction.next | |
effect = directions[direction%4] | |
distance.times do |n| | |
location[effect[0]] = location[effect[0]].public_send(effect[1]) | |
if !locations.index(location).nil? | |
found_location = true | |
break | |
end | |
locations << location.dup | |
end | |
break if found_location | |
end | |
location[:x].abs + location[:y].abs | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_code(instructions) | |
changes = { | |
U: [ Proc.new{|n| n > 3 }, :-, 3 ], | |
D: [ Proc.new{|n| n < 7 }, :+, 3 ], | |
L: [ Proc.new{|n| n%3 != 1 }, :-, 1 ], | |
R: [ Proc.new{|n| n%3 != 0 }, :+, 1 ] | |
} | |
position = 5 | |
code = [] | |
instructions.split("\n").each do |l| | |
l.each_char do |c| | |
d = changes[c.to_sym] | |
position = position.public_send(d[1], d[2]) if d[0].call(position) | |
end | |
code << position | |
end | |
code.join | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_code(instructions) | |
keypad = [ | |
[nil, nil, 1, nil, nil], | |
[nil, 2, 3, 4, nil], | |
[5, 6, 7, 8, 9], | |
[nil, 'A', 'B', 'C', nil], | |
[nil, nil, 'D', nil, nil], | |
] | |
changes = { | |
U: [ :y, :- ], | |
D: [ :y, :+ ], | |
L: [ :x, :- ], | |
R: [ :x, :+ ] | |
} | |
position = { y: 2, x: 0 } | |
code = [] | |
instructions.split("\n").each do |l| | |
l.each_char do |c| | |
d = changes[c.to_sym] | |
new_pos = position.dup | |
new_pos[d[0]] = new_pos[d[0]].public_send(d[1], 1) | |
if new_pos[:y] >= 0 && new_pos[:x] >= 0 && | |
!keypad[new_pos[:y]].nil? && !keypad[new_pos[:y]][new_pos[:x]].nil? | |
position = new_pos | |
end | |
end | |
code << keypad[position[:y]][position[:x]] | |
end | |
code.join | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_possible_triangle_count(inp) | |
possible = [] | |
inp.split("\n").map(&:strip).each do |l| | |
sides = l.split(/\s+/).map(&:to_i).sort | |
if sides[0] + sides[1] > sides[2] | |
possible << sides | |
end | |
end | |
possible.count | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_possible_triangle_count(inp) | |
possible = [] | |
lines = inp.split("\n").map(&:strip).map{|l| l.split(/\s+/).map(&:to_i) } | |
(0..(lines.count)).step(3).each do |i| | |
lines[i..(i+2)].transpose.each do |l| | |
sides = l.sort | |
if sides[0] + sides[1] > sides[2] | |
possible << sides | |
end | |
end | |
end | |
possible.count | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def sum_real_room_sector_ids(room_names) | |
room_names.split("\n").reduce(0) do |c,l| | |
name, sectorid, checksum = l.match(/^([a-z-]+)-(\d+)\[([a-z]+)\]$/).captures | |
if checksum == get_checksum(name) | |
c + sectorid.to_i | |
else | |
c | |
end | |
end | |
end | |
def get_checksum(name) | |
char_counts = Hash.new(0) | |
name.each_char do |c| | |
next if c == '-' | |
char_counts[c] += 1 | |
end | |
char_counts.to_a.sort_by{|a| [a[1] * -1, a[0]] }[0..4].map{|a| a[0] }.join('') | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def real_rooms(room_names) | |
room_names.split("\n").reduce([]) do |r,l| | |
name, sector_id, checksum = l.match(/^([a-z-]+)-(\d+)\[([a-z]+)\]$/).captures | |
if checksum == get_checksum(name) | |
r << { sector_id: sector_id.to_i, name: decrypt_name(name, sector_id.to_i) } | |
else | |
r | |
end | |
end | |
end | |
def decrypt_name(name, sector_id) | |
alpha = ('a'..'z').to_a | |
name.tr( alpha.join('') + '-', alpha.rotate(sector_id%26).join('') + ' ' ) | |
end | |
def get_checksum(name) | |
char_counts = Hash.new(0) | |
name.each_char do |c| | |
next if c == '-' | |
char_counts[c] += 1 | |
end | |
char_counts.to_a.sort_by{|a| [a[1] * -1, a[0]] }[0..4].map{|a| a[0] }.join('') | |
end | |
def sector_id_for_room(room_names, name) | |
real_rooms(room_names).select{|r| r[:name] == name }[0][:sector_id] | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'digest' | |
def get_password(door_id) | |
password = [] | |
n = 0 | |
loop do | |
hash = Digest::MD5.hexdigest("#{door_id}#{n}") | |
password << hash[5] if hash[0..4] == '00000' | |
break if password.count == 8 | |
n += 1 | |
end | |
password.join('') | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'digest' | |
def get_password(door_id) | |
password = [] | |
n = 0 | |
loop do | |
hash = Digest::MD5.hexdigest("#{door_id}#{n}") | |
if hash[0..4] == '00000' && ('0'..'7').include?(hash[5]) | |
password[hash[5].to_i] ||= hash[6] | |
break if password.select{|a| !a.nil? }.count == 8 | |
end | |
n += 1 | |
end | |
password.join('') | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_message(messages) | |
signals = messages.split("\n") | |
signal_letters = Array.new(signals.first.size){ Hash.new(0) } | |
signals.each do |s| | |
s.chars.each_with_index do |c, i| | |
signal_letters[i][c] += 1 | |
end | |
end | |
signal_letters.map{|h| h.sort_by{|k,v| v }.reverse.first.first }.join('') | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_message(messages) | |
signals = messages.split("\n") | |
signal_letters = Array.new(signals.first.size){ Hash.new(0) } | |
signals.each do |s| | |
s.chars.each_with_index do |c, i| | |
signal_letters[i][c] += 1 | |
end | |
end | |
signal_letters.map{|h| h.sort_by{|k,v| v }.first.first }.join('') | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def count_support_ssl(ips) | |
aba_regex = '([a-z])((?!\1)[a-z])\1' | |
bab_regex = '\2\1\2' | |
other_text_and_hypernets = '([a-z]*(\[[a-z]*\][a-z]*)*)' | |
ips.split("\n").count do |ip| | |
ip =~ /#{aba_regex}#{other_text_and_hypernets}\[[^\]]*#{bab_regex}[^\]]*\]/ || | |
ip =~ /\[[^\]]*#{aba_regex}[^\]]*\]#{other_text_and_hypernets}#{bab_regex}/ | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def count_support_tls(ips) | |
abba_regex = '([a-z])((?!\1)[a-z])\2\1' | |
ips.split("\n").count{|ip| !(ip =~ /\[[^\]]*#{abba_regex}[^\]]*\]/) && ip =~ /#{abba_regex}/ } | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_pixel_count(input) | |
width = 50 | |
height = 6 | |
screen = Array.new(height){ Array.new(width, '.') } | |
input.split("\n").each do |l| | |
parts = l.split | |
if parts[0] == 'rect' | |
cols,rows = parts[1].split('x').map(&:to_i) | |
rows.times do |i| | |
screen[i].fill('#', 0, cols) | |
end | |
elsif parts[0] == 'rotate' | |
ary = parts[2].split('=') | |
if ary[0] == 'x' | |
s = screen.transpose | |
s[ary[1].to_i].rotate!( -1 * parts[4].to_i) | |
screen = s.transpose | |
else | |
screen[ary[1].to_i].rotate!( -1 * parts[4].to_i ) | |
end | |
end | |
end | |
screen.reduce(0){|c, r| c += r.count{|p| p == '#' } } | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def see_screen(input) | |
width = 50 | |
height = 6 | |
screen = Array.new(height){ Array.new(width, '.') } | |
input.split("\n").each do |l| | |
parts = l.split | |
if parts[0] == 'rect' | |
cols,rows = parts[1].split('x').map(&:to_i) | |
rows.times do |i| | |
screen[i].fill('#', 0, cols) | |
end | |
elsif parts[0] == 'rotate' | |
ary = parts[2].split('=') | |
if ary[0] == 'x' | |
s = screen.transpose | |
s[ary[1].to_i].rotate!( -1 * parts[4].to_i) | |
screen = s.transpose | |
else | |
screen[ary[1].to_i].rotate!( -1 * parts[4].to_i ) | |
end | |
end | |
end | |
puts screen.map(&:join) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_decompressed_length(input) | |
len = 0 | |
s = input | |
while m = s.match(/\((\d+x\d+)\)/) | |
len += m.pre_match.length | |
parts = m.captures[0].split('x').map(&:to_i) | |
len += (parts[0] * parts[1]) | |
s = m.post_match[parts[0]..-1] | |
end | |
len + s.length | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_decompressed_length(input) | |
len = 0 | |
s = input | |
while m = s.match(/\((\d+x\d+)\)/) | |
len += m.pre_match.length | |
a = m.post_match | |
parts = m.captures[0].split('x').map(&:to_i) | |
r = a[0..(parts[0]-1)] | |
len += (get_decompressed_length(r) * parts[1]) | |
s = a[parts[0]..-1] | |
end | |
len + s.length | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def find_comparor(instruction_block) | |
targets = set_up_targets(instruction_block) | |
until comparor_bot(targets[:bots]) do | |
acs = actors(targets[:bots]) | |
if acs.size == 0 | |
puts targets | |
raise 'Out of actions' | |
end | |
acs.each do |bot| | |
chips = bot.chips.sort | |
%w(low high).each do |d| | |
target_type = (bot.public_send(d + '_type') + 's').to_sym | |
target_id = bot.public_send(d) | |
targets[target_type][target_id].chips << chips[ d == 'low' ? 0 : 1 ] | |
end | |
bot.chips = [] | |
end | |
end | |
comparor_bot(targets[:bots]).id | |
end | |
def actors(bots) | |
bots.select{|k,v| v.chips.length == 2 }.map{|b| b[1] } | |
end | |
def comparor_bot(bots) | |
actors(bots).select{|b| b.chips.sort == [17, 61] }.first | |
end | |
def set_up_targets(instruction_block) | |
targets = { bots: {}, outputs: {} } | |
instruction_block.split("\n").each do |l| | |
parts = l.split | |
case parts[0] | |
when 'value' | |
targets[:bots][parts[-1]] ||= Target.new(parts[-1]) | |
targets[:bots][parts[-1]].chips << parts[1].to_i | |
when 'bot' | |
targets[:bots][parts[1]] ||= Target.new(parts[1]) | |
targets[:bots][parts[1]].low = parts[6] | |
targets[:bots][parts[1]].low_type = parts[5] | |
targets[(parts[5] + 's').to_sym][parts[6]] ||= Target.new(parts[6]) | |
targets[:bots][parts[1]].high = parts[-1] | |
targets[:bots][parts[1]].high_type = parts[-2] | |
targets[(parts[-2] + 's').to_sym][parts[-1]] ||= Target.new(parts[-1]) | |
else | |
raise 'Invalid line: ' + l | |
end | |
end | |
targets | |
end | |
class Target | |
attr_accessor :low, :low_type, :high, :high_type, :chips, :id | |
def initialize(id) | |
@chips = [] | |
@id = id | |
super() | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def find_output_chips(instruction_block) | |
targets = set_up_targets(instruction_block) | |
until chips_placed(targets[:outputs]) do | |
acs = actors(targets[:bots]) | |
if acs.size == 0 | |
puts targets | |
raise 'Out of actions' | |
end | |
acs.each do |bot| | |
chips = bot.chips.sort | |
%w(low high).each do |d| | |
target_type = (bot.public_send(d + '_type') + 's').to_sym | |
target_id = bot.public_send(d) | |
targets[target_type][target_id].chips << chips[ d == 'low' ? 0 : 1 ] | |
end | |
bot.chips = [] | |
end | |
end | |
targets[:outputs].select{|k,o| ('0'..'2').include?(k) }.reduce(1){|m,(k,o)| m * o.chips.first } | |
end | |
def actors(bots) | |
bots.select{|k,v| v.chips.length == 2 }.map{|b| b[1] } | |
end | |
def chips_placed(outputs) | |
('0'..'2').select{|o| outputs[o].chips.count > 0 }.count == 3 | |
end | |
def set_up_targets(instruction_block) | |
targets = { bots: {}, outputs: {} } | |
instruction_block.split("\n").each do |l| | |
parts = l.split | |
case parts[0] | |
when 'value' | |
targets[:bots][parts[-1]] ||= Target.new(parts[-1]) | |
targets[:bots][parts[-1]].chips << parts[1].to_i | |
when 'bot' | |
targets[:bots][parts[1]] ||= Target.new(parts[1]) | |
targets[:bots][parts[1]].low = parts[6] | |
targets[:bots][parts[1]].low_type = parts[5] | |
targets[(parts[5] + 's').to_sym][parts[6]] ||= Target.new(parts[6]) | |
targets[:bots][parts[1]].high = parts[-1] | |
targets[:bots][parts[1]].high_type = parts[-2] | |
targets[(parts[-2] + 's').to_sym][parts[-1]] ||= Target.new(parts[-1]) | |
else | |
raise 'Invalid line: ' + l | |
end | |
end | |
targets | |
end | |
class Target | |
attr_accessor :low, :low_type, :high, :high_type, :chips, :id | |
def initialize(id) | |
@chips = [] | |
@id = id | |
super() | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'set' | |
setup_part1 = 'The first floor contains a polonium generator, a thulium generator, a thulium-compatible microchip, a promethium generator, a ruthenium generator, a ruthenium-compatible microchip, a cobalt generator, and a cobalt-compatible microchip. | |
The second floor contains a polonium-compatible microchip and a promethium-compatible microchip. | |
The third floor contains nothing relevant. | |
The fourth floor contains nothing relevant.' | |
setup_part2 = 'The first floor contains an elerium generator, an elerium-compatible microchip, a dilithium generator, a dilithium-compatible microchip, a polonium generator, a thulium generator, a thulium-compatible microchip, a promethium generator, a ruthenium generator, a ruthenium-compatible microchip, a cobalt generator, and a cobalt-compatible microchip. | |
The second floor contains a polonium-compatible microchip and a promethium-compatible microchip. | |
The third floor contains nothing relevant. | |
The fourth floor contains nothing relevant.' | |
class ElevatorFun | |
def initialize | |
@next_marker = 'a' | |
@markers = {} | |
@known_hashes = Set.new | |
end | |
def complete?(floors) | |
floors[0..-2].all?(&:empty?) && floors[-1].any? | |
end | |
def get_downward_possibilities(e, floors) | |
ne = e - 1 | |
floors[e].map do |i| | |
fs = floors.map(&:dup) | |
fs[e].delete(i) | |
fs[ne] << i | |
{ e: ne, fs: fs } | |
end | |
end | |
def get_marker(name) | |
@markers[name] ||= @next_marker.succ!.dup | |
end | |
def get_upward_possibilities(e, floors) | |
ne = e + 1 | |
floors[e].combination(2).map do |is| | |
fs = floors.map(&:dup) | |
is.each do |i| | |
fs[e].delete(i) | |
fs[ne] << i | |
end | |
{ e: ne, fs: fs } | |
end | |
end | |
def hash_state(e, floors) | |
# e [gm].sort | |
locations = Hash.new { |hash, key| hash[key] = Hash.new(0) } | |
floors.each_with_index{|f, i| f.each{|o| locations[o.split('')[0]][o.split('')[1]] = i } } | |
e.to_s + @markers.values.map{|m| l = locations[m]; "#{l['G']}#{l['M']}" }.sort.join('') | |
end | |
def pursue_state?(e, floors) | |
valid_state?(floors) && !@known_hashes.include?(hash_state(e, floors)) | |
end | |
def valid_state?(floors) | |
valid = true | |
floors.each do |f| | |
next if f.size < 2 | |
next if f.select{|i| i.match(/G$/) }.size == 0 | |
if f.select{|i| i.match(/G$/) }.size < f.size / 2.0 | |
valid = false | |
break | |
end | |
# Enough short circuits, let's do the full mapping now | |
f.select{|i| i.match(/M$/) }.each do |m| | |
if !f.index(m.gsub(/M$/, 'G')) | |
valid = false | |
break | |
end | |
end | |
break unless valid | |
end | |
valid | |
end | |
def perform(setup) | |
floors = [] | |
step = 0 | |
setup.split("\n").each do |l| | |
floor = [] | |
l.scan(/([a-z]+) generator/){|m| floor << "#{get_marker(m)}G"} | |
l.scan(/([a-z]+)-compatible microchip/){|m| floor << "#{get_marker(m)}M"} | |
floors << floor | |
end | |
queued_states = [{ e: 0, fs: floors }] | |
catch :complete do | |
loop do | |
new_queue = [] | |
puts "#{step}: #{queued_states.count}" | |
queued_states.each do |s| | |
e, fs = [s[:e], s[:fs]] | |
next unless pursue_state?(e,fs) | |
throw :complete if complete?(fs) | |
@known_hashes << hash_state(e,fs) | |
if e < floors.size - 1 | |
new_queue.concat get_upward_possibilities(e, fs) | |
end | |
if e > 0 && fs[(0..(e-1))].any?(&:any?) | |
new_queue.concat get_downward_possibilities(e, fs) | |
end | |
end | |
break if new_queue.size == 0 | |
queued_states = new_queue | |
step += 1 | |
end | |
end | |
puts step | |
end | |
end | |
ElevatorFun.new.perform(setup_part1) | |
ElevatorFun.new.perform(setup_part2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
registers_part1 = ('a'..'d').reduce({}){|h,l| h.merge({ l => 0 }) } | |
registers_part2 = ('a'..'d').reduce({}){|h,l| h.merge({ l => (l == 'c' ? 1 : 0) }) } | |
def get_registers(instruction_block, registers) | |
lines = instruction_block.split("\n") | |
line_count = lines.count | |
current_line = 0 | |
while current_line < lines.count | |
parts = lines[current_line].split(' ') | |
case parts[0] | |
when 'cpy' | |
registers[parts[2]] = if registers.has_key?(parts[1]) | |
registers[parts[1]] | |
else | |
parts[1].to_i | |
end | |
when 'dec' | |
registers[parts[1]] -= 1 | |
when 'inc' | |
registers[parts[1]] += 1 | |
when 'jnz' | |
if registers[parts[1]] != 0 | |
current_line += parts[2].to_i | |
next | |
end | |
else | |
raise 'Invalid instruction' | |
end | |
current_line += 1 | |
end | |
puts registers | |
end | |
# set variable data to the puzzle input text block | |
get_registers(data, registers_part1) | |
get_registers(data, registers_part2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'set' | |
class CubicalMaze | |
def initialize | |
@positions = {} | |
end | |
def calculate(x,y) | |
n = x*x + 3*x + 2*x*y + y + y*y + designers_fav_number | |
b = n.to_s(2) | |
b.chars.count{|i| i.to_i == 1 } % 2 == 0 ? '.' : '#' | |
end | |
def designers_fav_number | |
1364 | |
end | |
def get_at(p) | |
@positions[key(p)] ||= calculate(*p) | |
end | |
def key(p) | |
p.join('x') | |
end | |
def steps_to(*dest) | |
pos = [1,1] | |
history = Set.new | |
steps = 0 | |
queue = [pos] | |
catch :complete do | |
loop do | |
new_queue = [] | |
queue.each do |p| | |
throw :complete if p == dest | |
next if get_at(p) == '#' | |
next if history.include?(key(p)) | |
history << key(p) | |
if p[0] > 0 | |
new_queue << [p[0] - 1, p[1]] | |
end | |
if p[1] > 0 | |
new_queue << [p[0], p[1] - 1] | |
end | |
new_queue << [p[0], p[1] + 1] | |
new_queue << [p[0] + 1, p[1]] | |
end | |
queue = new_queue | |
steps += 1 | |
end | |
end | |
steps | |
end | |
end | |
CubicalMaze.new.steps_to(31,39) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'set' | |
class CubicalMaze | |
def initialize | |
@positions = {} | |
end | |
def calculate(x,y) | |
n = x*x + 3*x + 2*x*y + y + y*y + designers_fav_number | |
b = n.to_s(2) | |
b.chars.count{|i| i.to_i == 1 } % 2 == 0 ? '.' : '#' | |
end | |
def designers_fav_number | |
1364 | |
end | |
def get_at(p) | |
@positions[key(p)] ||= calculate(*p) | |
end | |
def key(p) | |
p.join('x') | |
end | |
def destinations_within(steps) | |
pos = [1,1] | |
history = Set.new | |
queue = [pos] | |
# first pass is 0 steps | |
(steps + 1).times do | |
new_queue = [] | |
queue.each do |p| | |
next if get_at(p) == '#' | |
next if history.include?(key(p)) | |
history << key(p) | |
if p[0] > 0 | |
new_queue << [p[0] - 1, p[1]] | |
end | |
if p[1] > 0 | |
new_queue << [p[0], p[1] - 1] | |
end | |
new_queue << [p[0], p[1] + 1] | |
new_queue << [p[0] + 1, p[1]] | |
end | |
queue = new_queue | |
end | |
history.count | |
end | |
end | |
CubicalMaze.new.destinations_within(50) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'digest' | |
class OTPKeys | |
def initialize(salt, hash_times) | |
@hashes = [] | |
@salt = salt | |
@hash_times = hash_times | |
super() | |
end | |
def get_idx_of_key_64 | |
i = 0 | |
keys = [] | |
while keys.count < 64 && i < 50000 do | |
hd = get_hash_data(i) | |
is_key = false | |
if c = hd[:first_triple] | |
((i+1)..(i + 1000)).each do |i2| | |
hd2 = get_hash_data(i2) | |
if hd2[:quints].include?(c) | |
puts "#{keys.count}: #{hd[:h]}" | |
is_key = true | |
break | |
end | |
end | |
end | |
keys << { hd: hd, i: i } if is_key | |
i += 1 | |
end | |
keys.last[:i] | |
end | |
def get_hash_data(i) | |
if !@hashes[i] | |
h = @salt + i.to_s | |
@hash_times.times do | |
h = Digest::MD5.hexdigest(h) | |
end | |
m = h.match(/(.)\1\1/) | |
ft = m ? m.captures.first : nil | |
q = h.scan(/(.)\1\1\1\1/).flatten | |
@hashes[i] = { h: h, first_triple: ft, quints: q } | |
end | |
@hashes[i] | |
end | |
end | |
OTPKeys.new('ahsbgdzn', 1).get_idx_of_key_64 | |
OTPKeys.new('ahsbgdzn', 2017).get_idx_of_key_64 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_timing(setup) | |
discs = [] | |
setup.split("\n").each do |d| | |
parts = d.split(' ') | |
disc_number = parts[1][1..-1].to_i | |
rotation_time = parts[3].to_i | |
start_pos = parts[-1][0..-2].to_i | |
discs << { dn: disc_number, rt: rotation_time, s: start_pos } | |
end | |
t = 0 | |
while true | |
break if discs.all?{|d| (t + d[:s] + d[:dn]) % d[:rt] == 0 } | |
t += 1 | |
end | |
t | |
end | |
# For part 2 I just added a line to my puzzle input that represented the new disc. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def gen_checksum(base_data, len) | |
data = base_data | |
until data.length >= len | |
new_data = data.reverse.tr('10','01') | |
data = data + '0' + new_data | |
end | |
data = data[0..(len-1)] | |
checksum = data | |
until checksum.length % 2 == 1 | |
checksum = checksum.scan(/../).map{|s| s[0] == s[1] ? '1' : '0' }.join | |
end | |
checksum | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'digest' | |
def get_path(password) | |
paths = [{ x: 1, y: 1, path: '' }] | |
map = { u: [:y,-1], d: [:y,1], l: [:x,-1], r: [:x,1] } | |
while paths.any? | |
new_paths = [] | |
paths.each do |p| | |
ds = get_possible_directions(password, p[:path]) | |
ds.each do |d| | |
np = p.dup | |
dm = map[d] | |
np[dm[0]] += dm[1] | |
if np[:x] == 4 && np[:y] == 4 | |
return np[:path] + d.to_s.upcase | |
elsif (1..4).include?(np[:x]) && (1..4).include?(np[:y]) | |
np[:path] += d.to_s.upcase | |
new_paths << np | |
end | |
end | |
end | |
paths = new_paths | |
end | |
return 'Failed to find path' | |
end | |
def get_possible_directions(password, path) | |
hsh = Digest::MD5.hexdigest(password + path) | |
%i(u d l r).zip(hsh[0..3].chars).select{|d| ('b'..'f').include?(d[1]) }.map{|d| d[0] } | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'digest' | |
def get_longest_path(password) | |
paths = [{ x: 1, y: 1, path: '' }] | |
map = { u: [:y,-1], d: [:y,1], l: [:x,-1], r: [:x,1] } | |
max_path_length = 0 | |
while paths.any? | |
new_paths = [] | |
paths.each do |p| | |
ds = get_possible_directions(password, p[:path]) | |
ds.each do |d| | |
np = p.dup | |
dm = map[d] | |
np[dm[0]] += dm[1] | |
if np[:x] == 4 && np[:y] == 4 | |
max_path_length = [max_path_length, np[:path].size + 1].max | |
elsif (1..4).include?(np[:x]) && (1..4).include?(np[:y]) | |
np[:path] += d.to_s.upcase | |
new_paths << np | |
end | |
end | |
end | |
paths = new_paths | |
end | |
max_path_length | |
end | |
def get_possible_directions(password, path) | |
hsh = Digest::MD5.hexdigest(password + path) | |
%i(u d l r).zip(hsh[0..3].chars).select{|d| ('b'..'f').include?(d[1]) }.map{|d| d[0] } | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_safe_tile_count(first_row, row_count) | |
row = first_row.split('') | |
count = row.count{|t| t == '.' } | |
(row_count-1).times do | |
row = row.each_with_index.map{|t,i| | |
l = i == 0 ? '.' : row[i-1] | |
r = row[i+1] || '.' | |
l != r ? '^' : '.' | |
} | |
count += row.count{|t| t == '.' } | |
end | |
count | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def find_winner(num_elves) | |
elves = (1..num_elves).to_a | |
until elves.count == 1 | |
n = elves.count | |
elves = elves.select.with_index{|_,i| i.even? } | |
if n.odd? | |
elves.unshift(elves.pop) | |
end | |
end | |
elves.first | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def find_winner(num_elves) | |
elves = (1..num_elves).to_a | |
n = elves.count | |
i = 0 | |
until n == 1 | |
i = 0 unless i < n | |
s = (i + n/2) % n | |
elves.delete_at(s) | |
n -= 1 | |
i += 1 if s > i | |
end | |
elves.first | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def find_min_ip(input_block) | |
condensed_ranges = [] | |
ranges = input_block.split("\n").map{|r| Range.new(*r.split('-').map(&:to_i)) }.sort_by{|r| r.first } | |
return 0 if ranges[0].first > 0 | |
i = 0 | |
current_range = 0..0 | |
while i < ranges.count | |
if current_range.cover?(ranges[i].first) || current_range.last + 1 == ranges[i].first | |
if !current_range.cover?(ranges[i].last) | |
current_range = (current_range.first..ranges[i].last) | |
end | |
i += 1 | |
else | |
return current_range.last + 1 | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def find_open_ip_count(input_block) | |
blocked_ips = 0 | |
ranges = input_block.split("\n").map{|r| Range.new(*r.split('-').map(&:to_i)) }.sort_by{|r| r.first } | |
i = 1 | |
current_range = ranges[0] | |
while i < ranges.count | |
if current_range.cover?(ranges[i].first) || current_range.last + 1 == ranges[i].first | |
if !current_range.cover?(ranges[i].last) | |
current_range = (current_range.first..ranges[i].last) | |
end | |
else | |
blocked_ips += current_range.size | |
current_range = ranges[i] | |
end | |
i += 1 | |
end | |
blocked_ips += current_range.size | |
4294967296 - blocked_ips | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def scramble_password(password, input_block) | |
pa = password.chars | |
input_block.split("\n").each do |i| | |
parts = i.split(' ') | |
case parts[0] | |
when 'swap' | |
if parts[1] == 'position' | |
pa[parts[2].to_i], pa[parts[5].to_i] = pa[parts[5].to_i], pa[parts[2].to_i] | |
else | |
s = parts[2] + parts[5] | |
pa = pa.join('').tr(s, s.reverse).chars | |
end | |
when 'rotate' | |
case parts[1] | |
when 'left' | |
pa.rotate!(parts[2].to_i) | |
when 'right' | |
pa.rotate!(parts[2].to_i * -1) | |
else | |
idx = pa.index(parts[6]) + 1 | |
idx += 1 if idx > 4 | |
pa.rotate!(idx * -1) | |
end | |
when 'reverse' | |
r = (parts[2].to_i)..(parts[4].to_i) | |
pa[r] = pa[r].reverse | |
when 'move' | |
pa.insert(parts[5].to_i, pa.delete_at(parts[2].to_i)) | |
else | |
raise 'Unexpected Command' | |
end | |
end | |
pa.join('') | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Only guaranteed to work with passwords of length 8 | |
def unscramble_password(password, input_block) | |
pa = password.chars | |
input_block.split("\n").reverse.each do |i| | |
parts = i.split(' ') | |
case parts[0] | |
when 'swap' | |
if parts[1] == 'position' | |
pa[parts[2].to_i], pa[parts[5].to_i] = pa[parts[5].to_i], pa[parts[2].to_i] | |
else | |
s = parts[2] + parts[5] | |
pa = pa.join('').tr(s, s.reverse).chars | |
end | |
when 'rotate' | |
case parts[1] | |
when 'left' | |
pa.rotate!(parts[2].to_i * -1) | |
when 'right' | |
pa.rotate!(parts[2].to_i) | |
else | |
idx = pa.index(parts[6]) | |
pa.rotate!( idx/2 + (idx%2 == 1 || idx == 0 ? 1 : 5)) | |
end | |
when 'reverse' | |
r = (parts[2].to_i)..(parts[4].to_i) | |
pa[r] = pa[r].reverse | |
when 'move' | |
pa.insert(parts[2].to_i, pa.delete_at(parts[5].to_i)) | |
else | |
raise 'Unexpected Command' | |
end | |
end | |
pa.join('') | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment