100 lines
2.7 KiB
Swift
100 lines
2.7 KiB
Swift
|
import Foundation
|
|||
|
|
|||
|
let input = loadData(day: 4)
|
|||
|
|
|||
|
let scanner = Scanner(string: input)
|
|||
|
scanner.charactersToBeSkipped = nil
|
|||
|
|
|||
|
extension Scanner {
|
|||
|
func field() -> (String, String)? {
|
|||
|
guard let name = scanUpToString(":"),
|
|||
|
scanString(":") != nil,
|
|||
|
let value = scanUpToCharacters(from: .whitespacesAndNewlines)
|
|||
|
else { return nil }
|
|||
|
return (name, value)
|
|||
|
}
|
|||
|
|
|||
|
func passport() -> [String: String]? {
|
|||
|
var fields = [(String, String)]()
|
|||
|
while !isAtEnd && scanString("\n\n") == nil {
|
|||
|
_ = scanner.scanCharacters(from: .whitespacesAndNewlines)
|
|||
|
guard let field = field() else { return nil }
|
|||
|
fields.append(field)
|
|||
|
}
|
|||
|
return try? Dictionary(fields, uniquingKeysWith: { _, _ in throw NSError(domain: "error", code: 1, userInfo: nil) })
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var valids = 0
|
|||
|
var valid2 = 0
|
|||
|
|
|||
|
let requiredFields = [
|
|||
|
"byr",
|
|||
|
"iyr",
|
|||
|
"eyr",
|
|||
|
"hgt",
|
|||
|
"hcl",
|
|||
|
"ecl",
|
|||
|
"pid",
|
|||
|
]
|
|||
|
|
|||
|
func validHeight(_ height: String) -> Bool {
|
|||
|
let scanner = Scanner(string: height)
|
|||
|
guard let number = scanner.scanInt() else { return false }
|
|||
|
if scanner.scanString("cm") != nil, 150...193 ~= number {
|
|||
|
return true
|
|||
|
}
|
|||
|
|
|||
|
if scanner.scanString("in") != nil, 59...76 ~= number {
|
|||
|
return true
|
|||
|
}
|
|||
|
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
func validHairColor(_ hcl: String) -> Bool {
|
|||
|
let scanner = Scanner(string: hcl)
|
|||
|
guard scanner.scanString("#") != nil else { return false }
|
|||
|
guard scanner.scanCharacters(from: CharacterSet(charactersIn: "0123456789abcdef"))?.count == 6 else { return false }
|
|||
|
return scanner.isAtEnd
|
|||
|
}
|
|||
|
|
|||
|
let validEyeColors: Set<String> = [
|
|||
|
"amb",
|
|||
|
"blu",
|
|||
|
"brn",
|
|||
|
"gry",
|
|||
|
"grn",
|
|||
|
"hzl",
|
|||
|
"oth"
|
|||
|
]
|
|||
|
|
|||
|
func isValidPassportId(_ pid: String) -> Bool {
|
|||
|
return pid.count == 9 && Int(pid) != nil
|
|||
|
}
|
|||
|
|
|||
|
while !scanner.isAtEnd {
|
|||
|
guard let passport = scanner.passport() else { fatalError() }
|
|||
|
print(passport)
|
|||
|
|
|||
|
let valid = requiredFields.allSatisfy { passport.keys.contains($0) }
|
|||
|
if valid {
|
|||
|
valids += 1
|
|||
|
}
|
|||
|
|
|||
|
guard let year = passport["byr"].flatMap(Int.init), 1920...2002 ~= year else { continue }
|
|||
|
guard let issueYear = passport["iyr"].flatMap(Int.init), 2010...2020 ~= issueYear else { continue }
|
|||
|
guard let expireYear = passport["eyr"].flatMap(Int.init), 2020...2030 ~= expireYear else { continue }
|
|||
|
guard let height = passport["hgt"], validHeight(height) else { continue }
|
|||
|
guard let hairColor = passport["hcl"], validHairColor(hairColor) else { continue }
|
|||
|
guard let eyeColor = passport["ecl"], validEyeColors.contains(eyeColor) else { continue }
|
|||
|
guard let pid = passport["pid"], isValidPassportId(pid) else { continue }
|
|||
|
|
|||
|
valid2 += 1
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
print("valids", valids)
|
|||
|
print("valid2", valid2)
|
|||
|
|