-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday4.nim
144 lines (130 loc) · 3.74 KB
/
day4.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import re
import strutils
import tables
# Passport fields
type
PassportField = enum
byr,
iyr,
eyr,
hgt,
hcl,
ecl,
pid,
cid
# Set of required fields
let requiredFields = {byr, iyr, eyr, hgt, hcl, ecl, pid}
# Parse passport fields in line
proc getFieldsInLine(line: string, fieldSet: var set[PassportField]) =
# Find field names using regex
for match in line.findAll(re"(\s+|^)\S+:"):
# Extract field name from match
let fieldName = match[^4..^2]
# Parse field name
let field = parseEnum[PassportField](fieldName)
# Include in set
fieldSet.incl(field)
# Parse passports file
let inputFile = "resources/day4_input.txt"
var passportFields: set[PassportField] # Empty passport field set
var validPassportCtr = 0 # Valid passport counter
for line in lines inputFile:
# Check last passport and create new passport if empty line found
if line.len == 0:
# Check if required fields is a subset of the passport fields
if requiredFields <= passportFields:
inc(validPassportCtr)
# Create new empty passport
passportFields = {}
else:
# Parse passport line
getFieldsInLine(line, passportFields)
# Check last passport
if requiredFields <= passportFields:
inc(validPassportCtr)
# Print results
echo "--- Part 1 Report ---"
echo "Valid passports found = " & $validPassportCtr
## Part 2
# Parse line with both field names and values
proc parseLine(line: string, passport: var Table[PassportField, string]) =
# Find field names using regex
for match in line.findAll(re"(?!\s+|^)?(\S+:\S+)(?=\s+|$)"):
# Separate field name and value
let splitMatch = match.split(':')
let fieldName = splitMatch[0]
let fieldValue = splitMatch[1]
let field = parseEnum[PassportField](fieldName)
# Store
passport[field] = fieldValue
# Validate field
proc validateField(field: PassportField, value: string): bool =
case field
# Birth year
of byr:
let numValue = parseInt(value)
result = numValue in 1920 .. 2020
# Issue year
of iyr:
let numValue = parseInt(value)
result = numValue in 2010 .. 2020
# Expiration year
of eyr:
let numValue = parseInt(value)
result = numValue in 2020 .. 2030
# Height
of hgt:
var
numValue: int
unit: string
let splitValue = value.findAll(re"((\d+)|(in|cm))")
if splitValue.len != 2:
return false
numValue = splitValue[0].parseInt
unit = splitValue[1]
if unit == "cm":
result = numValue in 150 .. 193
elif unit == "in":
result = numValue in 59 .. 76
else:
result = false
# Hair color
of hcl:
result = value.match(re"#[0-9a-f]{6}")
# Eye color
of ecl:
let allowedValues = ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]
result = value in allowedValues
# Passport ID
of pid:
result = value.match(re"\d{9}")
# Country ID
of cid:
result = true
# Check all passports
var passport: Table[PassportField, string] # Empty passport
validPassportCtr = 0 # Reset counter
for line in lines inputFile:
# Check last passport and create new passport if empty line found
if line.len == 0:
# Check if required fields is a subset of the passport fields
# and validate field values
var fieldList: set[PassportField]
for field in passport.keys:
fieldList.incl(field)
if requiredFields <= fieldList:
var validFields = true
for k, v in passport.pairs:
if not validateField(k, v):
validFields = false
break
if validFields:
inc(validPassportCtr)
# Create new empty passport
passport = initTable[PassportField, string]()
else:
# Parse passport line
parseLine(line, passport)
# Print results
echo "--- Part 2 Report ---"
echo "Valid passports found = " & $validPassportCtr