Advent of Code 2016, Day 14: One-Time Pad

#ruby #advent of code 2016

Part A

On Day 14 we have MD5 hashing again. I don’t wont to explain this one in different words. I will just post my solutions

require "digest"

salt = "abc"
index = 0

fives = []
hashes = []

while true
  hash = Digest::MD5.hexdigest([salt, index].join)

  if hash =~ /(.)\1{4}/
    valid = fives.select { |five| five[0] == Regexp.last_match[1] && index <= five[1] }

    valid.each do |v|
      fives.delete(v)
      hashes.push([*v, hash, index])
    end
  end

  if hash =~ /(.)\1{2}/
    fives.push([Regexp.last_match[1], index + 1000, hash])
  end

  if hashes.size == 64
    break
  end

  index += 1
end

hashes.sort { |a, b| a[1] <=> b[1] }.each { |h| puts "#{h[2]} at #{h[1] - 1000}, matching five hash at #{h[3]} value = #{h[4]}" }

Part B

And here is solution for second part:

require "digest"

salt = "abc"
index = 0

def md5(salt, index)
  hash = Digest::MD5.hexdigest([salt, index].join)
  2016.times { hash = Digest::MD5.hexdigest(hash) }
  hash
end

triplets = []
hashes = []
max_index = nil

while true
  hash = md5(salt, index)

  if hash =~ /(.)\1{4}/
    triplets.select! { |triplet| (index - triplet[:index]) <= 1000 }
    triplets.each do |triplet|
      if triplet[:char] == Regexp.last_match[1] && 
        hashes.push(triplet)
      end
    end
  end

  if hash =~ /(.)\1{2}/ && !max_index
    triplets.push({ char: Regexp.last_match[1], index: index })
  end

  if hashes.size >= 64
    max_index = index

    if triplets.empty?
      break
    end
  end

  index += 1

  if index % 100 == 0
    puts [index, hashes.size].inspect
  end
end

puts hashes.sort { |a, b| a[:index] <=> b[:index] }[63].inspect