diff --git a/day21/d21p2.swift b/day21/d21p2.swift new file mode 100644 index 0000000..4794db2 --- /dev/null +++ b/day21/d21p2.swift @@ -0,0 +1,187 @@ +import Foundation + +typealias Key = String +let (nA, nEmpty) = ("A", "") +let (n7, n8, n9, n4, n5, n6, n1, n2, n3, n0) = + ("7", "8", "9", "4", "5", "6", "1", "2", "3", "0") +let (nL, nR, nU, nD) = ("<", ">", "^", "v") + +protocol Pad { + static func seq(_ k1: Key, _ k2: Key) -> Set<[Key]> +} + +struct KeyPair : Hashable { + let a: Key + let b: Key +} + +struct Numpad : Pad { + static let pos: [Key: (Int, Int)] = [ + n7: (0, 0), n8: (0, 1), n9: (0, 2), + n4: (1, 0), n5: (1, 1), n6: (1, 2), + n1: (2, 0), n2: (2, 1), n3: (2, 2), + n0: (3, 1), nA: (3, 2) + ] + static var cache: [KeyPair: Set<[Key]>] = [:] + + static func seq(_ k1: Key, _ k2: Key) -> Set<[Key]> { + let cacheKey = KeyPair(a: k1, b: k2) + if let cached = cache[cacheKey] { + return cached + } + let (pos1, pos2) = (pos[k1]!, pos[k2]!) + let verKey = pos1.0 < pos2.0 ? nD : nU + let horKey = pos1.1 < pos2.1 ? nR : nL + let seqs: [[Key]] = [ // ??? + Array(repeating: verKey, count: abs(pos1.0 - pos2.0)) + + Array(repeating: horKey, count: abs(pos1.1 - pos2.1)), + Array(repeating: horKey, count: abs(pos1.1 - pos2.1)) + + Array(repeating: verKey, count: abs(pos1.0 - pos2.0)) + ].filter { seq in + (k1 != n1 || !seq.starts(with: [nD])) && + (k1 != n4 || !seq.starts(with: [nD, nD])) && + (k1 != n7 || !seq.starts(with: [nD, nD, nD])) && + (k1 != n0 || !seq.starts(with: [nL])) && + (k1 != nA || !seq.starts(with: [nL, nL])) + } + cache[cacheKey] = Set(seqs) + return cache[cacheKey]! + } + +} + +struct Dirpad : Pad { + static let pos: [Key: (Int, Int)] = [ + nU: (0, 1), nA: (0, 2), + nL: (1, 0), nD: (1, 1), nR: (1, 2) + ] + static var cache: [KeyPair: Set<[Key]>] = [ + // >^ >A >< >v (>>) + // v< v> v^ vA? (vv) + // Only solutions due to gap + KeyPair(a: nL, b: nU): [[nR, nU]], + KeyPair(a: nL, b: nA): [[nR, nR, nU]], + KeyPair(a: nU, b: nL): [[nD, nL]], + KeyPair(a: nA, b: nL): [[nD, nL, nL]], + // Only solutions + KeyPair(a: nL, b: nR): [[nR, nR]], + KeyPair(a: nR, b: nL): [[nL, nL]], + KeyPair(a: nL, b: nD): [[nR]], + KeyPair(a: nD, b: nL): [[nL]], + KeyPair(a: nU, b: nA): [[nR]], + KeyPair(a: nA, b: nU): [[nL]], + KeyPair(a: nD, b: nR): [[nR]], + KeyPair(a: nR, b: nD): [[nL]], + KeyPair(a: nA, b: nR): [[nD]], + KeyPair(a: nR, b: nA): [[nU]], + KeyPair(a: nU, b: nD): [[nD]], + KeyPair(a: nD, b: nU): [[nU]], + // Known short ones + KeyPair(a: nA, b: nD): [[nL, nD]], + KeyPair(a: nR, b: nU): [[nL, nU]], + KeyPair(a: nU, b: nR): [[nD, nR]] + ] + + static func seq(_ k1: Key, _ k2: Key) -> Set<[Key]> { + let cacheKey = KeyPair(a: k1, b: k2) + if let cached = cache[cacheKey] { + return cached + } + let (pos1, pos2) = (pos[k1]!, pos[k2]!) + let verKey = pos1.0 < pos2.0 ? nD : nU + let horKey = pos1.1 < pos2.1 ? nR : nL + let seqs: [[Key]] = [ // ??? + Array(repeating: verKey, count: abs(pos1.0 - pos2.0)) + + Array(repeating: horKey, count: abs(pos1.1 - pos2.1)), + Array(repeating: horKey, count: abs(pos1.1 - pos2.1)) + + Array(repeating: verKey, count: abs(pos1.0 - pos2.0)) + ].filter { seq in + (k1 != nU || !seq.starts(with: [nL])) && + (k1 != nA || !seq.starts(with: [nL, nL])) && + (k1 != nL || !seq.starts(with: [nU])) + } + cache[cacheKey] = Set(seqs) + return cache[cacheKey]! + } +} + +struct CacheKey : Hashable { + let a: Key + let b: ArraySlice +} + +var pressCache: [CacheKey: [[Key]]] = [:] +extension Pad { + static func press(_ start: Key, _ keys: ArraySlice) -> [[Key]] { + if keys.count == 0 { + return [[]] + } + if let cached = pressCache[CacheKey(a: start, b: keys)] { + return cached + } + pressCache[CacheKey(a: start, b: keys)] = + seq(start, keys[keys.startIndex]).flatMap { firstSeq in + press(keys[keys.startIndex], keys.dropFirst()).map { restSeq in + firstSeq + [nA] + restSeq + } + } + return pressCache[CacheKey(a: start, b: keys)]! + } +} + +func readInput(_ filePath: String) throws -> [[Key]] { + return try String(contentsOfFile: filePath, encoding: .ascii) + .split(separator: "\n").map { Array($0).map(String.init) } +} +/* +Dirpad.press(nA, ">^A".map(String.init)[...]) + .map { $0.joined() }.forEach { print(">^A: \($0.count): \($0)".replacingOccurrences(of: "A", with: "|")) } +Dirpad.press(nA, "v<^AA>A".map(String.init)[...]) + .map { $0.joined() }.forEach { print("v<^AA>A: \($0.count): \($0)".replacingOccurrences(of: "A", with: "|")) } +*/ + + +//let answer = try readInput(CommandLine.arguments[1]).map { passwd in +// Numpad.press(nA, passwd[...]).flatMap { dirSeq1 in +let _ = [[">", "A"]].map { passwd in + Dirpad.press(nA, passwd[...]).flatMap { dirSeq1 in + Dirpad.press(nA, dirSeq1[...]).flatMap { dirSeq2 in + Dirpad.press(nA, dirSeq2[...]).flatMap { dirSeq3 in + Dirpad.press(nA, dirSeq3[...]).flatMap { dirSeq4 in + Dirpad.press(nA, dirSeq4[...]).flatMap { dirSeq5 in + let out: String = ( + "\(passwd.joined()) " + + "\(dirSeq1.joined()) " + + "\(dirSeq2.joined()) " + + "\(dirSeq3.joined()) " + + "\(dirSeq4.joined()) " + + "\(dirSeq5.joined()) " + + "" + ) + print(out.replacingOccurrences(of: "A", with: "|")) + } + } + } + } + } + } + + +var answer = 0 +for passwd in try readInput(CommandLine.arguments[1]) { + var seq = Numpad.press(nA, passwd[...]) + seq.map { $0.joined() }.forEach { print("\($0.count): \($0)") } + print() + for _ in 0..<2 { + let tmpSeq = seq.flatMap { seq in Dirpad.press(nA, seq[...]) } + let minLen = seq.map { $0.count }.min()! + seq = tmpSeq.filter { $0.count > minLen } + seq.map { $0.joined() }.forEach { print("\($0.count): \($0)") } + print() + } + let r = seq.map { $0.count }.min()! * Int(passwd.joined().dropLast())! + print("\(passwd.joined()): \(r)") + answer += r +} +print(answer) +