From aa1b1f017b5ac1963ef31b214267f2b7088d13af Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 20 Dec 2020 13:12:31 +0100 Subject: [PATCH] day 20, part 1 --- AdventOfCode2020.xcodeproj/project.pbxproj | 93 ++++++++++ day20/main.swift | 196 +++++++++++++++++++++ 2 files changed, 289 insertions(+) create mode 100644 day20/main.swift diff --git a/AdventOfCode2020.xcodeproj/project.pbxproj b/AdventOfCode2020.xcodeproj/project.pbxproj index 68d9485..2546822 100644 --- a/AdventOfCode2020.xcodeproj/project.pbxproj +++ b/AdventOfCode2020.xcodeproj/project.pbxproj @@ -26,6 +26,9 @@ 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 */; }; + 26DD9C74258F2CEE0082D4F2 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DD9C73258F2CEE0082D4F2 /* main.swift */; }; + 26DD9C8C258F2CF80082D4F2 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E2AD3A2581713A00702405 /* LoadData.swift */; }; + 26DD9C8D258F2CF80082D4F2 /* 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 */; }; @@ -166,6 +169,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 26DD9C6F258F2CEE0082D4F2 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 26E2ACD3257ECFFA00702405 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -263,6 +275,8 @@ 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 = ""; }; + 26DD9C71258F2CEE0082D4F2 /* day20 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = day20; sourceTree = BUILT_PRODUCTS_DIR; }; + 26DD9C73258F2CEE0082D4F2 /* 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 = ""; }; @@ -361,6 +375,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 26DD9C6E258F2CEE0082D4F2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 26E2ACD2257ECFFA00702405 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -459,6 +480,7 @@ 26BD8863258B2D5300E92A8E /* day17 */, 26BD88A3258C818300E92A8E /* day18 */, 26DD9C3F258DEAC70082D4F2 /* day19 */, + 26DD9C72258F2CEE0082D4F2 /* day20 */, 268D953A25781DD80030EC4D /* Products */, ); sourceTree = ""; @@ -485,6 +507,7 @@ 26BD8862258B2D5300E92A8E /* day17 */, 26BD88A2258C818300E92A8E /* day18 */, 26DD9C3E258DEAC70082D4F2 /* day19 */, + 26DD9C71258F2CEE0082D4F2 /* day20 */, ); name = Products; sourceTree = ""; @@ -561,6 +584,14 @@ path = day19; sourceTree = ""; }; + 26DD9C72258F2CEE0082D4F2 /* day20 */ = { + isa = PBXGroup; + children = ( + 26DD9C73258F2CEE0082D4F2 /* main.swift */, + ); + path = day20; + sourceTree = ""; + }; 26E2ACD6257ECFFA00702405 /* day8 */ = { isa = PBXGroup; children = ( @@ -824,6 +855,23 @@ productReference = 26DD9C3E258DEAC70082D4F2 /* day19 */; productType = "com.apple.product-type.tool"; }; + 26DD9C70258F2CEE0082D4F2 /* day20 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 26DD9C77258F2CEE0082D4F2 /* Build configuration list for PBXNativeTarget "day20" */; + buildPhases = ( + 26DD9C6D258F2CEE0082D4F2 /* Sources */, + 26DD9C6E258F2CEE0082D4F2 /* Frameworks */, + 26DD9C6F258F2CEE0082D4F2 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = day20; + productName = day20; + productReference = 26DD9C71258F2CEE0082D4F2 /* day20 */; + productType = "com.apple.product-type.tool"; + }; 26E2ACD4257ECFFA00702405 /* day8 */ = { isa = PBXNativeTarget; buildConfigurationList = 26E2ACDB257ECFFA00702405 /* Build configuration list for PBXNativeTarget "day8" */; @@ -1002,6 +1050,9 @@ 26DD9C3D258DEAC70082D4F2 = { CreatedOnToolsVersion = 12.3; }; + 26DD9C70258F2CEE0082D4F2 = { + CreatedOnToolsVersion = 12.3; + }; 26E2ACD4257ECFFA00702405 = { CreatedOnToolsVersion = 12.2; }; @@ -1060,6 +1111,7 @@ 26BD8861258B2D5300E92A8E /* day17 */, 26BD88A1258C818300E92A8E /* day18 */, 26DD9C3D258DEAC70082D4F2 /* day19 */, + 26DD9C70258F2CEE0082D4F2 /* day20 */, ); }; /* End PBXProject section */ @@ -1175,6 +1227,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 26DD9C6D258F2CEE0082D4F2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 26DD9C74258F2CEE0082D4F2 /* main.swift in Sources */, + 26DD9C8D258F2CF80082D4F2 /* Extensions.swift in Sources */, + 26DD9C8C258F2CF80082D4F2 /* LoadData.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 26E2ACD1257ECFFA00702405 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1633,6 +1695,28 @@ }; name = Release; }; + 26DD9C75258F2CEE0082D4F2 /* 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; + }; + 26DD9C76258F2CEE0082D4F2 /* 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 = { @@ -1936,6 +2020,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 26DD9C77258F2CEE0082D4F2 /* Build configuration list for PBXNativeTarget "day20" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 26DD9C75258F2CEE0082D4F2 /* Debug */, + 26DD9C76258F2CEE0082D4F2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 26E2ACDB257ECFFA00702405 /* Build configuration list for PBXNativeTarget "day8" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/day20/main.swift b/day20/main.swift new file mode 100644 index 0000000..7db0727 --- /dev/null +++ b/day20/main.swift @@ -0,0 +1,196 @@ +import Foundation + +let input = loadData(day: 20) +let scanner = Scanner(string: input) + + +struct Tile { + var id: Int + var imageData: [Bool] + var size: Int + + subscript (_ x: Int, _ y: Int) -> Bool { + get { imageData[x + y * size] } + set { imageData[x + y * size] = newValue } + } +} + + +extension Scanner { + func tile() -> Tile? { + guard string("Tile"), + let id = scanInt(), + string(":"), + let (image, size) = imageData() + else { return nil } + return Tile(id: id, imageData: image, size: size) + } + + func imageData() -> ([Bool], Int)? { + var result: [Bool] = [] + let set = CharacterSet(charactersIn: "#.") + var width: Int? = nil + while let line = scanCharacters(from: set) { + assert(width == nil || line.count == width) + width = line.count + result.append(contentsOf: line.map { $0 == "#" }) + } + + guard let w = width else { return nil } + + return (result, w) + } + + func tileSet() -> [Int: Tile]? { + var result: [Int: Tile] = [:] + while !isAtEnd { + guard let tile = self.tile() else { return nil } + result[tile.id] = tile + } + return result + } +} + +let tilesById = scanner.tileSet()! + +struct TileReference: Hashable { + enum Rotation: CaseIterable { + case rotate0, rotate90, rotate180, rotate270 + } + + var id: Int + var flipped: Bool + var rotation: Rotation + + subscript (x: Int, y: Int) -> Bool { + get { + let tile = tilesById[id]! + + var coord = flipped ? (x: tile.size - 1 - x, y: y) : (x: x, y: y) + coord = rotation.get(x: coord.x, y: coord.y, size: tile.size) + + return tile[coord.x, coord.y] + } + } + + var end: Int { tilesById[id]!.size - 1 } +} + +extension TileReference.Rotation { + func get(x: Int, y: Int, size: Int) -> (x: Int, y: Int) { + let e = size - 1 + switch self { + case .rotate0: return (x, y) + case .rotate90: return (y, e - x) + case .rotate180: return (x, e - y) + case .rotate270: return (e - y, x) + } + } +} + +struct Board { + struct Cell { + var possible: Array + } + + var size: Int + var cells: [Cell] + + init(tiles: T) where T: Sequence, T.Element == Int { + let orientations = [true, false].flatMap { flipped in TileReference.Rotation.allCases.map { (flipped, $0) } } + let allOptions = tiles.flatMap { id in orientations.map { TileReference(id: id, flipped: $0.0, rotation: $0.1 ) } } + size = Int(sqrt(Double(tilesById.count))) + cells = Array(repeating: Cell(possible: allOptions), count: size*size) + assert(size * size == tilesById.count) + } + + subscript(x: Int, y: Int) -> Cell { + get { cells[x + y * size] } + set { cells[x + y * size] = newValue } + } + + func coordinate(_ cell: Int) -> (x: Int, y: Int) { + let (y, x) = cell.quotientAndRemainder(dividingBy: size) + return (x, y) + } + + func solve(startingAt: Int = 0) -> Board? { + if startingAt >= cells.count { return self } + + let coord = coordinate(startingAt) + let left = coord.x > 0 ? self[coord.x - 1, coord.y].chosen! : nil + let above = coord.y > 0 ? self[coord.x, coord.y - 1].chosen! : nil + + func matchLeft(_ ref: TileReference) -> Bool { + guard let left = left else { return true } + return (0...ref.end).allSatisfy { left[left.end, $0] == ref[0, $0] } + } + + func matchAbove(_ ref: TileReference) -> Bool { + guard let above = above else { return true } + return (0...ref.end).allSatisfy { above[$0, above.end] == ref[$0, 0] } + } + + for option in cells[startingAt].possible.lazy.filter({ matchLeft($0) && matchAbove($0) }) { + var result = self + result.cells[startingAt].possible = [option] + + let next = startingAt + 1 + + for cell in next..