Skip to content

Commit c799a67

Browse files
committed
Day 17 - puzzle 1.
1 parent 23b8581 commit c799a67

File tree

4 files changed

+167
-0
lines changed

4 files changed

+167
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.gradle
22
.idea
33
build
4+
/bin
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package io.dmitrijs.aoc2022
2+
3+
class Day17(input: String) {
4+
private val moves = input.trim()
5+
6+
fun puzzle1(): Int {
7+
val board = Board(7)
8+
var moveIndex = 0
9+
10+
repeat(2022) { step ->
11+
val figure = figures[step % figures.size].also { board.place(it) }
12+
13+
do {
14+
val leftOrRight = Direction.of(moves[moveIndex])
15+
moveIndex = ++moveIndex % moves.length
16+
board.move(figure, leftOrRight)
17+
} while (board.move(figure, Direction.DOWN))
18+
19+
board.freeze(figure)
20+
}
21+
22+
return board.figuresHeight
23+
}
24+
25+
private class Board(private val width: Int, private val topBlankLines: Int = 3) {
26+
private val field = ArrayDeque<String>()
27+
private val blankLine = SPACE.toString().repeat(width)
28+
private var position = start
29+
30+
val figuresHeight get() = field.size
31+
32+
fun place(figure: Figure) {
33+
position = start
34+
field.insertBlankLines(topBlankLines + figure.height)
35+
}
36+
37+
fun freeze(figure: Figure) {
38+
figure.placeAt(position)
39+
field.removeBlankLines()
40+
}
41+
42+
fun move(figure: Figure, direction: Direction): Boolean {
43+
val next = position + direction
44+
45+
return canMove(figure, next).also { success ->
46+
if (success) {
47+
position = next
48+
}
49+
}
50+
}
51+
52+
private fun canMove(figure: Figure, position: Point): Boolean {
53+
val (posX, posY) = position
54+
55+
if (posX < 0 || (posX + figure.width) > width || (posY + figure.height) > field.size) {
56+
return false
57+
}
58+
59+
return (0 until figure.height).all { y ->
60+
(0 until figure.width).all { x ->
61+
field[posY + y][posX + x] == SPACE || figure[y][x] == SPACE
62+
}
63+
}
64+
}
65+
66+
override fun toString() = field.joinToString("\n")
67+
68+
private fun Figure.placeAt(position: Point) = repeat(height) { y ->
69+
val line = field[position.y + y]
70+
val replace = this[y].mapIndexed { x, char ->
71+
// This is the trick - take char from original line if figure has space.
72+
char.takeUnless { it == SPACE } ?: line[position.x + x]
73+
}.joinToString("")
74+
75+
field[position.y + y] = line.substring(0, position.x) + replace + line.substring(position.x + width)
76+
}
77+
78+
private fun ArrayDeque<String>.insertBlankLines(count: Int) = repeat(count) {
79+
addFirst(blankLine)
80+
}
81+
82+
private fun ArrayDeque<String>.removeBlankLines() {
83+
do {
84+
val line = first()
85+
if (line == blankLine) {
86+
removeFirst()
87+
}
88+
} while (line == blankLine)
89+
}
90+
}
91+
92+
private data class Figure(private val lines: List<String>) {
93+
val width = lines.first().length
94+
val height = lines.size
95+
96+
operator fun get(row: Int) = lines[row]
97+
98+
companion object {
99+
fun of(vararg shape: String) = Figure(shape.toList())
100+
}
101+
}
102+
103+
private fun Direction.Companion.of(char: Char) = when (char) {
104+
'>' -> Direction.RIGHT
105+
'<' -> Direction.LEFT
106+
else -> error("Invalid direction supplied: $char.")
107+
}
108+
109+
companion object {
110+
private const val SPACE = '.'
111+
private val start = Point(2, 0)
112+
private val figures = listOf(
113+
Figure.of(
114+
"@@@@",
115+
),
116+
Figure.of(
117+
".@.",
118+
"@@@",
119+
".@.",
120+
),
121+
Figure.of(
122+
"..@",
123+
"..@",
124+
"@@@",
125+
),
126+
Figure.of(
127+
"@",
128+
"@",
129+
"@",
130+
"@",
131+
),
132+
Figure.of(
133+
"@@",
134+
"@@",
135+
),
136+
)
137+
}
138+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.dmitrijs.aoc2022
2+
3+
import io.dmitrijs.aoc2022.Resources.resourceAsText
4+
import org.junit.jupiter.api.DisplayName
5+
import org.junit.jupiter.api.Nested
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
9+
@DisplayName("Day 17")
10+
internal class Day17Test {
11+
private val exampleInput = resourceAsText("day17_example")
12+
private val problemInput = resourceAsText("day17")
13+
14+
@Nested
15+
@DisplayName("Puzzle 1")
16+
inner class Puzzle1 {
17+
@Test
18+
fun `solves example`() {
19+
assertEquals(3_068, Day17(exampleInput).puzzle1())
20+
}
21+
22+
@Test
23+
fun `solves problem`() {
24+
assertEquals(3_106, Day17(problemInput).puzzle1())
25+
}
26+
}
27+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>

0 commit comments

Comments
 (0)