Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Advent of Code 2016
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
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)
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)
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)
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
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.
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
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
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
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
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
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
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
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
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
#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