Compare commits
	
		
			4 Commits
		
	
	
		
			20c02557bd
			...
			4c2c6244db
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4c2c6244db | |||
| 9a19ca9251 | |||
| 4a16309479 | |||
| 450b7184e6 | 
							
								
								
									
										106
									
								
								day21/d21p1.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								day21/d21p1.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | 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 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 func seq(_ k1: Key, _ k2: Key) -> Set<[Key]> { | ||||||
|  |         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])) | ||||||
|  |             } | ||||||
|  |         return Set(seqs) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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 func seq(_ k1: Key, _ k2: Key) -> Set<[Key]> { | ||||||
|  |         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])) | ||||||
|  |             } | ||||||
|  |         return Set(seqs) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | extension Pad { | ||||||
|  |     static func press(_ start: Key, _ keys: ArraySlice<Key>) -> [[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 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func readInput(_ filePath: String) throws -> [[Key]] { | ||||||
|  |     return try String(contentsOfFile: filePath, encoding: .ascii) | ||||||
|  |         .split(separator: "\n").map { Array($0).map(String.init) } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | let answer = try readInput(CommandLine.arguments[1]).map { passwd in | ||||||
|  |         Numpad.press(nA, passwd[...]).flatMap { dirSeq1 in | ||||||
|  |             Dirpad.press(nA, dirSeq1[...]).flatMap { dirSeq2 in | ||||||
|  |                 Dirpad.press(nA, dirSeq2[...]) | ||||||
|  |             } | ||||||
|  |         }.map { $0.count }.min()! * Int(passwd.joined().dropLast())! | ||||||
|  |     }.reduce(0, +) | ||||||
|  | print(answer) | ||||||
|  |  | ||||||
|  | //}.forEach { line in | ||||||
|  | //    print() | ||||||
|  | //    line.map { $0.joined() }.forEach { print("\($0.count): \($0)") } | ||||||
|  | //} | ||||||
|  |              | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Numpad.press(nA, [n0, n2, n9, nA]) | ||||||
|  |     .map { $0.joined() }.forEach { print("\($0.count): \($0)") } | ||||||
|  | print() | ||||||
|  | Dirpad.press(nA, "<A^A^^>AvvvA".map(String.init)[...]) | ||||||
|  |     .map { $0.joined() }.forEach { print("\($0.count): \($0)") } | ||||||
|  | print() | ||||||
|  | */ | ||||||
|  |  | ||||||
							
								
								
									
										178
									
								
								day21/d21p2.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								day21/d21p2.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | |||||||
|  | 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 { | ||||||
|  |     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]! | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static func press(_ start: Key, _ keys: ArraySlice<Key>) -> [[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 { | ||||||
|  |     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: [Key]] = [ | ||||||
|  |         // <<0 <v- <>- <^. <A. | ||||||
|  |         // v<- vv0 v>- 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): [], | ||||||
|  |         // 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. 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] | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     static func seq(_ k1: Key, _ k2: Key) -> [Key] { | ||||||
|  |         return cache[KeyPair(a: k1, b: k2)]! | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static func press(_ start: Key, _ keys: ArraySlice<Key>) -> [Key] { | ||||||
|  |         if keys.count == 0 { | ||||||
|  |             return [] | ||||||
|  |         } | ||||||
|  |         return seq(start, keys[keys.startIndex]) + [nA] + | ||||||
|  |             press(keys[keys.startIndex], keys.dropFirst()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func readInput(_ filePath: String) throws -> [[Key]] { | ||||||
|  |     return try String(contentsOfFile: filePath, encoding: .ascii) | ||||||
|  |         .split(separator: "\n").map { Array($0).map(String.init) } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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) } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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]) { | ||||||
|  |     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) | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								day21/input.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								day21/input.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | 169A | ||||||
|  | 279A | ||||||
|  | 540A | ||||||
|  | 869A | ||||||
|  | 789A | ||||||
							
								
								
									
										5
									
								
								day21/test.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								day21/test.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | 029A | ||||||
|  | 980A | ||||||
|  | 179A | ||||||
|  | 456A | ||||||
|  | 379A | ||||||
		Reference in New Issue
	
	Block a user