Advent of Code 2020: Day 4 (Ruby solution)

Another day, another problem to solve. This time, you are asked for some data validation. You are supposed to parse entries from a text file.

In the first task, only validating the presence of some keys is required. In the second one, you have to check if values meet certain specific constraints:

  • byr (Birth Year) - four digits; at least 1920 and at most 2002.
  • iyr (Issue Year) - four digits; at least 2010 and at most 2020.
  • eyr (Expiration Year) - four digits; at least 2020 and at most 2030.
  • hgt (Height) - a number followed by either cm or in: If cm, the number must be at least 150 and at most 193. If in, the number must be at least 59 and at most 76.
  • hcl (Hair Color) - a # followed by exactly six characters 0-9 or a-f.
  • ecl (Eye Color) - exactly one of: amb blu brn gry grn hzl oth.
  • pid (Passport ID) - a nine-digit number, including leading zeroes.
  • cid (Country ID) - ignored, missing or not.
REQUIRED_FIELDS = %w(byr iyr eyr hgt hcl ecl pid)
EYE_COLORS = %w(amb blu brn gry grn hzl oth)

def count_valid_entries file, strong_validation = false
  valid = 0
  file.each_line("\n\n") do |entry|
    attrs = Hash[entry.split.map { |key_and_val| key_and_val.split(':') }]
    next unless (REQUIRED_FIELDS - attrs.keys).empty?
    if strong_validation
      next unless attrs['byr'].to_i.between?(1920, 2002)
      next unless attrs['iyr'].to_i.between?(2010, 2020)
      next unless attrs['eyr'].to_i.between?(2020, 2030)
      hgt = attrs['hgt'].scan(/\d+/).first.to_i
      if attrs['hgt'].end_with?('cm')
        next unless hgt.between?(150, 193)
      elsif attrs['hgt'].end_with?('in')
        next unless hgt.between?(59, 76)
      else next
      end
      next unless attrs['hcl'].match? /\A#[0-9a-fA-F]{6}\z/
      next unless EYE_COLORS.include? attrs['ecl']
      next unless attrs['pid'].match? /\A\d{9}\z/
    end
    valid += 1
  end
  valid
end

file = File.read('inputs/day4.txt')
puts count_valid_entries(file)
puts count_valid_entries(file, true)