Skip to content

Commit ef86e37

Browse files
committed
2018-17, part 1 (Reservoir Research)
The tests are green and the rendered result looks good but the resulting number is too high.
1 parent 6621f05 commit ef86e37

File tree

2 files changed

+212
-7
lines changed

2 files changed

+212
-7
lines changed

src/main/kotlin/de/ronny_h/aoc/year2018/day17/ReservoirResearch.kt

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ fun main() = ReservoirResearch().run(0, 0)
1212

1313
class ReservoirResearch : AdventOfCode<Int>(2018, 17) {
1414
override fun part1(input: List<String>): Int {
15-
return 0
15+
val slice = VerticalSliceOfGround(input)
16+
slice.waterFlow(slice.springCoordinates)
17+
println(slice)
18+
return slice.countTilesOfWater()
1619
}
1720

1821
override fun part2(input: List<String>): Int {
@@ -24,11 +27,15 @@ class VerticalSliceOfGround(input: List<String>) {
2427
private val clay = '#'
2528
private val sand = '.'
2629
private val waterSpring = '+'
30+
private val waterFalling = '|'
31+
private val waterResting = '~'
32+
33+
val springCoordinates = Coordinates(500, 0)
2734

28-
private val slice = MapBackedCharGrid(Coordinates(500, 0), sand)
35+
private val slice = MapBackedCharGrid(springCoordinates, sand)
2936

3037
init {
31-
slice[Coordinates(500, 0)] = waterSpring
38+
slice[springCoordinates] = waterSpring
3239
input.forEach {
3340
val (single, range) = it.split(", ")
3441
if (single.startsWith('x')) {
@@ -39,6 +46,74 @@ class VerticalSliceOfGround(input: List<String>) {
3946
}
4047
}
4148

49+
fun waterFlow(spring: Coordinates) {
50+
// downwards
51+
var current = spring.down()
52+
while (slice[current] == sand) {
53+
if (current.y > slice.maxY) {
54+
return
55+
}
56+
slice[current] = waterFalling
57+
current = current.down()
58+
}
59+
if (slice[current] == waterFalling) {
60+
return
61+
}
62+
63+
// sidewards & upwards
64+
var isOverflowing = false
65+
while (!isOverflowing) {
66+
current = current.up()
67+
var leftmost = current.left()
68+
var rightmost = current.right()
69+
while (true) {
70+
if (slice[leftmost] in listOf(sand, waterFalling)) {
71+
if (slice[leftmost.down()] == sand) {
72+
isOverflowing = true
73+
waterFlow(leftmost)
74+
break
75+
} else if (slice[leftmost.down()] == waterFalling) {
76+
// already been there
77+
isOverflowing = true
78+
break
79+
} else if (slice[leftmost.down()] in listOf(clay, waterResting)) {
80+
leftmost = leftmost.left()
81+
}
82+
} else if (slice[leftmost] == clay) {
83+
leftmost = leftmost.right()
84+
break
85+
}
86+
}
87+
if (slice[rightmost] == waterResting) {
88+
// a different recursive call has already filled up
89+
return
90+
}
91+
while (true) {
92+
if (slice[rightmost] in listOf(sand, waterFalling)) {
93+
if (slice[rightmost.down()] == sand) {
94+
isOverflowing = true
95+
waterFlow(rightmost)
96+
break
97+
} else if (slice[rightmost.down()] == waterFalling) {
98+
// already been there
99+
isOverflowing = true
100+
break
101+
} else if (slice[rightmost.down()] in listOf(clay, waterResting)) {
102+
rightmost = rightmost.right()
103+
}
104+
} else if (slice[rightmost] == clay) {
105+
rightmost = rightmost.left()
106+
break
107+
}
108+
}
109+
if (slice[current] != waterResting) {
110+
slice[leftmost.x..rightmost.x, current.y] = if (isOverflowing) waterFalling else waterResting
111+
}
112+
}
113+
}
114+
115+
fun countTilesOfWater(): Int = slice.count { it in listOf(waterFalling, waterResting) }
116+
42117
override fun toString(): String = slice.toString()
43118

44119
private fun String.intAfter(delimiter: String): Int = substringAfter(delimiter).toInt()
@@ -49,15 +124,22 @@ class VerticalSliceOfGround(input: List<String>) {
49124
}
50125
}
51126

127+
private fun Coordinates.up() = Coordinates(x, y - 1)
128+
private fun Coordinates.down() = Coordinates(x, y + 1)
129+
private fun Coordinates.left() = Coordinates(x - 1, y)
130+
private fun Coordinates.right() = Coordinates(x + 1, y)
131+
52132
class MapBackedCharGrid(center: Coordinates, default: Char) {
53133
private var minX = center.x
54134
private var maxX = center.x
55135
private var minY = center.y
56-
private var maxY = center.y
136+
var maxY = center.y
137+
private set
57138

58139
private val slice = mutableMapOf<Coordinates, Char>().withDefault { default }
59140

60141
operator fun set(position: Coordinates, value: Char) {
142+
check(value == '#' || slice[position] != '#')
61143
minX = min(minX, position.x)
62144
minY = min(minY, position.y)
63145
maxX = max(maxX, position.x)
@@ -93,4 +175,6 @@ class MapBackedCharGrid(center: Coordinates, default: Char) {
93175
}
94176
return out.toString(UTF_8).trim()
95177
}
178+
179+
fun count(predicate: (Char) -> Boolean): Int = slice.values.count(predicate)
96180
}

src/test/kotlin/de/ronny_h/aoc/year2018/day17/ReservoirResearchTest.kt

Lines changed: 124 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,130 @@ class ReservoirResearchTest : StringSpec({
3737
VerticalSliceOfGround(input).toString() shouldBe expectedSlice
3838
}
3939

40-
"part 1" {
41-
val input = listOf("")
42-
ReservoirResearch().part1(input) shouldBe 0
40+
"let the water flow according to the puzzle's example" {
41+
val expectedSlice = """
42+
......+.......
43+
......|.....#.
44+
.#..#||||...#.
45+
.#..#~~#|.....
46+
.#..#~~#|.....
47+
.#~~~~~#|.....
48+
.#~~~~~#|.....
49+
.#######|.....
50+
........|.....
51+
...|||||||||..
52+
...|#~~~~~#|..
53+
...|#~~~~~#|..
54+
...|#~~~~~#|..
55+
...|#######|..
56+
""".trimIndent()
57+
58+
val slice = VerticalSliceOfGround(input)
59+
slice.waterFlow(slice.springCoordinates)
60+
slice.toString() shouldBe expectedSlice
61+
}
62+
63+
"overflow within a bin" {
64+
val input = """
65+
x=498, y=2..6
66+
x=505, y=2..6
67+
x=502, y=3..4
68+
y=6, x=498..505
69+
""".asList()
70+
val expectedInit = """
71+
...+......
72+
..........
73+
.#......#.
74+
.#...#..#.
75+
.#...#..#.
76+
.#......#.
77+
.########.
78+
""".trimIndent()
79+
val expectedFlooded = """
80+
....+.......
81+
.||||||||||.
82+
.|#~~~~~~#|.
83+
.|#~~~#~~#|.
84+
.|#~~~#~~#|.
85+
.|#~~~~~~#|.
86+
.|########|.
87+
""".trimIndent()
88+
89+
val slice = VerticalSliceOfGround(input)
90+
slice.toString() shouldBe expectedInit
91+
slice.waterFlow(slice.springCoordinates)
92+
slice.toString() shouldBe expectedFlooded
93+
}
94+
95+
"plateau within a bin" {
96+
val input = """
97+
x=496, y=2..6
98+
x=503, y=2..6
99+
x=499, y=3..4
100+
x=500, y=3..4
101+
y=6, x=496..503
102+
""".asList()
103+
val expectedInit = """
104+
.....+....
105+
..........
106+
.#......#.
107+
.#..##..#.
108+
.#..##..#.
109+
.#......#.
110+
.########.
111+
""".trimIndent()
112+
val expectedFlooded = """
113+
......+.....
114+
.||||||||||.
115+
.|#~~~~~~#|.
116+
.|#~~##~~#|.
117+
.|#~~##~~#|.
118+
.|#~~~~~~#|.
119+
.|########|.
120+
""".trimIndent()
121+
122+
val slice = VerticalSliceOfGround(input)
123+
slice.toString() shouldBe expectedInit
124+
slice.waterFlow(slice.springCoordinates)
125+
slice.toString() shouldBe expectedFlooded
126+
}
127+
128+
"two springs into one bin" {
129+
val input = """
130+
x=500, y=2..2
131+
x=499, y=5..7
132+
x=503, y=3..7
133+
y=7, x=499..503
134+
""".asList()
135+
val expectedInit = """
136+
..+....
137+
.......
138+
..#....
139+
.....#.
140+
.....#.
141+
.#...#.
142+
.#...#.
143+
.#####.
144+
""".trimIndent()
145+
val expectedFlooded = """
146+
...+....
147+
..|||...
148+
..|#|...
149+
..|.|.#.
150+
.|||||#.
151+
.|#~~~#.
152+
.|#~~~#.
153+
.|#####.
154+
""".trimIndent()
155+
156+
val slice = VerticalSliceOfGround(input)
157+
slice.toString() shouldBe expectedInit
158+
slice.waterFlow(slice.springCoordinates)
159+
slice.toString() shouldBe expectedFlooded
160+
}
161+
162+
"part 1: the number of tiles the water can reach" {
163+
ReservoirResearch().part1(input) shouldBe 57
43164
}
44165

45166
"part 2" {

0 commit comments

Comments
 (0)