11package de.ronny_h.aoc.extensions.grids
22
3+ import de.ronny_h.aoc.extensions.graphs.ShortestPath
4+ import de.ronny_h.aoc.extensions.graphs.aStarAllPaths
35import java.io.ByteArrayOutputStream
46import java.io.PrintStream
57import java.nio.charset.StandardCharsets
68
79/* *
10+ * A 2-dimensional, rectangular Grid where each cell is identified by its [Coordinates] and represented by a value of type [T].
11+ *
12+ * @param height The number of rows in the Grid.
13+ * @param width The number of columns in the Grid.
814 * @param nullElement The initial element within the grid
9- * @param overrideElement Used for out of bound positions. Defaults to ` nullElement`
15+ * @param fallbackElement Used for out of bound positions. Defaults to [ nullElement].
1016 */
1117abstract class Grid <T >(
1218 val height : Int ,
1319 val width : Int ,
1420 protected val nullElement : T ,
15- private val overrideElement : T = nullElement,
21+ private val fallbackElement : T = nullElement,
1622) {
1723
1824 private val grid: MutableList <MutableList <T >> = MutableList (height) { MutableList (width) { nullElement } }
1925
26+ /* *
27+ * A function that maps each `Char` that may occur in the `input: List<String>` to a value of type [T].
28+ */
2029 abstract fun Char.toElementType (): T
2130
31+ /* *
32+ * Constructs a Grid with the specified dimensions filled with the [nullElement] as default value and all
33+ * coordinates in [overrides] set to [overrideElement].
34+ */
2235 constructor (
2336 height: Int ,
2437 width: Int ,
@@ -31,6 +44,12 @@ abstract class Grid<T>(
3144 }
3245 }
3346
47+ /* *
48+ * Constructs a Grid from the [input] list. Uses function [toElementType] for converting the [String]s' [Char]s
49+ * to values of type [T].
50+ *
51+ * @param input A list of `String`s of the same length that determines the Grid's width. Its size determines the Grid's height.
52+ */
3453 constructor (input: List <String >, nullElement: T , overrideElement: T = nullElement) : this (
3554 input.size,
3655 input[0 ].length,
@@ -41,15 +60,18 @@ abstract class Grid<T>(
4160 initGrid(input)
4261 }
4362
44- fun initGrid (input : List <String >) = input.forEachIndexed { row, line ->
63+ protected fun initGrid (input : List <String >) = input.forEachIndexed { row, line ->
4564 line.forEachIndexed { col, char -> grid[row][col] = char.toElementType() }
4665 }
4766
67+ /* *
68+ * @return The value at the specified cell.
69+ */
4870 operator fun get (row : Int , col : Int ): T {
4971 return grid
5072 .getOrNull(row)
5173 ?.getOrNull(col)
52- ? : overrideElement
74+ ? : fallbackElement
5375 }
5476
5577 fun getAt (position : Coordinates ) = get(position.row, position.col)
@@ -82,7 +104,7 @@ abstract class Grid<T>(
82104 }
83105
84106 /* *
85- * Returns the first element matching the given ` value` .
107+ * Returns the first element matching the given [ value] .
86108 *
87109 * @throws NoSuchElementException If no matching element can be found.
88110 */
@@ -92,6 +114,10 @@ abstract class Grid<T>(
92114
93115 override fun toString (): String = toString(setOf ())
94116
117+ /* *
118+ * @return A String representation of this [Grid] with all coordinates in [overrides] overridden
119+ * by the Char specified as [overrideChar].
120+ */
95121 fun toString (
96122 overrides : Set <Coordinates > = setOf(),
97123 overrideChar : Char = '#',
@@ -103,6 +129,12 @@ abstract class Grid<T>(
103129 return out .toString().trim()
104130 }
105131
132+ /* *
133+ * Prints this [Grid] to [System.out] with (the first matching rule applies):
134+ * - if not `null`, the [highlightPosition] overridden by the `Char` representation of [highlightDirection]
135+ * - each coordinate in [path] overridden by the `Char` that is mapped to it
136+ * - all coordinates in [overrides] overridden by the `Char` specified as [overrideChar]
137+ */
106138 fun printGrid (
107139 overrides : Set <Coordinates > = setOf(),
108140 overrideChar : Char = '#',
@@ -113,7 +145,7 @@ abstract class Grid<T>(
113145 printGrid(System .out , overrides, overrideChar, highlightPosition, highlightDirection, path)
114146 }
115147
116- fun printGrid (
148+ private fun printGrid (
117149 writer : PrintStream ,
118150 overrides : Set <Coordinates > = setOf(),
119151 overrideChar : Char = '#',
@@ -134,4 +166,26 @@ abstract class Grid<T>(
134166 if (position.col == width - 1 ) writer.println ()
135167 }.last()
136168 }
169+
170+ /* *
171+ * Determines all shortest paths possible from [start] to [goal] with the following conditions:
172+ * - The Grid's [nullElement] is the obstacle.
173+ * - No path outside the Grid is possible.
174+ * - Only direct neighbours (no diagonal ones) are considered.
175+ * - The cost of moving to a neighbour equals 1.
176+ */
177+ fun shortestPaths (start : Coordinates , goal : Coordinates ): List <ShortestPath <Coordinates >> {
178+ val neighbours: (Coordinates ) -> List <Coordinates > = { position ->
179+ position.neighbours().filter { getAt(it) != nullElement }
180+ }
181+
182+ val d: (Coordinates , Coordinates ) -> Int = { a, b ->
183+ require(a taxiDistanceTo b == 1 ) { " a and b have to be neighbours" }
184+ 1
185+ }
186+
187+ val h: (Coordinates ) -> Int = { it taxiDistanceTo goal }
188+
189+ return aStarAllPaths(start, { this == goal }, neighbours, d, h)
190+ }
137191}
0 commit comments