Skip to content

Commit fb2225c

Browse files
committed
Solution Day 21, part one (Keypad Conundrum)
1 parent e65f3f4 commit fb2225c

File tree

3 files changed

+118
-69
lines changed

3 files changed

+118
-69
lines changed

src/main/kotlin/de/ronny_h/aoc/extensions/SimpleCharGrid.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package de.ronny_h.aoc.extensions
22

3-
class SimpleCharGrid(input: List<String>, nullElement: Char = '#') : Grid<Char>(input, nullElement) {
3+
open class SimpleCharGrid(input: List<String>, nullElement: Char = '#') : Grid<Char>(input, nullElement) {
44
override fun Char.toElementType() = this
55

66
fun shortestPaths(start: Coordinates, goal: Coordinates): List<ShortestPath<Coordinates>> {
Lines changed: 48 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
package de.ronny_h.aoc.year24.day21
22

33
import de.ronny_h.aoc.AdventOfCode
4-
import de.ronny_h.aoc.extensions.Coordinates
54
import de.ronny_h.aoc.extensions.Direction
6-
import de.ronny_h.aoc.extensions.Grid
7-
import de.ronny_h.aoc.extensions.ShortestPath
8-
import de.ronny_h.aoc.extensions.aStar
5+
import de.ronny_h.aoc.extensions.Direction.*
6+
import de.ronny_h.aoc.extensions.SimpleCharGrid
97
import de.ronny_h.aoc.extensions.asList
108

11-
fun main() = KeypadConundrum().run(0, 0)
9+
fun main() = KeypadConundrum().run(94426, 0)
1210

1311
class KeypadConundrum : AdventOfCode<Int>(2024, 21) {
14-
override fun part1(input: List<String>): Int {
15-
println("directionalKeypad: $directionalKeypadLayout")
1612

17-
val numericKeypad = Keypad(numericKeypadLayout)
18-
val sequence = input.map { code ->
19-
val sequence1 = code.map { position ->
20-
numericKeypad.moveTo(position)
21-
}.joinToString("")
22-
println("robot1: $sequence1")
23-
24-
val directionalKeypad1 = Keypad(directionalKeypadLayout)
25-
val sequence2 = sequence1.map { direction ->
26-
directionalKeypad1.moveTo(direction)
27-
}.joinToString("")
28-
println("robot2: $sequence2")
13+
data class Node(val code: String, val children: List<Node>) {
14+
fun collectSequences(level: Int = 0): String {
15+
if (children.isEmpty()) {
16+
check(level % 2 == 0)
17+
return code
18+
}
19+
return if (level % 2 == 0) {
20+
// direct children = sequence
21+
children.joinToString("") { it.collectSequences(level + 1) }
22+
} else {
23+
// direct children = different options (branches in the tree)
24+
children
25+
.map { it.collectSequences(level + 1) }
26+
.minBy { it.length }
27+
}
28+
}
29+
}
2930

30-
val directionalKeypad2 = Keypad(directionalKeypadLayout)
31-
val sequence3 = sequence2.map { direction ->
32-
directionalKeypad2.moveTo(direction)
33-
}.joinToString("")
34-
println("$code: $sequence3")
31+
fun input(code: String, keypad: Keypad, depth: Int): Node {
32+
if (depth == 0) {
33+
return Node(code, emptyList())
3534
}
35+
return Node(code, code.map { char ->
36+
Node("$char", keypad.moveTo(char).map {
37+
input(it, Keypad(directionalKeypadLayout), depth - 1)
38+
})
39+
})
40+
}
3641

37-
return 0
42+
override fun part1(input: List<String>): Int = input.sumOf {
43+
input(it, Keypad(numericKeypadLayout), 3).collectSequences().length * it.dropLast(1).toInt()
3844
}
3945

4046
override fun part2(input: List<String>): Int {
@@ -72,46 +78,28 @@ val directionalKeypadLayout = """
7278
<v>
7379
""".asList()
7480

75-
class Keypad(layout: List<String>) : Grid<Char>(layout, ' ') {
76-
override fun Char.toElementType(): Char = this
77-
78-
data class Node(val direction: Direction, val position: Coordinates) {
79-
override fun toString() = "$position$direction"
80-
}
81+
class Keypad(layout: List<String>) : SimpleCharGrid(layout, ' ') {
82+
override fun toString(): String = toString(setOf(currentPosition), 'X')
8183

8284
private var currentPosition = find('A')
8385

84-
fun moveTo(target: Char): String {
86+
fun moveTo(target: Char): List<String> {
8587
val targetPosition = find(target)
86-
val path = shortestPath(currentPosition, targetPosition)
88+
val paths = shortestPaths(currentPosition, targetPosition)
8789
currentPosition = targetPosition
88-
val directions = path.path.drop(1).joinToString("") {
89-
when (it.direction) {
90-
Direction.NORTH -> "^"
91-
Direction.EAST -> ">"
92-
Direction.SOUTH -> "v"
93-
Direction.WEST -> "<"
94-
}
90+
val directions = paths.map { shortestPath ->
91+
shortestPath.path
92+
.windowed(2)
93+
.map { (from, to) -> Direction.entries.first { from + it == to } }
94+
.joinToString("") {
95+
when (it) {
96+
NORTH -> "^"
97+
EAST -> ">"
98+
SOUTH -> "v"
99+
WEST -> "<"
100+
}
101+
}
95102
}
96-
return "${directions}A"
97-
}
98-
99-
fun shortestPath(start: Coordinates, goal: Coordinates): ShortestPath<Node> {
100-
101-
val neighbours: (Node) -> List<Node> = { node ->
102-
Direction
103-
.entries
104-
.map { Node(it, node.position + it) }
105-
.filter { getAt(it.position) != nullElement }
106-
}
107-
108-
val d: (Node, Node) -> Int = { a, b ->
109-
// pre-condition: a and b are neighbours
110-
1
111-
}
112-
113-
val h: (Node) -> Int = { it.position taxiDistanceTo goal }
114-
115-
return aStar(Node(Direction.EAST, start), { this.position == goal }, neighbours, d, h)
103+
return directions.map { "${it}A" }
116104
}
117105
}
Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package de.ronny_h.aoc.year24.day21
22

33
import de.ronny_h.aoc.extensions.asList
4+
import de.ronny_h.aoc.year24.day21.KeypadConundrum.Node
45
import io.kotest.core.spec.style.StringSpec
6+
import io.kotest.matchers.collections.shouldBeIn
7+
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
58
import io.kotest.matchers.shouldBe
69

710
class KeypadConundrumTest : StringSpec({
@@ -15,21 +18,79 @@ class KeypadConundrumTest : StringSpec({
1518

1619
"make the 1st robot type 029A" {
1720
val numericKeypad = Keypad(numericKeypadLayout)
18-
numericKeypad.moveTo('0') shouldBe "<A"
19-
numericKeypad.moveTo('2') shouldBe "^A"
20-
numericKeypad.moveTo('9') shouldBe "^^>A"
21-
numericKeypad.moveTo('A') shouldBe "vvvA"
21+
numericKeypad.moveTo('0') shouldBe listOf("<A")
22+
numericKeypad.moveTo('2') shouldBe listOf("^A")
23+
numericKeypad.moveTo('9') shouldContainExactlyInAnyOrder listOf("^^>A", "^>^A", ">^^A")
24+
numericKeypad.moveTo('A') shouldBe listOf("vvvA")
2225
}
2326

2427
"make the 2nd robot type ^^>A" {
2528
val directionalKeypad = Keypad(directionalKeypadLayout)
26-
directionalKeypad.moveTo('^') shouldBe "<A"
27-
directionalKeypad.moveTo('^') shouldBe "A"
28-
directionalKeypad.moveTo('>') shouldBe ">vA"
29-
directionalKeypad.moveTo('A') shouldBe "^A"
29+
directionalKeypad.moveTo('^') shouldBe listOf("<A")
30+
directionalKeypad.moveTo('^') shouldBe listOf("A")
31+
directionalKeypad.moveTo('>') shouldContainExactlyInAnyOrder listOf(">vA", "v>A")
32+
directionalKeypad.moveTo('A') shouldBe listOf("^A")
33+
}
34+
35+
"input v<<A>>^A, last robot" {
36+
KeypadConundrum().input("v<<A>>^A", Keypad(directionalKeypadLayout), 1).collectSequences() shouldBeIn
37+
listOf(
38+
"<vA<AA>>^AvAA<^A>A",
39+
"v<A<AA>^>AvAA^<A>A",
40+
)
41+
}
42+
43+
"input 0, all robots" {
44+
KeypadConundrum().input("0", Keypad(numericKeypadLayout), 3).collectSequences() shouldBeIn
45+
listOf(
46+
"<vA<AA>>^AvAA<^A>A",
47+
"v<A<AA>^>AvAA^<A>A",
48+
)
49+
}
50+
51+
"input with depth 1" {
52+
KeypadConundrum().input("v", Keypad(directionalKeypadLayout), 1) shouldBe
53+
Node("v", listOf(Node("v", listOf(Node("v<A", emptyList()), Node("<vA", emptyList())))))
3054
}
3155

3256
"part 1: The sum of the complexities of the five codes" {
3357
KeypadConundrum().part1(input) shouldBe 126384
3458
}
3559
})
60+
61+
62+
/*
63+
Example Nodes tree for input("<", Keypad(directionalKeypadLayout), depth = 2):
64+
Node(code=<, children=[
65+
Node(code=<, children=[
66+
Node(code=v<<A, children=[
67+
Node(code=v, children=[
68+
Node(code=v<A, children=[]), Node(code=<vA, children=[])
69+
]),
70+
Node(code=<, children=[
71+
Node(code=<A, children=[])
72+
]),
73+
Node(code=<, children=[
74+
Node(code=A, children=[])
75+
]),
76+
Node(code=A, children=[
77+
Node(code=>^>A, children=[]), Node(code=>>^A, children=[])
78+
])
79+
]),
80+
Node(code=<v<A, children=[
81+
Node(code=<, children=[
82+
Node(code=v<<A, children=[]), Node(code=<v<A, children=[])
83+
]),
84+
Node(code=v, children=[
85+
Node(code=>A, children=[])
86+
]),
87+
Node(code=<, children=[
88+
Node(code=<A, children=[])
89+
]),
90+
Node(code=A, children=[
91+
Node(code=>^>A, children=[]), Node(code=>>^A, children=[])
92+
])
93+
])
94+
])
95+
])
96+
*/

0 commit comments

Comments
 (0)