Advent of Code (Day - 4 Passport Processing)
Tags: adventofcode, challenge
Published: 2020-12-09 06:46:47

To solve this challenge visit https://adventofcode.com/2020/day/4

You can find my solutions for other problems at : 
https://github.com/Rage-ops/Advent-of-code-2020

---

The challenge is to validate passports

## Part 1
Detecting which passports have all required fields. The expected fields are as follows:
```
byr (Birth Year)
iyr (Issue Year)
eyr (Expiration Year)
hgt (Height)
hcl (Hair Color)
ecl (Eye Color)
pid (Passport ID)
cid (Country ID)
```
Passport data is validated in batch files (your puzzle input). Each passport is represented as a sequence of `key:value` pairs separated by spaces or newlines. Passports are separated by blank lines.

Here is an example batch file containing four passports:
```
ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm

iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
hcl:#cfa07d byr:1929

hcl:#ae17e1 iyr:2013
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm

hcl:#cfa07d eyr:2025 pid:166559648
iyr:2011 ecl:brn hgt:59in
```
The first passport is valid - all eight fields are present. The second passport is invalid - it is missing `hgt` (the Height field).

The third passport is interesting; the only missing field is `cid`, so it looks like data from North Pole Credentials, not a passport at all! Surely, nobody would mind if you made the system temporarily ignore missing cid fields. Treat this "passport" as valid.

The fourth passport is missing two fields, `cid` and `byr`. Missing `cid` is fine, but missing any other field is not, so this passport is invalid.

According to the above rules, your improved system would report 2 valid passports.

Count the number of valid passports - those that have all required fields. Treat `cid` as optional. In your batch file, how many passports are valid?

## Solution in Python

### Parsing Input File
```python
import re

with open("Problem-4/Day 4: Passport Processing.txt") as file:
    # As passports are separated by blank line
    data = file.read().split("\n\n")
    # Now data will have a list of all passports
```
### Solution Part 1
```python
import re

with open("Problem-4/Day 4: Passport Processing.txt") as file:
    # As passports are separated by blank line
    data = file.read().split("\n\n")
    # Now data will have a list of all passports

required_keys =  ['byr', 'iyr', 'eyr', 'hgt', 'hcl', 'ecl', 'pid']
out = 0

for passport in data:
        # key:value pairs are separated by spaces and newlines
        # Replacing those spaces and newlines which separate key:value pairs by ", "
        row = re.sub(r'[\n ]{1,}', ", ", passport) + ","
        #  Extracting key: value pairs from the passport
        #  re.findall will return a list of tuples where each tuple has two elements key and value respectively
        # dict function will generate a dictonary from those list of tuples
        d = dict(re.findall(r'([a-z]+):(.*?),', row))
        # If all required keys are present in the passport
        if all([key in d.keys() for key in required_keys]):
            out += 1
print('There are %d valid passports according to part 1.' % out)
```
## Part 2
Checking data validation for required fields.
You can continue to ignore the cid field, but each other field has strict rules about what values are valid for automatic validation:

```
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.
```
Your job is to count the passports where all required fields are both present and valid according to the above rules. Here are some example values:
```
byr valid:   2002
byr invalid: 2003

hgt valid:   60in
hgt valid:   190cm
hgt invalid: 190in
hgt invalid: 190

hcl valid:   #123abc
hcl invalid: #123abz
hcl invalid: 123abc

ecl valid:   brn
ecl invalid: wat

pid valid:   000000001
pid invalid: 0123456789
```
Here are some invalid passports:
```
eyr:1972 cid:100
hcl:#18171d ecl:amb hgt:170 pid:186cm iyr:2018 byr:1926

iyr:2019
hcl:#602927 eyr:1967 hgt:170cm
ecl:grn pid:012533040 byr:1946

hcl:dab227 iyr:2012
ecl:brn hgt:182cm pid:021572410 eyr:2020 byr:1992 cid:277

hgt:59cm ecl:zzz
eyr:2038 hcl:74454a iyr:2023
pid:3556412378 byr:2007
```
Here are some valid passports:
```
pid:087499704 hgt:74in ecl:grn iyr:2012 eyr:2030 byr:1980
hcl:#623a2f

eyr:2029 ecl:blu cid:129 byr:1989
iyr:2014 pid:896056539 hcl:#a97842 hgt:165cm

hcl:#888785
hgt:164cm byr:2001 iyr:2015 cid:88
pid:545766238 ecl:hzl
eyr:2022

iyr:2010 hgt:158cm hcl:#b6652a ecl:blu byr:1944 eyr:2021 pid:093154719
```
Count the number of valid passports - those that have all required fields and valid values. Continue to treat cid as optional. In your batch file, how many passports are valid?

### Solution with Part1 and Part 2
```python
import re

with open("Problem-4/Day 4: Passport Processing.txt") as file:
    # As passports are separated by blank line
    data = file.read().split("\n\n")
    # Now data will have a list of all passports

required_keys =  ['byr', 'iyr', 'eyr', 'hgt', 'hcl', 'ecl', 'pid']
out, out2 = 0, 0

for passport in data:
        # key:value pairs are separated by spaces and newlines
        # Replacing those spaces and newlines which separate key:value pairs by ", "
        row = re.sub(r'[\n ]{1,}', ", ", passport) + ","
        #  Extracting key: value pairs from the passport
        #  re.findall will return a list of tuples where each tuple has two elements key and value respectively
        # dict function will generate a dictonary from those list of tuples
        d = dict(re.findall(r'([a-z]+):(.*?),', row))
        # If all required keys are present in the passport
        if all([key in d.keys() for key in required_keys]):
            out += 1
            # Checking Validation
            if (2002 >= int(d['byr']) >= 1920 and 2020 >= int(d['iyr']) >= 2010
                and 2030 >= int(d['eyr']) >= 2020 and d['ecl'] in ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]
                and re.search(r'^#[0-9a-f]{6}$', d['hcl']) and re.match('^\d{9}$', d['pid'])):

                height = d['hgt']
                if (height[-2:] == 'cm' and 150 <= int(height[:-2]) <= 193) or (height[-2:] == 'in' and 59 <= int(height[:-2]) <= 76):
                    out2 += 1
print('There are %d valid passports according to part 1.' % out)
print('There are %d valid passports according to part 2.' % out2)
```
---
## Solution with Part 1 and Part 2 in Javascript
```javascript
const fs = require('fs');
const data = fs.readFileSync("Problem-4/Day 4: Passport Processing.txt").toString().split('\n\n');
const required_keys =  ['byr', 'iyr', 'eyr', 'hgt', 'hcl', 'ecl', 'pid'];
let [out1, out2] = [0, 0];

data.forEach((PassportData) => {
    const row = PassportData.split(/[\n ]{1,}/g);
    const entries = row.reduce((accum, info) => {
        accum.push(info.split(":"));
        return accum;
    }, []);
    const d = Object.fromEntries(entries);
    const keys = Object.keys(d);
    let flag = true;
    required_keys.forEach((key) => {
        if (! keys.includes(key)) {
            flag = false;
            return
        }
    })
    if(flag){
        
        out1++;
        if (2002 >= Number(d['byr']) &&  Number(d['byr']) >= 1920
            && 2020 >= Number(d['iyr']) && Number(d['iyr']) >= 2010
            && 2030 >= Number(d['eyr']) && Number(d['eyr'])>= 2020 
            && ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"].includes(d['ecl'])
            && d['hcl'].match(/^#[0-9a-f]{6}$/) 
            && d['pid'].match(/^\d{9}$/)
            ){
            const height = d['hgt'];
            if ((height.slice(-2) === 'cm' && 150 <= Number(height.slice(0,-2)) &&  Number(height.slice(0,-2)) <= 193)
            || (height.slice(-2) === 'in' && 59 <= Number(height.slice(0,-2)) && Number(height.slice(0,-2)) <= 76)){
                
                out2++;
            }
        }
    }    
})
console.log(`There are ${out1} valid passports according to part 1.`);
console.log(`There are ${out2} valid passports according to part 2.`);
```