Skip to content

Commit

Permalink
day 21
Browse files Browse the repository at this point in the history
  • Loading branch information
gereons committed Dec 21, 2024
1 parent 2adb2bf commit b2976cf
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 7 deletions.
119 changes: 116 additions & 3 deletions Sources/Day21/Day21.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,129 @@
import AoCTools

final class Day21: AOCDay {
let title = ""
let title = "Keypad Conundrum"

let codes: [String]
typealias KeyPair = Pair<Character, Character>
let numPadSteps: [KeyPair: [String]]
let dPadSteps: [KeyPair: [String]]

init(input: String) {
codes = input.lines

let numberKeys = """
789
456
123
X0A
"""
self.numPadSteps = Self.generateSteps(str: numberKeys)

let dPadKeys = """
X^A
<v>
"""
self.dPadSteps = Self.generateSteps(str: dPadKeys)
}

struct State: Hashable {
let prevChar: Character
let thisChar: Character
let levels: Int
}

private var cache = [State: Int]()

func part1() -> Int {
0
solve(levels: 3)
}

func part2() -> Int {
0
solve(levels: 26)
}

private func solve(levels: Int) -> Int {
var sum = 0
for code in codes {
var value = 0
var prevChar: Character = "A"
for c in code {
value += numberOfKeyPresses(
map: numPadSteps,
state: State(prevChar: prevChar, thisChar: c, levels: levels)
)
prevChar = c
}
let complexity = value * Int(code.replacingOccurrences(of: "A", with: ""))!
sum += complexity
}
return sum
}

private func numberOfKeyPresses(map: [KeyPair: [String]], state: State) -> Int {
if let result = cache[state] {
return result
}

if state.levels == 0 {
return 1
}

var best = Int.max
for steps in map[KeyPair(state.prevChar, state.thisChar)]! {
var value = 0
var prevChar: Character = "A"
for c in steps {
value += numberOfKeyPresses(
map: dPadSteps,
state: State(prevChar: prevChar, thisChar: c, levels: state.levels - 1)
)
prevChar = c
}
if value < best {
best = value
}
}
cache[state] = best
return best
}

static func generateSteps(str: String) -> [KeyPair: [String]] {
let chars = str.replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: "X", with: "")
let grid = Grid<Character>.parse(str)
var steps = [KeyPair: [String]]()
for start in chars {
for end in chars {
steps[KeyPair(start, end)] = generateSteps(map: grid.points, start: start, end: end)
}
}
return steps
}

static func generateSteps(map: [Point: Character], start: Character, end: Character, visited: Set<Character> = []) -> [String] {
if start == end { return ["A"] }

let startPoint = map.first { $0.value == start }!.key
var results = [String]()
for dir in Direction.orthogonal {
guard let nextChar = map[startPoint + dir] else { continue }
if nextChar != "X" && !visited.contains(nextChar) {
let nextSteps = generateSteps(map: map, start: nextChar, end: end, visited: visited + start)
results.append(contentsOf: nextSteps.map { dir.char + $0 })
}
}
return results
}
}

extension Direction {
var char: String {
switch self {
case .n: "^"
case .e: ">"
case .w: "<"
case .s: "v"
default: fatalError()
}
}
}
13 changes: 9 additions & 4 deletions Tests/Day21Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,36 @@ import Testing
@testable import AdventOfCode

fileprivate let testInput = """
029A
980A
179A
456A
379A
"""

@Suite("Day 21 Tests")
struct Day21Tests {
@MainActor @Test("Day 21 Part 1")
func testDay21_part1() {
let day = Day21(input: testInput)
#expect(day.part1() == 0)
#expect(day.part1() == 126384)
}

@MainActor @Test("Day 21 Part 1 Solution")
func testDay21_part1_solution() {
let day = Day21(input: Day21.input)
#expect(day.part1() == 0)
#expect(day.part1() == 123096)
}

@MainActor @Test("Day 21 Part 2")
func testDay21_part2() {
let day = Day21(input: testInput)
#expect(day.part2() == 0)
#expect(day.part2() == 154115708116294)
}

@MainActor @Test("Day 21 Part 2 Solution")
func testDay21_part2_solution() {
let day = Day21(input: Day21.input)
#expect(day.part2() == 0)
#expect(day.part2() == 154517692795352)
}
}

0 comments on commit b2976cf

Please sign in to comment.