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