Skip to content

Commit 6cbe2db

Browse files
committed
Refactoring tests for performance.
1 parent 0e1d0af commit 6cbe2db

File tree

6 files changed

+107
-82
lines changed

6 files changed

+107
-82
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Here are the solutions I have implemented along with the time it took to run eac
2525
| [Day 3: Mull It Over](src/test/kotlin/adventofcode/Day03Test.kt) | 39 |
2626
| [Day 4: Ceres Search](src/test/kotlin/adventofcode/Day04Test.kt) | 120 |
2727
| [Day 5: Print Queue](src/test/kotlin/adventofcode/Day05Test.kt) | 50 |
28-
| [Day 6: Guard Gallivant](src/test/kotlin/adventofcode/Day06Test.kt) | 6,359 |
28+
| [Day 6: Guard Gallivant](src/test/kotlin/adventofcode/Day06Test.kt) | 778 |
2929
| [Day 7: Bridge Repair](src/test/kotlin/adventofcode/Day07Test.kt) | 69 |
3030
| [Day 8: Resonant Collinearity](src/test/kotlin/adventofcode/Day08Test.kt) | 77 |
3131
| [Day 9: Disk Fragmenter](src/test/kotlin/adventofcode/Day09Test.kt) | 1,012 |

src/main/kotlin/adventofcode/day06/GuardGallivant.kt

Lines changed: 43 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18,76 +18,66 @@ package adventofcode.day06
1818

1919
import adventofcode.util.geom.plane.Direction
2020
import adventofcode.util.geom.plane.Point2D
21+
import adventofcode.util.geom.plane.Pose
2122
import adventofcode.util.grid.TextGrid
2223

23-
private fun Direction.turnRight() = when (this) {
24-
Direction.NORTH -> Direction.EAST
25-
Direction.EAST -> Direction.SOUTH
26-
Direction.SOUTH -> Direction.WEST
27-
Direction.WEST -> Direction.NORTH
28-
Direction.NORTHEAST -> Direction.SOUTHEAST
29-
Direction.SOUTHEAST -> Direction.SOUTHWEST
30-
Direction.SOUTHWEST -> Direction.NORTHWEST
31-
Direction.NORTHWEST -> Direction.NORTHEAST
32-
}
33-
3424
private const val OBSTACLE = '#'
3525
private const val OPEN = '.'
3626

37-
private fun TextGrid.isObstacle(position: Point2D) = position in this && this[position] == OBSTACLE
27+
private class Guard(input: String) {
28+
29+
private val lab = TextGrid(input.lines())
30+
private val start = Pose(lab.coordinates().first { lab[it] == '^' }, Direction.NORTH)
3831

39-
private class Guard {
40-
fun patrol(map: TextGrid) = sequence {
41-
var step = Step(map.coordinates().first { map[it] == '^' }, Direction.NORTH)
42-
while (step.position in map) {
43-
yield(step)
44-
val nextStep = step.nextStep()
45-
step = if (map.isObstacle(nextStep.position)) {
46-
step.turnRight()
47-
} else {
48-
nextStep
49-
}
32+
private fun Pose.patrolProtocol(): Pose {
33+
val fwd = forward()
34+
if(fwd.position !in lab) {
35+
return fwd
36+
}
37+
return if (lab[fwd.position] == OBSTACLE) {
38+
turnRight()
39+
} else {
40+
fwd
5041
}
5142
}
52-
}
5343

54-
private data class Step(val position: Point2D, val direction: Direction) {
55-
fun nextStep() = copy(position = position + direction)
56-
fun turnRight() = copy(direction = direction.turnRight())
57-
}
58-
59-
private fun Sequence<Step>.isLoop(): Boolean {
60-
val visited = mutableSetOf<Step>()
61-
for (step in this) {
62-
if (step in visited) {
63-
return true
44+
fun patrol(): List<Point2D> {
45+
var current = start
46+
val visited = mutableSetOf<Pose>()
47+
val path = mutableListOf<Point2D>()
48+
while (current.position in lab && current !in visited) {
49+
visited.add(current)
50+
path.add(current.position)
51+
current = current.patrolProtocol()
6452
}
65-
visited.add(step)
53+
path.add(current.position)
54+
return path
6655
}
67-
return false
68-
}
6956

70-
private fun <T> TextGrid.withObstacleAt(position: Point2D, function: (TextGrid) -> T): T {
71-
try {
72-
this[position] = OBSTACLE
73-
return function(this)
74-
} finally {
75-
this[position] = OPEN
57+
fun patrolWithObstacleAt(position: Point2D): List<Point2D> {
58+
val original = lab[position]
59+
try {
60+
lab[position] = OBSTACLE
61+
return patrol()
62+
} finally {
63+
lab[position] = original
64+
}
7665
}
66+
67+
fun isOpen(position:Point2D): Boolean = lab[position] == OPEN
68+
fun isInLab(position:Point2D): Boolean = position in lab
7769
}
7870

7971
fun String.countObstacleLocationsThatCauseLoops(): Int {
80-
val map = TextGrid(this.lines())
81-
val guard = Guard()
82-
val candidates = guard.patrol(map)
83-
.map { it.position }
84-
.filter { map[it] == OPEN }
72+
val guard = Guard(this)
73+
return guard.patrol()
74+
.dropLast(1)
75+
.filter { guard.isOpen(it) }
8576
.toSet()
86-
return candidates
87-
.count { location -> map.withObstacleAt(location) { guard.patrol(it).isLoop() } }
77+
.count { guard.isInLab(guard.patrolWithObstacleAt(it).last()) }
8878
}
8979

90-
fun String.calculatePatrolPathLength() = Guard().patrol(TextGrid(this.lines()))
91-
.map { it.position }
80+
fun String.calculatePatrolPathLength() = Guard(this)
81+
.patrol()
9282
.toSet()
93-
.size
83+
.size - 1

src/main/kotlin/adventofcode/day16/ReindeerMaze.kt

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package adventofcode.day16
1818

1919
import adventofcode.util.geom.plane.Direction
20-
import adventofcode.util.geom.plane.Point2D
20+
import adventofcode.util.geom.plane.Pose
2121
import adventofcode.util.graph.Graphs
2222
import adventofcode.util.graph.ShortestPaths
2323
import adventofcode.util.grid.TextGrid
@@ -26,29 +26,8 @@ fun String.countBestTiles(): Int = ReindeerMaze(this).countBestTiles()
2626

2727
fun String.findLowestScore() = ReindeerMaze(this).findLowestScore()
2828

29-
data class Pose(val position: Point2D, val orientation: Direction) {
30-
fun goForward() = Pose(position + orientation, orientation)
31-
fun goBack() = Pose(position - orientation, orientation)
32-
fun turnLeft() = Pose(
33-
position, when (orientation) {
34-
Direction.NORTH -> Direction.WEST
35-
Direction.WEST -> Direction.SOUTH
36-
Direction.SOUTH -> Direction.EAST
37-
else -> Direction.NORTH
38-
}
39-
)
40-
41-
fun turnRight() = Pose(
42-
position, when (orientation) {
43-
Direction.NORTH -> Direction.EAST
44-
Direction.EAST -> Direction.SOUTH
45-
Direction.SOUTH -> Direction.WEST
46-
else -> Direction.NORTH
47-
}
48-
)
29+
fun Pose.distanceTo(other: Pose) = if(orientation == other.orientation) 1.0 else 1000.0
4930

50-
fun distanceTo(other: Pose) = if (orientation == other.orientation) 1.0 else 1000.0
51-
}
5231

5332
class Traversal(val path: List<Pose>, val distance: Double) {
5433
val visited = path.toSet()
@@ -64,8 +43,8 @@ class ReindeerMaze(input: String) {
6443

6544
private fun Pose.isWall() = maze[position] == WALL
6645

67-
fun successorsOf(pose: Pose) = neighborsFn(END) { it.goForward() }(pose)
68-
fun predecessorsOf(pose: Pose) = neighborsFn(START) { it.goBack() }(pose)
46+
fun successorsOf(pose: Pose) = neighborsFn(END) { it.forward() }(pose)
47+
fun predecessorsOf(pose: Pose) = neighborsFn(START) { it.backward() }(pose)
6948

7049
private fun neighborsFn(goal: Char, advance: (Pose) -> Pose): (Pose) -> List<Pose> = { state ->
7150
val neighbors = mutableListOf<Pose>()

src/main/kotlin/adventofcode/util/geom/plane/Direction.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,37 @@ enum class Direction(val dx: Int, val dy: Int) {
2727
NORTHWEST(-1, -1);
2828

2929
fun isDiagonal() = dx != 0 && dy != 0
30+
31+
fun turnRight() = when(this) {
32+
NORTH -> EAST
33+
EAST -> SOUTH
34+
SOUTH -> WEST
35+
WEST -> NORTH
36+
NORTHEAST -> SOUTHEAST
37+
SOUTHEAST -> SOUTHWEST
38+
SOUTHWEST -> NORTHWEST
39+
NORTHWEST -> NORTHEAST
40+
}
41+
42+
fun turnLeft() = when(this) {
43+
NORTH -> WEST
44+
WEST -> SOUTH
45+
SOUTH -> EAST
46+
EAST -> NORTH
47+
NORTHEAST -> NORTHWEST
48+
NORTHWEST -> SOUTHWEST
49+
SOUTHWEST -> SOUTHEAST
50+
SOUTHEAST -> NORTHEAST
51+
}
52+
53+
fun turnAround() = when(this) {
54+
NORTH -> SOUTH
55+
SOUTH -> NORTH
56+
EAST -> WEST
57+
WEST -> EAST
58+
NORTHEAST -> SOUTHWEST
59+
SOUTHWEST -> NORTHEAST
60+
SOUTHEAST -> NORTHWEST
61+
NORTHWEST -> SOUTHEAST
62+
}
3063
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2025 James Carman
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package adventofcode.util.geom.plane
18+
19+
data class Pose(val position: Point2D, val orientation: Direction) {
20+
fun turnRight() = copy(orientation = orientation.turnRight())
21+
fun turnLeft() = copy(orientation = orientation.turnLeft())
22+
fun turnAround() = copy(orientation = orientation.turnAround())
23+
fun forward() = copy(position = position + orientation)
24+
fun backward() = copy(position = position - orientation)
25+
}

src/main/kotlin/adventofcode/util/grid/ListGrid.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ class ListGrid<T : Any>(private val width: Int, values: List<T>) : AbstractGrid<
2323
private val height: Int
2424

2525
init {
26-
if (values.size % width != 0) {
27-
throw IllegalArgumentException("Values length is not a multiple of provided width $width.")
28-
}
26+
require(values.size % width == 0) { "Values length is not a multiple of provided width $width." }
2927
this.values = values.toMutableList()
3028
this.height = values.size / width
3129
}

0 commit comments

Comments
 (0)