kotlintris
// 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++
}
}
*/
}
}
Download
// 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++
}
}
*/
}
}