// TODO scoring // TODO readable block definitions package kotlintris import kotlin.browser.* import kotlin.math.* import org.w3c.dom.* import org.w3c.dom.events.KeyboardEvent class Tetris { private val height = 25 private val width = 10 private var tetromino = 0 private var nextTetromino = 0 private var rotation = 0 private var column = 0 private var row = 0 private var score = 0 private var lines = 0 private var speed = 650 // ms private var timer = 0 private val blockDefinitions = "defgaeimdefgaeim" + "defjaeibdhijibfj" + "dhefaeijhifjabfj" + "eifjeifjeifjeifj" + "heifaefjheifaefj" + "deifaeifheijebfj" + "deijeibfdeijeibf" init { val main = drawBucket(width, height, "main") drawBucket(4, 4, "next") newTetronimo() timer = window.setInterval({ moveDown(main) }, speed) document.body?.addEventListener("keydown", { event -> keydown(event as KeyboardEvent) } ) } private fun keydown(event: KeyboardEvent) { val main = document.querySelector("table.bucket.main") as HTMLTableElement when (event.which) { 74, 37 -> moveTetromino(main, -1, 0, tetromino, rotation) // left 75, 38 -> moveTetromino(main, 0, 0, tetromino, (rotation + 1) % 4) // rotate 76, 39 -> moveTetromino(main, 1, 0, tetromino, rotation) // right 32 -> { // drop if(timer > 0) { // game not over? window.clearInterval(timer) timer = window.setInterval({ moveDown(main) }, 9) } } 73, 40, 77 -> moveDown(main) // move down } } private fun drawBucket(width: Int, height: Int, className: String): HTMLTableElement { val bucket = document.createElement("table") as HTMLTableElement bucket.classList.add("bucket") bucket.classList.add(className) for (y in 1..height) { val row = bucket.insertRow() for (x in 1..width) { row.insertCell() } } document.body?.appendChild(bucket) return bucket } private fun newTetronimo() { val next = document.querySelector("table.bucket.next") as HTMLTableElement val main = document.querySelector("table.bucket.main") as HTMLTableElement // clear next drawTetromino(next.rows, 0, 0, nextTetromino, 0, true) tetromino = nextTetromino rotation = 0 column = width / 2 - 2 row = 0 // draw next nextTetromino = (0 until blockDefinitions.length / 7 / 4).random() drawTetromino(next.rows, 0, 0, nextTetromino, 0) if (!moveTetromino(main, 0, 1, tetromino, rotation)) { window.clearInterval(timer) timer = 0 window.alert(":-(") } } private fun drawTetromino(rows: HTMLCollection, column: Int, row: Int, block: Int, rotation: Int, clear: Boolean = false) { var colors = arrayOf( "cyan", "blue", "orange", "yellow", "green", "purple", "red" ) val color = colors[block] for(i in 0..3) { val code = blockDefinitions[block * 16 + rotation * 4 + i].toInt() - 96 val x = code % 4 val y = code / 4 var cell = (rows[row + y] as HTMLTableRowElement).cells[column + x] cell?.className = "" if(!clear) { cell?.classList?.add(color) } } } private fun moveTetromino(bucket: HTMLTableElement, deltaX: Int, deltaY: Int, tetromino: Int, rotation: Int): Boolean { // clear tetromino drawTetromino(bucket.rows, this.column, this.row, this.tetromino, this.rotation, true) var free = testTetromino(bucket, this.column + deltaX, this.row + deltaY, tetromino, rotation) if(free) { this.column += deltaX this.row += deltaY this.rotation = rotation } // draw tetromino drawTetromino(bucket.rows, this.column, this.row, this.tetromino, this.rotation) return free } private fun testTetromino(bucket: HTMLTableElement, column: Int, row: Int, tetromino: Int, rotation: Int): Boolean { for(i in 0..3) { val code = blockDefinitions[tetromino * 16 + rotation * 4 + i].toInt() - 96 val x = code % 4 val y = code / 4 if (column + x < 0 || column + x >= width || row + y >= height) { return false } var cell = (bucket.rows[row + y] as HTMLTableRowElement).cells[column + x] if(cell?.className != "") { return false } } return true } private fun moveDown(bucket: HTMLTableElement) { // cannot place at bottom? -> clear lines and new tetromino if(!moveTetromino(bucket, 0, 1, tetromino, rotation)) { window.clearInterval(timer) clearLines(bucket) newTetronimo() val main = document.querySelector("table.bucket.main") as HTMLTableElement timer = window.setInterval({ moveDown(main) }, speed) } } private fun clearLines(bucket: HTMLTableElement) { for(y in this.row..height - 1) { var row = bucket.rows[y] as HTMLTableRowElement var freeCount = 0 for(x in 0..row.cells.length - 1) { var cell = row.cells[x] if(cell?.className == "") { freeCount++ } } if(freeCount == 0) { bucket.deleteRow(y) var row = bucket.insertRow(0) for (x in 1..width) { row.insertCell() } } } /* for (p = 0, y = Y; y < H; y++) { for (f = 0, x = W; x--;) f |= !S[y].cells[x].s if (!f) { B.deleteRow(y) a() p++ } } */ } }