This commit is contained in:
Sven Weidauer 2021-12-23 20:20:12 +01:00
parent 67d4d86e81
commit ba6cee935a
2 changed files with 369 additions and 0 deletions

View file

@ -7,6 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
26132D372774C886004F0228 /* day23.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26132D2B2774C871004F0228 /* day23.swift */; };
26132D382774C886004F0228 /* common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269BE5CB2762A08800871C85 /* common.swift */; };
2615545A276A6C2C00374D18 /* day14.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26155459276A6C2C00374D18 /* day14.swift */; }; 2615545A276A6C2C00374D18 /* day14.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26155459276A6C2C00374D18 /* day14.swift */; };
2615545B276A6C3200374D18 /* common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269BE5CB2762A08800871C85 /* common.swift */; }; 2615545B276A6C3200374D18 /* common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269BE5CB2762A08800871C85 /* common.swift */; };
26155468276A6D0A00374D18 /* day15.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26155467276A6D0A00374D18 /* day15.swift */; }; 26155468276A6D0A00374D18 /* day15.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26155467276A6D0A00374D18 /* day15.swift */; };
@ -44,6 +46,15 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
26132D2E2774C87D004F0228 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
26155450276A6C1C00374D18 /* CopyFiles */ = { 26155450276A6C1C00374D18 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -200,6 +211,8 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
26132D2B2774C871004F0228 /* day23.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = day23.swift; sourceTree = "<group>"; };
26132D302774C87D004F0228 /* Day23 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Day23; sourceTree = BUILT_PRODUCTS_DIR; };
26155452276A6C1C00374D18 /* Day14 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Day14; sourceTree = BUILT_PRODUCTS_DIR; }; 26155452276A6C1C00374D18 /* Day14 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Day14; sourceTree = BUILT_PRODUCTS_DIR; };
26155459276A6C2C00374D18 /* day14.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = day14.swift; sourceTree = "<group>"; }; 26155459276A6C2C00374D18 /* day14.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = day14.swift; sourceTree = "<group>"; };
26155460276A6CF700374D18 /* Day15 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Day15; sourceTree = BUILT_PRODUCTS_DIR; }; 26155460276A6CF700374D18 /* Day15 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Day15; sourceTree = BUILT_PRODUCTS_DIR; };
@ -238,6 +251,13 @@
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
26132D2D2774C87D004F0228 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
2615544F276A6C1C00374D18 /* Frameworks */ = { 2615544F276A6C1C00374D18 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -363,6 +383,7 @@
263BA594275E974800839C92 = { 263BA594275E974800839C92 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
26132D2B2774C871004F0228 /* day23.swift */,
2680ECFB27732A9400CAB23C /* day22.swift */, 2680ECFB27732A9400CAB23C /* day22.swift */,
2680ECE22771D82400CAB23C /* day21.swift */, 2680ECE22771D82400CAB23C /* day21.swift */,
2680ECDF2770A2FC00CAB23C /* day20.swift */, 2680ECDF2770A2FC00CAB23C /* day20.swift */,
@ -405,6 +426,7 @@
2680ECD82770A2E900CAB23C /* Day20 */, 2680ECD82770A2E900CAB23C /* Day20 */,
2680ECE72771D82F00CAB23C /* Day21 */, 2680ECE72771D82F00CAB23C /* Day21 */,
2680ECF427732A8300CAB23C /* Day22 */, 2680ECF427732A8300CAB23C /* Day22 */,
26132D302774C87D004F0228 /* Day23 */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@ -412,6 +434,23 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
26132D2F2774C87D004F0228 /* Day23 */ = {
isa = PBXNativeTarget;
buildConfigurationList = 26132D342774C87D004F0228 /* Build configuration list for PBXNativeTarget "Day23" */;
buildPhases = (
26132D2C2774C87D004F0228 /* Sources */,
26132D2D2774C87D004F0228 /* Frameworks */,
26132D2E2774C87D004F0228 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = Day23;
productName = Day23;
productReference = 26132D302774C87D004F0228 /* Day23 */;
productType = "com.apple.product-type.tool";
};
26155451276A6C1C00374D18 /* Day14 */ = { 26155451276A6C1C00374D18 /* Day14 */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 26155458276A6C1C00374D18 /* Build configuration list for PBXNativeTarget "Day14" */; buildConfigurationList = 26155458276A6C1C00374D18 /* Build configuration list for PBXNativeTarget "Day14" */;
@ -712,6 +751,9 @@
LastUpgradeCheck = 1310; LastUpgradeCheck = 1310;
ORGANIZATIONNAME = 5sw; ORGANIZATIONNAME = 5sw;
TargetAttributes = { TargetAttributes = {
26132D2F2774C87D004F0228 = {
CreatedOnToolsVersion = 13.2.1;
};
26155451276A6C1C00374D18 = { 26155451276A6C1C00374D18 = {
CreatedOnToolsVersion = 13.1; CreatedOnToolsVersion = 13.1;
LastSwiftMigration = 1310; LastSwiftMigration = 1310;
@ -806,11 +848,21 @@
2680ECD72770A2E900CAB23C /* Day20 */, 2680ECD72770A2E900CAB23C /* Day20 */,
2680ECE62771D82F00CAB23C /* Day21 */, 2680ECE62771D82F00CAB23C /* Day21 */,
2680ECF327732A8300CAB23C /* Day22 */, 2680ECF327732A8300CAB23C /* Day22 */,
26132D2F2774C87D004F0228 /* Day23 */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
26132D2C2774C87D004F0228 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26132D372774C886004F0228 /* day23.swift in Sources */,
26132D382774C886004F0228 /* common.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2615544E276A6C1C00374D18 /* Sources */ = { 2615544E276A6C1C00374D18 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -967,6 +1019,30 @@
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
26132D352774C87D004F0228 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 722B335UM5;
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 12.1;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
26132D362774C87D004F0228 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 722B335UM5;
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 12.1;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
26155456276A6C1C00374D18 /* Debug */ = { 26155456276A6C1C00374D18 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@ -1616,6 +1692,15 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
26132D342774C87D004F0228 /* Build configuration list for PBXNativeTarget "Day23" */ = {
isa = XCConfigurationList;
buildConfigurations = (
26132D352774C87D004F0228 /* Debug */,
26132D362774C87D004F0228 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
26155458276A6C1C00374D18 /* Build configuration list for PBXNativeTarget "Day14" */ = { 26155458276A6C1C00374D18 /* Build configuration list for PBXNativeTarget "Day14" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (

284
day23.swift Normal file
View file

@ -0,0 +1,284 @@
@main
struct Day23: Puzzle {
func run() {
part1()
part2()
}
func part1() {
var board = Board(height: 3)
board.fillHomeRow(for: .a, pieces: [.c, .c])
board.fillHomeRow(for: .b, pieces: [.b, .d])
board.fillHomeRow(for: .c, pieces: [.a, .a])
board.fillHomeRow(for: .d, pieces: [.d, .b])
board.show()
print("Part 1:", find(start: board))
}
func part2() {
var board = Board(height: 5)
board.fillHomeRow(for: .a, pieces: [.c, .d, .d, .c])
board.fillHomeRow(for: .b, pieces: [.b, .c, .b, .d])
board.fillHomeRow(for: .c, pieces: [.a, .b, .a, .a])
board.fillHomeRow(for: .d, pieces: [.d, .a, .c, .b])
board.show()
print("Part 2:", find(start: board))
}
func find(start: Board) -> Int {
let target = Board.makeGoal(height: start.height)
var current = start
var totalCost = 0
var possible: [Board: Int] = [:]
var visited: Set<Board> = []
while current != target {
visited.insert(current)
for (next, cost) in current.possibleMoves() where !visited.contains(next) {
possible[next] = min(possible[next, default: .max], totalCost + cost)
}
(current, totalCost) = possible.min { $0.value < $1.value }!
possible.removeValue(forKey: current)
}
return totalCost
}
}
struct Board: Hashable {
enum Piece: CaseIterable, Hashable, CustomStringConvertible {
case a, b, c, d
var homeColumn: Int {
switch self {
case .a:
return 2
case .b:
return 4
case .c:
return 6
case .d:
return 8
}
}
var cost: Int {
switch self {
case .a:
return 1
case .b:
return 10
case .c:
return 100
case .d:
return 1000
}
}
var sign: Character {
switch self {
case .a:
return "A"
case .b:
return "B"
case .c:
return "C"
case .d:
return "D"
}
}
var description: String { String(sign) }
}
enum Cell: Hashable {
case piece(Piece)
case empty
case outside
}
var width = 11
var height = 3
var board: [Cell]
init(height: Int) {
self.height = height
board = Array(repeating: .outside, count: width * height)
for x in 0..<width {
self[x, 0] = .empty
}
}
mutating func fillHomeRow(for piece: Piece, pieces: [Piece]) {
precondition(pieces.count == height - 1)
let x = piece.homeColumn
for (y, piece) in pieces.enumerated() {
self[x, y + 1] = .piece(piece)
}
}
static func makeGoal(height: Int) -> Self {
var board = Self(height: height)
for piece in Piece.allCases {
for y in 1..<board.height {
board[piece.homeColumn, y] = .piece(piece)
}
}
return board
}
func movablePieces() -> [(Piece, Int, Int)] {
var result: [(Piece, Int, Int)] = []
for x in 0..<width {
if case .piece(let piece) = self[x, 0] {
result.append((piece, x, 0))
}
}
for home in Piece.allCases {
let x = home.homeColumn
var y = 1
while y < height, case .empty = self[x, y] {
y += 1
}
if y < height, case let .piece(piece) = self[x, y] {
result.append((piece, x, y))
}
}
return result
}
func possibleMoves() -> [(Board, Int)] {
let pieces = movablePieces()
.filter { !isHome(piece: $0.0, x: $0.1, y: $0.2) }
var result: [(Board, Int)] = []
for (piece, x, y) in pieces {
if let newY = freeHomeRowPosition(piece: piece), freeCorridor(from: x, to: piece.homeColumn) {
result.append(move(piece: piece, from: (x, y), to: (piece.homeColumn, newY)))
}
for newX in openCorridorPositions(x: x) {
result.append(move(piece: piece, from: (x, y), to: (newX, 0)))
}
}
return result
}
func freeCorridor(from x0: Int, to x1: Int) -> Bool {
let minX: Int
let maxX: Int
if x0 < x1 { (minX, maxX) = (x0 + 1, x1) }
else { (minX, maxX) = (x1, x0 - 1) }
for x in minX...maxX {
if self[x, 0] != .empty {
return false
}
}
return true
}
func move(piece: Piece, from: (Int, Int), to: (Int, Int)) -> (Board, Int) {
let (x, y) = from
let (newX, newY) = to
precondition(self[newX,newY] == .empty)
let cost = (y + distance((x, 0), (newX, newY))) * piece.cost
var board = self
board[x, y] = .empty
board[newX, newY] = .piece(piece)
return (board, cost)
}
func freeHomeRowPosition(piece: Piece) -> Int? {
let x = piece.homeColumn
var depth = height - 1
loop: while depth > 0 {
switch self[x, depth] {
case .piece(piece): break
case .piece: return nil
case .empty: break loop
case .outside: preconditionFailure("Invalid board")
}
depth -= 1
}
return depth
}
static let homeColumns = Set(Piece.allCases.map(\.homeColumn))
func openCorridorPositions(x: Int) -> [Int] {
var xmin = x
var xmax = x
while xmin > 0 && self[xmin, 0] == .empty {
xmin -= 1
}
while xmax < width && self[xmax, 0] == .empty {
xmax += 1
}
let result = (xmin..<xmax).filter { !Self.homeColumns.contains($0) && self[$0,0] == .empty }
return result
}
func distance(_ a: (Int, Int), _ b: (Int, Int)) -> Int {
let (x0, y0) = a
let (x1, y1) = b
return abs(x1 - x0) + abs(y1 - y0)
}
func isHome(piece: Piece, x: Int, y: Int) -> Bool {
guard x == piece.homeColumn && y > 0 else { return false }
for otherY in (y + 1)..<height {
if self[x, otherY] != .piece(piece) {
return false
}
}
return true
}
func show() {
print(String(repeating: "#", count: width + 2))
for y in 0..<height {
var line = ""
for x in 0..<width {
switch self[x, y] {
case .outside:
line += "#"
case .empty: line += "."
case .piece(let piece): line.append(piece.sign)
}
}
print("#\(line)#")
}
print(String(repeating: "#", count: width + 2))
}
subscript(x: Int, y: Int) -> Cell {
get {
board[x + width * y]
}
set {
board[x + width * y] = newValue
}
}
}