Skip to content

Commit cc82fe0

Browse files
committed
Solution 2015-18 (Like a GIF For Your Yard)
1 parent 13ebaf1 commit cc82fe0

File tree

5 files changed

+136
-10
lines changed

5 files changed

+136
-10
lines changed

src/main/kotlin/de/ronny_h/aoc/extensions/grids/Coordinates.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
package de.ronny_h.aoc.extensions.grids
22

3+
import de.ronny_h.aoc.extensions.grids.Direction.*
34
import kotlin.math.abs
45

56
data class Coordinates(val row: Int, val col: Int) {
67

78
companion object {
89
val ZERO = Coordinates(0, 0)
910
}
11+
1012
operator fun plus(other: Coordinates) = Coordinates(row + other.row, col + other.col)
1113
operator fun minus(other: Coordinates) = Coordinates(row - other.row, col - other.col)
1214

1315
operator fun times(other: Int) = Coordinates(row * other, col * other)
1416

1517
operator fun plus(direction: Direction) = Coordinates(row + direction.row, col + direction.col)
1618

17-
fun neighbours() = listOf(this + Direction.EAST, this + Direction.SOUTH, this + Direction.WEST, this + Direction.NORTH)
19+
fun neighbours() = listOf(this + EAST, this + SOUTH, this + WEST, this + NORTH)
20+
21+
fun neighboursIncludingDiagonals() = neighbours() +
22+
listOf(this + EAST + NORTH, this + SOUTH + EAST, this + WEST + SOUTH, this + NORTH + WEST)
23+
1824
fun directedNeighbours() = listOf(
19-
Direction.EAST to this + Direction.EAST,
20-
Direction.SOUTH to this + Direction.SOUTH,
21-
Direction.WEST to this + Direction.WEST,
22-
Direction.NORTH to this + Direction.NORTH,
25+
EAST to this + EAST,
26+
SOUTH to this + SOUTH,
27+
WEST to this + WEST,
28+
NORTH to this + NORTH,
2329
)
2430

2531
/**

src/main/kotlin/de/ronny_h/aoc/extensions/grids/Grid.kt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,30 @@ abstract class Grid<T>(
1919

2020
abstract fun Char.toElementType(): T
2121

22-
constructor(height: Int, width: Int, nullElement: T, overrideElement: T = nullElement, overrides: List<Coordinates> = emptyList()) : this(height, width, nullElement, overrideElement) {
22+
constructor(
23+
height: Int,
24+
width: Int,
25+
nullElement: T,
26+
overrideElement: T = nullElement,
27+
overrides: List<Coordinates> = emptyList()
28+
) : this(height, width, nullElement, overrideElement) {
2329
overrides.forEach {
2430
grid[it.row][it.col] = overrideElement
2531
}
2632
}
2733

28-
constructor(input: List<String>, nullElement: T, overrideElement: T = nullElement) : this(input.size, input[0].length, nullElement, overrideElement, emptyList()){
29-
input.forEachIndexed { row, line ->
30-
line.forEachIndexed { col, char -> grid[row][col] = char.toElementType() }
31-
}
34+
constructor(input: List<String>, nullElement: T, overrideElement: T = nullElement) : this(
35+
input.size,
36+
input[0].length,
37+
nullElement,
38+
overrideElement,
39+
emptyList()
40+
) {
41+
initGrid(input)
42+
}
43+
44+
fun initGrid(input: List<String>) = input.forEachIndexed { row, line ->
45+
line.forEachIndexed { col, char -> grid[row][col] = char.toElementType() }
3246
}
3347

3448
operator fun get(row: Int, col: Int): T {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package de.ronny_h.aoc.year2015.day18
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
import de.ronny_h.aoc.extensions.grids.Coordinates
5+
import de.ronny_h.aoc.extensions.grids.Grid
6+
7+
fun main() = LikeAGIFForYourYard().run(768, 0)
8+
9+
class LikeAGIFForYourYard : AdventOfCode<Int>(2015, 18) {
10+
11+
override fun part1(input: List<String>): Int = GameOfLightGrid(input).animateSteps(100).countLightsOn()
12+
13+
override fun part2(input: List<String>): Int =
14+
GameOfLightGrid(input).animateStepsWithCornersStuckOn(100).countLightsOn()
15+
16+
companion object {
17+
fun GameOfLightGrid.animateSteps(steps: Int): GameOfLightGrid {
18+
var grid = this
19+
repeat(steps) { grid = grid.animateStep() }
20+
return grid
21+
}
22+
23+
fun GameOfLightGrid.animateStepsWithCornersStuckOn(steps: Int): GameOfLightGrid {
24+
var grid = this.withCornersStuckOn()
25+
repeat(steps) { grid = grid.animateStep().withCornersStuckOn() }
26+
return grid
27+
}
28+
29+
private fun GameOfLightGrid.animateStep(): GameOfLightGrid {
30+
val nextGeneration = GameOfLightGrid(this.height, this.width)
31+
forEachCoordinates { position, light ->
32+
val neighboursOn = position.neighboursIncludingDiagonals().count { getAt(it) == on }
33+
val nextState = if (neighboursOn == 3 || light == on && neighboursOn == 2) on else off
34+
nextGeneration.setAt(position, nextState)
35+
}.last()
36+
return nextGeneration
37+
}
38+
39+
private fun GameOfLightGrid.withCornersStuckOn(): GameOfLightGrid {
40+
setAt(Coordinates(0, 0), on)
41+
setAt(Coordinates(0, width - 1), on)
42+
setAt(Coordinates(height - 1, 0), on)
43+
setAt(Coordinates(height - 1, width - 1), on)
44+
return this
45+
}
46+
}
47+
}
48+
49+
private const val on = '#'
50+
private const val off = '.'
51+
52+
class GameOfLightGrid(height: Int, width: Int) : Grid<Char>(height, width, nullElement = off) {
53+
54+
constructor(input: List<String>) : this(input.size, input[0].length) {
55+
initGrid(input)
56+
}
57+
58+
override fun Char.toElementType() = this
59+
60+
fun countLightsOn(): Int = forEachElement { _, _, light ->
61+
if (light == on) 1 else 0
62+
}.sum()
63+
}

src/test/kotlin/de/ronny_h/aoc/extensions/grids/CoordinatesTest.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,19 @@ class CoordinatesTest : StringSpec({
6464
)
6565
}
6666

67+
"Neighbours including diagonals" {
68+
Coordinates(5, 5).neighboursIncludingDiagonals() shouldContainAll listOf(
69+
Coordinates(4, 5),
70+
Coordinates(6, 5),
71+
Coordinates(5, 4),
72+
Coordinates(5, 6),
73+
Coordinates(4, 6),
74+
Coordinates(6, 6),
75+
Coordinates(6, 4),
76+
Coordinates(4, 4),
77+
)
78+
}
79+
6780
"Directed neighbours" {
6881
Coordinates(5, 5).directedNeighbours() shouldContainAll listOf(
6982
Direction.NORTH to Coordinates(4, 5),
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package de.ronny_h.aoc.year2015.day18
2+
3+
import de.ronny_h.aoc.year2015.day18.LikeAGIFForYourYard.Companion.animateSteps
4+
import de.ronny_h.aoc.year2015.day18.LikeAGIFForYourYard.Companion.animateStepsWithCornersStuckOn
5+
import io.kotest.core.spec.style.StringSpec
6+
import io.kotest.matchers.shouldBe
7+
8+
class LikeAGIFForYourYardTest : StringSpec({
9+
10+
val input = listOf(
11+
".#.#.#",
12+
"...##.",
13+
"#....#",
14+
"..#...",
15+
"#.#..#",
16+
"####..",
17+
)
18+
19+
"part 1: the number of lights on after one and four iterations" {
20+
val grid = GameOfLightGrid(input)
21+
grid.animateSteps(1).countLightsOn() shouldBe 11
22+
grid.animateSteps(4).countLightsOn() shouldBe 4
23+
}
24+
25+
"part 2: the number of lights on after one and five iterations with corners stuck on" {
26+
val grid = GameOfLightGrid(input)
27+
grid.animateStepsWithCornersStuckOn(1).countLightsOn() shouldBe 18
28+
grid.animateStepsWithCornersStuckOn(5).countLightsOn() shouldBe 17
29+
}
30+
})

0 commit comments

Comments
 (0)