@@ -18,76 +18,66 @@ package adventofcode.day06
1818
1919import adventofcode.util.geom.plane.Direction
2020import adventofcode.util.geom.plane.Point2D
21+ import adventofcode.util.geom.plane.Pose
2122import 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-
3424private const val OBSTACLE = ' #'
3525private 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
7971fun 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
0 commit comments