From 8992f4d02b21c3d36b7b0ca4c952e3de1cb154de Mon Sep 17 00:00:00 2001 From: adrien Date: Fri, 25 Oct 2019 02:12:47 +0200 Subject: [PATCH] all shapes, autorepeat --- js/webtris.js | 257 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 198 insertions(+), 59 deletions(-) diff --git a/js/webtris.js b/js/webtris.js index 778ac37..ed9beef 100644 --- a/js/webtris.js +++ b/js/webtris.js @@ -1,23 +1,23 @@ -Array.prototype.add = function(movement) { - return this.map((x, i) => x + movement[i]) +Array.prototype.add = function(other) { + return this.map((x, i) => x + other[i]) } - Array.prototype.rotate = function(spin) { return [spin*pos[1], pos[0]] } +Array.prototype.sample = function() { + return this.splice(Math.floor(Math.random()*this.length), 1)[0] +} const MINO_SIZE = 20 -const LINES = 20 -const COLLUMNS = 10 -const INIT_POSITION = [4, -1] -const ACTION_KEY = { - MOVE_LEFT: "ArrowLeft", - MOVE_RIGHT: "ArrowRight", - SOFT_DROP: "ArrowDown", - ROTATE_CW: "ArrowUp", - ROTATE_CCW: "z" -} +const MATRIX_LINES = 20 +const MATRIX_COLLUMNS = 10 +const NEXT_PIECES = 5 +const INIT_POSITION = [4, 0] +const LOCK_DELAY = 500 +const FALL_DELAY = 1000 +const AUTOREPEAT_DELAY = 300 +const AUTOREPEAT_PERIOD = 10 const MOVEMENT = { LEFT: [-1, 0], RIGHT: [1, 0], @@ -27,8 +27,27 @@ const SPIN = { CW: -1, CCW: 1 } +const T_SPIN = { + NULL: "", + MINI: "MINI\nT-SPIN", + T_SPIN: "T-SPIN" +} +const T_SLOT = { + A: 0, + B: 1, + C: 3, + D: 2 +} +const SCORES = [ + {LINES_CLEAR_NAME: "", NO_T_SPIN: 0, MINI_T_SPIN: 1, T_SPIN: 4}, + {LINES_CLEAR_NAME: "SINGLE", NO_T_SPIN: 1, MINI_T_SPIN: 2, T_SPIN: 8}, + {LINES_CLEAR_NAME: "DOUBLE", NO_T_SPIN: 3, T_SPIN: 12}, + {LINES_CLEAR_NAME: "TRIPLE", NO_T_SPIN: 5, T_SPIN: 16}, + {LINES_CLEAR_NAME: "TETRIS", NO_T_SPIN: 8}, +] +shapes = [] class Tetromino { constructor() { this.pos = INIT_POSITION @@ -36,20 +55,71 @@ class Tetromino { this.rotated_last = false this.rotation_point_5_used = false this.hold_enabled = true - this.SRS = { - "-1": [ - [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]], - [[0, 0], [1, 0], [1, 1], [0, -2], [1, -2]], - [[0, 0], [1, 0], [1, -1], [0, 2], [1, 2]], - [[0, 0], [-1, 0], [-1, 1], [0, 2], [-1, -2]], + this.srs = { + CW: [ + [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]], + [[0, 0], [ 1, 0], [ 1, 1], [0, -2], [ 1, -2]], + [[0, 0], [ 1, 0], [ 1, -1], [0, 2], [ 1, 2]], + [[0, 0], [-1, 0], [-1, 1], [0, 2], [-1, -2]], ], - 1: [ - [[0, 0], [1, 0], [1, -1], [0, 2], [1, 2]], - [[0, 0], [1, 0], [1, 1], [0, -2], [1, -2]], - [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]], - [[0, 0], [-1, 0], [-1, 1], [0, -2], [-1, -2]], + CCW: [ + [[0, 0], [ 1, 0], [ 1, -1], [0, 2], [ 1, 2]], + [[0, 0], [ 1, 0], [ 1, 1], [0, -2], [ 1, -2]], + [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]], + [[0, 0], [-1, 0], [-1, 1], [0, -2], [-1, -2]], ], } + if (!shapes.lenght) + shapes = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'] + this.shape = shapes.sample() + switch(this.shape) { + case 'I': + this.color = "cyan" + this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [2, 0]] + this.srs = { + CW: [ + [[ 1, 0], [-1, 0], [ 2, 0], [-1, 1], [ 2, -2]], + [[ 0, 1], [-1, 1], [ 2, 1], [-1, -1], [ 2, 2]], + [[-1, 0], [ 1, 0], [-2, 0], [ 1, -1], [-2, 2]], + [[ 0, 1], [ 1, -1], [-2, -1], [ 1, 1], [-2, -2]], + ], + CCW: [ + [[ 0, 1], [-1, 1], [ 2, 1], [-1, -1], [ 2, 2]], + [[-1, 0], [ 1, 0], [-2, 0], [ 1, -1], [-2, 2]], + [[ 0, -1], [ 1, -1], [-2, -1], [ 1, 1], [-2, -2]], + [[ 1, 0], [-1, 0], [ 2, 0], [-1, 1], [ 2, -2]], + ], + } + break + case 'J': + this.color = "blue" + this.minoes_pos = [[-1, -1], [-1, 0], [0, 0], [1, 0]] + break + case 'L': + this.color = "orange" + this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [1, -1]] + break + case 'O': + this.color = "yellow" + this.minoes_pos = [[0, 0], [1, 0], [0, -1], [1, -1]] + this.srs = { + CW: [[]], + CCW: [[]] + } + break + case 'S': + this.color = "green" + this.minoes_pos = [[-1, -1], [0, -1], [0, 0], [1, 0]] + break + case 'T': + this.color = "magenta" + this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [0, -1]] + break + case 'Z': + this.color = "red" + this.minoes_pos = [[-1, -1], [0, -1], [0, 0], [1, 0]] + break + } } get abs_minoes_pos() { @@ -61,15 +131,6 @@ class Tetromino { } } - -class T_Tetrimino extends Tetromino { - constructor() { - super() - this.color = "magenta" - this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [0, 1]] - } -} - function draw_mino(context, x, y, color) { context.fillStyle = color @@ -80,11 +141,11 @@ function draw_mino(context, x, y, color) { class Matrix { constructor() { - this.cells = Array.from(Array(COLLUMNS), y => Array(LINES)) + this.cells = Array.from(Array(MATRIX_COLLUMNS), y => Array(MATRIX_LINES)) } cell_is_occupied(x, y) { - return 0 <= x && x < COLLUMNS && y < LINES ? this.cells[x][y] : true + return 0 <= x && x < MATRIX_COLLUMNS && y < MATRIX_LINES ? this.cells[x][y] : true } space_to_move(piece_pos, minoes_pos) { @@ -99,11 +160,11 @@ class Matrix { // grid context.strokeStyle = "rgba(128, 128, 128, 128)"; context.beginPath(); - for (var x = 0; x <= COLLUMNS*MINO_SIZE; x += MINO_SIZE) { + for (var x = 0; x <= MATRIX_COLLUMNS*MINO_SIZE; x += MINO_SIZE) { context.moveTo(x, 0); context.lineTo(x, matrixCanvas.height); } - for (var y = 0; y <= LINES*MINO_SIZE; y += MINO_SIZE) { + for (var y = 0; y <= MATRIX_LINES*MINO_SIZE; y += MINO_SIZE) { context.moveTo(0, y); context.lineTo(matrixCanvas.width, y); } @@ -116,18 +177,22 @@ function move(movement) { const test_pos = tetro.pos.add(movement) if (matrix.space_to_move(test_pos, tetro.minoes_pos)) { tetro.pos = test_pos + return true + } + else { + return false } } function rotate(spin) { const text_minoes_pos = tetro.minoes_pos.map(pos => [spin*pos[1], pos[0]]) rotation_point = 0 - for (const movement of tetro.SRS[spin][tetro.orientation]) { + for (const movement of tetro.srs[spin==SPIN.CW?"CW":"CCW"][tetro.orientation]) { const test_pos = [tetro.pos[0]+movement[0], tetro.pos[1]+movement[1]] if (matrix.space_to_move(test_pos, text_minoes_pos)) { tetro.pos = test_pos tetro.minoes_pos = text_minoes_pos - tetro.orientation = (tetro.orientation + spin + 4) % 4 + tetro.orientation = (tetro.orientation - spin + 4) % 4 break; } rotation_point++ @@ -138,46 +203,120 @@ function fall() { move(MOVEMENT.DOWN); } +function moveLeft() { + move(MOVEMENT.LEFT); +} + +function moveRight() { + move(MOVEMENT.RIGHT) +} + +function softDrop() { + move(MOVEMENT.DOWN) +} + +function hardDrop() { + while(move(MOVEMENT.DOWN)) { + + } +} + +function rotateCW() { + rotate(SPIN.CW) +} + +function rotateCCW() { + rotate(SPIN.CCW) +} + +actions = { + "ArrowLeft": moveLeft, + "ArrowRight": moveRight, + "ArrowDown": softDrop, + " ": hardDrop, + "ArrowUp": rotateCW, + "z": rotateCCW +} + +pressedKeys = new Set() +repeatableActions = [moveLeft, moveRight, softDrop] +actionsToRepeat = [] +autorepeatTimeoutID = null +autorepeatIntervalID = null + +function autorepeat() { + if (actionsToRepeat.length) { + actionsToRepeat[0]() + if (autorepeatTimeoutID) { + autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID) + autorepeatIntervalID = setInterval(autorepeat, AUTOREPEAT_PERIOD) + } + } + else { + if (autorepeatTimeoutID) + autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID) + if (autorepeatIntervalID) + autorepeatIntervalID = clearInterval(autorepeatIntervalID) + } +} + function keyDownHandler(e) { - switch(e.key){ - case ACTION_KEY.MOVE_LEFT: - move(MOVEMENT.LEFT); - break - case ACTION_KEY.MOVE_RIGHT: - move(MOVEMENT.RIGHT); - break - case ACTION_KEY.SOFT_DROP: - move(MOVEMENT.DOWN); - break - case ACTION_KEY.ROTATE_CW: - rotate(SPIN.CW); - break - case ACTION_KEY.ROTATE_CCW: - rotate(SPIN.CCW); - break + if (!pressedKeys.has(e.key)) { + pressedKeys.add(e.key) + if (e.key in actions) { + action = actions[e.key] + action() + if (repeatableActions.includes(action)) { + actionsToRepeat.unshift(action) + if (autorepeatTimeoutID) { + autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID) + } + if (autorepeatIntervalID) { + autorepeatIntervalID = clearInterval(autorepeatIntervalID) + } + if (actionsToRepeat == softDrop) + autorepeatIntervalID = setInterval(autorepeat, FALL_DELAY / 20) + else + autorepeatTimeoutID = setTimeout(autorepeat, AUTOREPEAT_DELAY) + } + } } } function keyUpHandler(e) { + pressedKeys.delete(e.key) + if (e.key in actions) { + action = actions[e.key] + if (actionsToRepeat.includes(action)) { + actionsToRepeat.splice(actionsToRepeat.indexOf(action), 1) + if (!actionsToRepeat.length) { + if (autorepeatTimeoutID) { + autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID) + } + if (autorepeatIntervalID) { + autorepeatIntervalID = clearInterval(autorepeatIntervalID) + } + } + } + } } function draw() { - matrixContext.clearRect(0, 0, COLLUMNS*MINO_SIZE, LINES*MINO_SIZE); + matrixContext.clearRect(0, 0, MATRIX_COLLUMNS*MINO_SIZE, MATRIX_LINES*MINO_SIZE); matrix.draw(matrixContext) tetro.draw(matrixContext) requestAnimationFrame(draw) } window.onload = function() { - matrixCanvas = document.getElementById("matrix"); matrixContext = matrixCanvas.getContext("2d"); - tetro = new T_Tetrimino() matrix = new Matrix() + tetro = new Tetromino() document.addEventListener("keydown", keyDownHandler, false); document.addEventListener("keyup", keyUpHandler, false); - setInterval(fall, 1000); + setInterval(fall, FALL_DELAY); requestAnimationFrame(draw) } \ No newline at end of file