|
| 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 | +} |
0 commit comments