@@ -8,18 +8,119 @@ class Day10(private var filename: String) {
88 private val ICON_NORTH_WEST_BEND = ' J'
99 private val ICON_SOUTH_WEST_BEND = ' 7'
1010 private val ICON_SOUTH_EAST_BEND = ' F'
11- private val ICON_FREE_GROUND = ' .'
1211
1312 fun solvePart1 (): Long =
14- solvePart1 (
13+ (computeMazeLoop (
1514 getResourceAsText(filename)
1615 .map { row -> row.toCharArray().toList() }
16+ ).size / 2 )
17+ .toLong()
18+
19+ fun solvePart2 (): Long {
20+ val maze = getResourceAsText(filename)
21+ .map { row -> row.toCharArray().toList() }
22+
23+ return computeEnclosedTiles(
24+ computeMazeLoop(maze),
25+ maze
1726 )
27+ }
28+
29+ @SuppressWarnings(" kotlin:S3776" )
30+ private fun computeEnclosedTiles (loop : List <Pair <Int , Int >>, maze : List <List <Char >>): Long {
31+ val (startIndex, newStart) = loop
32+ .withIndex()
33+ .minWith(compareBy<IndexedValue <Pair <Int , Int >>> { it.value.first }.thenBy { it.value.second })
34+
35+ val counterClockwise = loop[startIndex + 1 ].first > newStart.first
36+ || loop[startIndex + 1 ].second < newStart.second
37+
38+ val enclosedTiles = loop
39+ .indices
40+ .asSequence()
41+ .flatMap {
42+ val index = (it + startIndex) % loop.size
43+ val currentTile = loop[index]
44+ val prevTile = loop[(index + loop.size - 1 ) % loop.size]
45+ val adjacentTileDirections = setOf (
46+ currentTile.getDirectionTo(prevTile),
47+ currentTile.getDirectionTo(loop[(index + loop.size + 1 ) % loop.size])
48+ )
49+
50+ when (adjacentTileDirections) {
51+ setOf (
52+ Direction .NORTH ,
53+ Direction .EAST
54+ ) -> when {
55+ ! counterClockwise && prevTile.first < currentTile.first || counterClockwise && prevTile.second > currentTile.second -> {
56+ left(loop, currentTile, maze).asSequence()
57+ }
58+ else -> {
59+ emptySequence()
60+ }
61+ }
62+
63+ setOf (
64+ Direction .SOUTH ,
65+ Direction .WEST
66+ ) -> when {
67+ ! counterClockwise && prevTile.first > currentTile.first || counterClockwise && prevTile.second < currentTile.second -> {
68+ right(loop, currentTile, maze).asSequence()
69+ }
70+ else -> {
71+ emptySequence()
72+ }
73+ }
74+
75+ setOf (
76+ Direction .SOUTH ,
77+ Direction .EAST
78+ ) -> when {
79+ ! counterClockwise && prevTile.second > currentTile.second || counterClockwise && prevTile.first > currentTile.first -> {
80+ left(loop, currentTile, maze).asSequence()
81+ }
82+ else -> {
83+ emptySequence()
84+ }
85+ }
1886
19- fun solvePart2 (): Long =
20- 0L
87+ setOf (
88+ Direction .NORTH ,
89+ Direction .WEST
90+ ) -> when {
91+ ! counterClockwise && prevTile.second < currentTile.second || counterClockwise && prevTile.first < currentTile.first -> {
92+ right(loop, currentTile, maze).asSequence()
93+ }
94+ else -> {
95+ emptySequence()
96+ }
97+ }
2198
22- private fun solvePart1 (maze : List <List <Char >>): Long {
99+ setOf (Direction .NORTH , Direction .SOUTH ) -> {
100+ when {
101+ ! counterClockwise == prevTile.first < currentTile.first -> {
102+ left(loop, currentTile, maze).asSequence()
103+ }
104+ ! counterClockwise == prevTile.first > currentTile.first -> {
105+ right(loop, currentTile, maze).asSequence()
106+ }
107+ else -> {
108+ emptySequence()
109+ }
110+ }
111+ }
112+
113+ else -> emptySequence()
114+ }
115+ }
116+ .toSet()
117+
118+ return enclosedTiles
119+ .size
120+ .toLong()
121+ }
122+
123+ private fun computeMazeLoop (maze : List <List <Char >>): List <Pair <Int , Int >> {
23124 var start: Pair <Int , Int >? = null
24125
25126 out @ for (row in maze.indices) {
@@ -31,9 +132,7 @@ class Day10(private var filename: String) {
31132 }
32133 }
33134
34- val loop = getLoop(start!! , maze)
35-
36- return (loop.size / 2 ).toLong()
135+ return getLoop(start!! , maze)
37136 }
38137
39138 private fun getLoop (start : Pair <Int , Int >, maze : List <List <Char >>): List <Pair <Int , Int >> {
@@ -106,6 +205,67 @@ class Day10(private var filename: String) {
106205 }
107206 }
108207
208+ private fun left (
209+ loop : List <Pair <Int , Int >>,
210+ currentTile : Pair <Int , Int >,
211+ maze : List <List <Char >>
212+ ): List <Pair <Int , Int >> {
213+ val rowTiles = loop
214+ .filter { it.first == currentTile.first && it.second < currentTile.second }
215+
216+ when {
217+ rowTiles.isEmpty() -> {
218+ return emptyList()
219+ }
220+ else -> {
221+ val nextTile = rowTiles
222+ .maxWith(compareBy { it.second })
223+
224+ return maze[currentTile.first]
225+ .withIndex()
226+ .filter { nextTile.second < it.index && it.index < currentTile.second }
227+ .map { Pair (currentTile.first, it.index) }
228+ }
229+ }
230+ }
231+
232+ private fun right (
233+ loop : List <Pair <Int , Int >>,
234+ currentTile : Pair <Int , Int >,
235+ maze : List <List <Char >>
236+ ): List <Pair <Int , Int >> {
237+ val rowTiles = loop
238+ .filter { it.first == currentTile.first && it.second > currentTile.second }
239+
240+ when {
241+ rowTiles.isEmpty() -> {
242+ return emptyList()
243+ }
244+ else -> {
245+ val nextTile = rowTiles
246+ .minWith(compareBy { it.second })
247+
248+ return maze[currentTile.first]
249+ .withIndex()
250+ .filter { currentTile.second < it.index && it.index < nextTile.second }
251+ .map { Pair (currentTile.first, it.index) }
252+ }
253+ }
254+ }
255+
256+ private fun Pair <Int , Int >.getDirectionTo (location : Pair <Int , Int >): Direction {
257+ when {
258+ this .first != location.first && this .second != location.second -> throw IllegalArgumentException ()
259+ else -> return when {
260+ this .first == location.first && this .second < location.second -> return Direction .EAST
261+ this .first == location.first && this .second > location.second -> return Direction .WEST
262+ this .second == location.second && this .first < location.first -> return Direction .SOUTH
263+ this .second == location.second && this .first > location.first -> return Direction .NORTH
264+ else -> Direction .NONE
265+ }
266+ }
267+ }
268+
109269 private fun getResourceAsText (path : String ): List <String > =
110270 this .javaClass.classLoader.getResourceAsStream(path)!! .bufferedReader().readLines()
111271}
0 commit comments