diff --git a/day21/d21p2.swift b/day21/d21p2.swift index 2dd431a..159a857 100644 --- a/day21/d21p2.swift +++ b/day21/d21p2.swift @@ -15,7 +15,7 @@ struct KeyPair : Hashable { let b: Key } -struct Numpad : Pad { +struct Numpad { static let pos: [Key: (Int, Int)] = [ n7: (0, 0), n8: (0, 1), n9: (0, 2), n4: (1, 0), n5: (1, 1), n6: (1, 2), @@ -48,94 +48,70 @@ struct Numpad : Pad { return cache[cacheKey]! } + static func press(_ start: Key, _ keys: ArraySlice) -> [[Key]] { + if keys.count == 0 { + return [[]] + } + return seq(start, keys[keys.startIndex]).flatMap { firstSeq in + press(keys[keys.startIndex], keys.dropFirst()).map { restSeq in + firstSeq + [nA] + restSeq + } + } + } } -struct Dirpad : Pad { +struct Dirpad { 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]>] = [ + static var cache: [KeyPair: [Key]] = [ // <<0 - <^. - v^- vA // ><- >v- >>0 >^ >A- // ^<. ^v- ^> ^^0 ^A- // A<. Av A>- A^- AA0 // Noops - KeyPair(a: nL, b: nL): [[]], - KeyPair(a: nR, b: nR): [[]], - KeyPair(a: nU, b: nU): [[]], - KeyPair(a: nD, b: nD): [[]], - KeyPair(a: nA, b: nA): [[]], + KeyPair(a: nL, b: nL): [], + KeyPair(a: nR, b: nR): [], + KeyPair(a: nU, b: nU): [], + KeyPair(a: nD, b: nD): [], + KeyPair(a: nA, b: nA): [], // 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]], + 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]], + 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. TODO: proper DP this part instead of by hand - KeyPair(a: nA, b: nD): [[nL, nD]], - KeyPair(a: nD, b: nA): [[nU, nR]], - KeyPair(a: nR, b: nU): [[nL, nU]], - KeyPair(a: nU, b: nR): [[nD, nR]] + KeyPair(a: nA, b: nD): [nL, nD], + KeyPair(a: nD, b: nA): [nU, nR], + 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]! + static func seq(_ k1: Key, _ k2: Key) -> [Key] { + return cache[KeyPair(a: k1, b: k2)]! } -} -struct CacheKey : Hashable { - let a: Key - let b: ArraySlice -} - -var pressCache: [CacheKey: [[Key]]] = [:] -extension Pad { - static func press(_ start: Key, _ keys: ArraySlice) -> [[Key]] { + static func press(_ start: Key, _ keys: ArraySlice) -> [Key] { if keys.count == 0 { - return [[]] + 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)]! + return seq(start, keys[keys.startIndex]) + [nA] + + press(keys[keys.startIndex], keys.dropFirst()) } } @@ -143,57 +119,60 @@ 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: "|")) } -*/ +extension Array { + func combos(l: Int) -> [[Element]] { + guard count > 0 else { return [[]] } + if l == 0 { return [[]] } + return flatMap { e in + combos(l: l - 1).map { $0 + [e] } + } + } + func combos(maxl: Int) -> [[Element]] { + return (0...maxl).flatMap { combos(l: $0) } + } +} -//let answer = try readInput(CommandLine.arguments[1]).map { passwd in -// Numpad.press(nA, passwd[...]).flatMap { dirSeq1 in -let _ = [["v", "A"]].map { passwd in - Dirpad.press(nA, passwd[...]).forEach { dirSeq1 in - Dirpad.press(nA, dirSeq1[...]).forEach { dirSeq2 in - Dirpad.press(nA, dirSeq2[...]).forEach { dirSeq3 in - Dirpad.press(nA, dirSeq3[...]).forEach { dirSeq4 in - Dirpad.press(nA, dirSeq4[...]).forEach { dirSeq5 in - //Dirpad.press(nA, dirSeq4[...]).forEach { dirSeq6 in - let out: String = ( - "\(passwd.joined()) " + - "\(dirSeq1.joined()) " + - "\(dirSeq2.joined()) " + - "\(dirSeq3.joined()) " + - "\(dirSeq4.joined()) " + - "\(dirSeq5.count) " + - // "\(dirSeq6.count) " + - "" - ).replacingOccurrences(of: "A", with: "|") - print("\(dirSeq5.count)" + out) - //} - } - } - } - } +struct LKs : Hashable { + let l: Int + let ks: [Key] +} + +// Returns a map of a sequence of pressed starting at A and ending at A +// to how many key presses total for that +func bestLevel1() -> [LKs: Int] { + var res: [LKs: Int] = [:] + [nL, nR, nU, nD].combos(maxl: 3).forEach { seq in + res[LKs(l: 1, ks: seq)] = Dirpad.press(nA, seq + [nA]).count } + return res +} +func best(maxLevel: Int) -> [LKs: Int] { + var res = bestLevel1() + if maxLevel == 1 { return res } + (2...maxLevel).forEach { level in + [nL, nR, nU, nD].combos(maxl: 3).forEach { seq in + res[LKs(l: level, ks: Array(seq))] = Dirpad.press(nA, seq + [nA]) + .split(separator: "A", omittingEmptySubsequences: false).dropLast() + .map { res[LKs(l: level-1, ks: Array($0))]! }.reduce(0, +) + } } + return res +} + +let levels = Int(CommandLine.arguments[2])! - 1 +let bests = best(maxLevel: levels) 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..<25 { - 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 + let n = Numpad.press(nA, passwd[...]) + .map { Dirpad.press(nA, $0[...]) }.map { ks in + return ks + .split(separator: "A", omittingEmptySubsequences: false).dropLast() + .map { bests[LKs(l: levels, ks: Array($0))]! } + .reduce(0, +) + }.min()! + answer += n * Int(passwd.joined().dropLast())! } print(answer)