import Foundation let input = loadData(day: 16) let scanner = Scanner(string: input) extension Scanner { func scanRange() -> ClosedRange? { guard let low = scanInt(), string("-"), let high = scanInt() else { return nil } return low...high } func scanFieldDefinition() -> (String, ClosedRange, ClosedRange)? { var location = scanLocation guard let name = scanUpToString(":"), string(":"), let first = scanRange(), string("or"), let second = scanRange() else { scanLocation = location return nil } return (name, first, second) } func scanFieldDefinitions() -> [(String, ClosedRange, ClosedRange)] { var result = [(String, ClosedRange, ClosedRange)]() while let field = scanFieldDefinition() { result.append(field) } return result } func scanTicket() -> [Int]? { var result = [Int]() repeat { guard let field = scanInt() else { return nil } result.append(field) } while string(",") return result } func scanYourTicket() -> [Int]? { guard string("your ticket:"), let ticket = scanTicket() else { return nil } return ticket } func scanNearbyTickets() -> [[Int]]? { guard string("nearby tickets:") else { return nil } var result = [[Int]]() while !isAtEnd { guard let ticket = scanTicket() else { return nil } result.append(ticket) } return result } } let definitions = scanner.scanFieldDefinitions() guard let yourTicket = scanner.scanYourTicket(), let nearbyTickets = scanner.scanNearbyTickets() else { fatalError() } func isValid(_ value: Int) -> Bool { definitions.contains { $0.1.contains(value) || $0.2.contains(value) } } func isValid(_ ticket: [Int]) -> Bool { ticket.allSatisfy(isValid) } let invalidSums = nearbyTickets .flatten() .filter( { !isValid($0) } ) .reduce(0, +) print(invalidSums) var possibleFields: [Set] = Array(repeating: Set(definitions.map { $0.0 }), count: yourTicket.count) for ticket in nearbyTickets where isValid(ticket) { for (index, value) in ticket.enumerated() { for (name, first, second) in definitions { if !(first.contains(value) || second.contains(value)) { possibleFields[index].remove(name) } } } } for index in possibleFields.indices.sorted(by: { possibleFields[$0].count < possibleFields[$1].count }) { guard possibleFields[index].count == 1, let key = possibleFields[index].first else { fatalError() } for i in possibleFields.indices where i != index { possibleFields[i].remove(key) } } let mapping = possibleFields.map { $0.first! } let result = mapping.enumerated() .map { ($0.element, yourTicket[$0.offset]) } .filter { $0.0.hasPrefix("departure") } .map { $0.1 } .reduce(1, *) print(result)