diff --git a/AdventOfCode2020.xcodeproj/project.pbxproj b/AdventOfCode2020.xcodeproj/project.pbxproj index 96f9cad..68d9485 100644 --- a/AdventOfCode2020.xcodeproj/project.pbxproj +++ b/AdventOfCode2020.xcodeproj/project.pbxproj @@ -23,6 +23,9 @@ 26BD88A5258C818300E92A8E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26BD88A4258C818300E92A8E /* main.swift */; }; 26BD88BB258C818E00E92A8E /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2AD3A2581713A00702405 /* LoadData.swift */; }; 26BD88BC258C818E00E92A8E /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2ACE5257ED09000702405 /* Extensions.swift */; }; + 26DD9C41258DEAC70082D4F2 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DD9C40258DEAC70082D4F2 /* main.swift */; }; + 26DD9C58258DEACF0082D4F2 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2AD3A2581713A00702405 /* LoadData.swift */; }; + 26DD9C59258DEACF0082D4F2 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2ACE5257ED09000702405 /* Extensions.swift */; }; 26E2ACD8257ECFFA00702405 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2ACD7257ECFFA00702405 /* main.swift */; }; 26E2ACE6257ED09000702405 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2ACE5257ED09000702405 /* Extensions.swift */; }; 26E2ACE7257ED09000702405 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2ACE5257ED09000702405 /* Extensions.swift */; }; @@ -154,6 +157,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 26DD9C3C258DEAC70082D4F2 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 26E2ACD3257ECFFA00702405 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -249,6 +261,8 @@ 26BD8864258B2D5300E92A8E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 26BD88A2258C818300E92A8E /* day18 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = day18; sourceTree = BUILT_PRODUCTS_DIR; }; 26BD88A4258C818300E92A8E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 26DD9C3E258DEAC70082D4F2 /* day19 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = day19; sourceTree = BUILT_PRODUCTS_DIR; }; + 26DD9C40258DEAC70082D4F2 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 26E2ACD5257ECFFA00702405 /* day8 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = day8; sourceTree = BUILT_PRODUCTS_DIR; }; 26E2ACD7257ECFFA00702405 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 26E2ACE5257ED09000702405 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; @@ -340,6 +354,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 26DD9C3B258DEAC70082D4F2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 26E2ACD2257ECFFA00702405 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -437,6 +458,7 @@ 261C554625895FB70035FEC0 /* day16 */, 26BD8863258B2D5300E92A8E /* day17 */, 26BD88A3258C818300E92A8E /* day18 */, + 26DD9C3F258DEAC70082D4F2 /* day19 */, 268D953A25781DD80030EC4D /* Products */, ); sourceTree = ""; @@ -462,6 +484,7 @@ 261C554525895FB70035FEC0 /* day16 */, 26BD8862258B2D5300E92A8E /* day17 */, 26BD88A2258C818300E92A8E /* day18 */, + 26DD9C3E258DEAC70082D4F2 /* day19 */, ); name = Products; sourceTree = ""; @@ -530,6 +553,14 @@ path = day18; sourceTree = ""; }; + 26DD9C3F258DEAC70082D4F2 /* day19 */ = { + isa = PBXGroup; + children = ( + 26DD9C40258DEAC70082D4F2 /* main.swift */, + ); + path = day19; + sourceTree = ""; + }; 26E2ACD6257ECFFA00702405 /* day8 */ = { isa = PBXGroup; children = ( @@ -776,6 +807,23 @@ productReference = 26BD88A2258C818300E92A8E /* day18 */; productType = "com.apple.product-type.tool"; }; + 26DD9C3D258DEAC70082D4F2 /* day19 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 26DD9C44258DEAC70082D4F2 /* Build configuration list for PBXNativeTarget "day19" */; + buildPhases = ( + 26DD9C3A258DEAC70082D4F2 /* Sources */, + 26DD9C3B258DEAC70082D4F2 /* Frameworks */, + 26DD9C3C258DEAC70082D4F2 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = day19; + productName = day19; + productReference = 26DD9C3E258DEAC70082D4F2 /* day19 */; + productType = "com.apple.product-type.tool"; + }; 26E2ACD4257ECFFA00702405 /* day8 */ = { isa = PBXNativeTarget; buildConfigurationList = 26E2ACDB257ECFFA00702405 /* Build configuration list for PBXNativeTarget "day8" */; @@ -951,6 +999,9 @@ 26BD88A1258C818300E92A8E = { CreatedOnToolsVersion = 12.3; }; + 26DD9C3D258DEAC70082D4F2 = { + CreatedOnToolsVersion = 12.3; + }; 26E2ACD4257ECFFA00702405 = { CreatedOnToolsVersion = 12.2; }; @@ -1008,6 +1059,7 @@ 261C554425895FB70035FEC0 /* day16 */, 26BD8861258B2D5300E92A8E /* day17 */, 26BD88A1258C818300E92A8E /* day18 */, + 26DD9C3D258DEAC70082D4F2 /* day19 */, ); }; /* End PBXProject section */ @@ -1113,6 +1165,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 26DD9C3A258DEAC70082D4F2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 26DD9C41258DEAC70082D4F2 /* main.swift in Sources */, + 26DD9C59258DEACF0082D4F2 /* Extensions.swift in Sources */, + 26DD9C58258DEACF0082D4F2 /* LoadData.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 26E2ACD1257ECFFA00702405 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1549,6 +1611,28 @@ }; name = Release; }; + 26DD9C42258DEAC70082D4F2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 722B335UM5; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 26DD9C43258DEAC70082D4F2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 722B335UM5; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 26E2ACD9257ECFFA00702405 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1843,6 +1927,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 26DD9C44258DEAC70082D4F2 /* Build configuration list for PBXNativeTarget "day19" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 26DD9C42258DEAC70082D4F2 /* Debug */, + 26DD9C43258DEAC70082D4F2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 26E2ACDB257ECFFA00702405 /* Build configuration list for PBXNativeTarget "day8" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/day19/main.swift b/day19/main.swift new file mode 100644 index 0000000..9cc9ce3 --- /dev/null +++ b/day19/main.swift @@ -0,0 +1,142 @@ +import Foundation + +let input = loadData(day: 19) +let scanner = Scanner(string: input) +scanner.charactersToBeSkipped = .whitespaces + +enum Rule { + case character(Character) + indirect case sequence(Rule, Rule) + indirect case alternative(Rule, Rule) + case reference(Int) +} + + +typealias RuleSet = [Int: Rule] + +extension Scanner { + func parseRuleSet() -> RuleSet { + var result: [Int: Rule] = [:] + while !string("\n") { + guard let (index, rule) = parseRuleDefinition() else { fatalError() } + result[index] = rule + } + + return result + } + + func parseRuleDefinition() -> (Int, Rule)? { + guard let label = parseLabel(), + let rule = parseRule(), + string("\n") + else { return nil } + return (label, rule) + } + + func parseLabel() -> Int? { + guard let num = scanInt(), + string(":") + else { return nil } + return num + } + + func parseRule() -> Rule? { + if string("\""), let character = scanCharacter(), string("\"") { + return .character(character) + } + + guard let sequence = parseSequence() else { return nil } + + if string("|"), let rhs = parseSequence() { + return .alternative(sequence, rhs) + } + + return sequence + } + + func parseReference() -> Rule? { + guard let num = scanInt() else { return nil } + return .reference(num) + } + + func parseSequence() -> Rule? { + guard var sequence = parseReference() else { + return nil + } + + while let rhs = parseReference() { + sequence = .sequence(sequence, rhs) + } + + return sequence + } +} + +let rules = scanner.parseRuleSet() + +extension Rule { + func matches(rules: RuleSet, _ s: Substring) -> Substring? { + switch self { + case .character(let ch): + if s.first == ch { + return s.dropFirst() + } + + return nil + + case let .sequence(first, second): + if let firstMatch = first.matches(rules: rules, s), let secondMatch = second.matches(rules: rules, firstMatch) { + return secondMatch + } + + return nil + + case let .alternative(first, second): + if let firstMatch = first.matches(rules: rules, s) { + return firstMatch + } + + if let secondMatch = second.matches(rules: rules, s) { + return secondMatch + } + + return nil + + case .reference(let index): + return rules[index]!.matches(rules: rules, s) + } + } + + func matches(rules: RuleSet, _ s: String) -> Bool { + if let result = matches(rules: rules, s[...]), result.isEmpty { + return true + } + + return false + } +} + +extension Scanner { + func scanLine() -> String? { + guard let result = scanUpToCharacters(from: .newlines), + scanCharacters(from: .newlines) != nil else { + return nil + } + return result + } + + func readLines() -> [String] { + + var lines = [String]() + while let line = scanner.scanLine() { + lines.append(line) + } + return lines + } + +} + +let messages = scanner.readLines() + +print("part 1", messages.lazy.filter { rules[0]!.matches(rules: rules, $0) }.count) +