url-tetris

tetris inside the browser address bar
git clone https://tongong.net/git/url-tetris.git
Log | Files | Refs | README

main.js (8147B)


      1 const tetrominoes = [
      2     [
      3         [0, 0, 0, 0],
      4         [0, 0, 0, 0],
      5         [1, 1, 1, 1],
      6         [0, 0, 0, 0],
      7     ],
      8     [
      9         [0, 1, 0, 0],
     10         [0, 1, 0, 0],
     11         [0, 1, 1, 0],
     12         [0, 0, 0, 0],
     13     ],
     14     [
     15         [0, 0, 0, 0],
     16         [0, 1, 1, 0],
     17         [0, 1, 0, 0],
     18         [0, 1, 0, 0],
     19     ],
     20     [
     21         [0, 0, 0, 0],
     22         [0, 1, 0, 0],
     23         [0, 1, 1, 0],
     24         [0, 0, 1, 0],
     25     ],
     26     [
     27         [0, 0, 0, 0],
     28         [0, 0, 1, 0],
     29         [0, 1, 1, 0],
     30         [0, 1, 0, 0],
     31     ],
     32     [
     33         [0, 0, 0, 0],
     34         [0, 1, 1, 0],
     35         [0, 1, 1, 0],
     36         [0, 0, 0, 0],
     37     ],
     38     [
     39         [0, 0, 0, 0],
     40         [0, 1, 0, 0],
     41         [1, 1, 1, 0],
     42         [0, 0, 0, 0],
     43     ],
     44 ];
     45 var field = [];
     46 var fallingTetrominoes = [];
     47 var score;
     48 var playing = false;
     49 var timer;
     50 var speed;
     51 
     52 function FallingTetromino(tetrominoIndex, posX, posY) {
     53     this.tetrominoIndex = tetrominoIndex;
     54     this.posX = posX;
     55     this.posY = posY;
     56     this.rotation = 0;
     57     this.render = function (onField) {
     58         return addTetromino(onField, this.tetrominoIndex, this.posX, this.posY, this.rotation).field;
     59     };
     60     this.rotate = function () {
     61         this.rotation++;
     62         this.rotation %= 4;
     63         if (!addTetromino(field, this.tetrominoIndex, this.posX, this.posY, this.rotation).success) {
     64             if (addTetromino(field, this.tetrominoIndex, this.posX, this.posY + 1, this.rotation).success) this.posY++;
     65             else if (addTetromino(field, this.tetrominoIndex, this.posX, this.posY - 1, this.rotation).success) this.posY--;
     66             else if (addTetromino(field, this.tetrominoIndex, this.posX, this.posY + 2, this.rotation).success) this.posY += 2;
     67             else if (addTetromino(field, this.tetrominoIndex, this.posX, this.posY - 2, this.rotation).success) this.posY -= 2;
     68             else {
     69                 this.rotation--;
     70                 if (this.rotation == -1) this.rotation = 3;
     71             }
     72         }
     73     };
     74     this.goUp = function () {
     75         this.posY--;
     76         if (!addTetromino(field, this.tetrominoIndex, this.posX, this.posY, this.rotation).success) {
     77             this.posY++;
     78         }
     79     };
     80     this.goDown = function () {
     81         this.posY++;
     82         if (!addTetromino(field, this.tetrominoIndex, this.posX, this.posY, this.rotation).success) {
     83             this.posY--;
     84         }
     85     };
     86     this.goLeft = function () {
     87         this.posX--;
     88         if (!addTetromino(field, this.tetrominoIndex, this.posX, this.posY, this.rotation).success) {
     89             this.posX++;
     90             field = addTetromino(field, this.tetrominoIndex, this.posX, this.posY, this.rotation).field;
     91             this.delete();
     92         }
     93     };
     94     this.delete = function () {
     95         fallingTetrominoes.forEach((e, index) => {
     96             if (e === this) fallingTetrominoes.splice(index, 1);
     97         });
     98         checkRows();
     99         if (playing) spawnTetromino();
    100     };
    101 }
    102 
    103 function setUrl(url) {
    104     history.replaceState({}, url, "#" + url);
    105 }
    106 
    107 // leftColoum and rightColoum are arrays of bit from the top to the bottom
    108 function getBraille(leftColumn, rightColumn) {
    109     // https://en.wikipedia.org/wiki/Braille_Patterns
    110     let brailleChar = String.fromCharCode(
    111         0x2800 +
    112             leftColumn[0] +
    113             leftColumn[1] * 2 +
    114             leftColumn[2] * 4 +
    115             rightColumn[0] * 8 +
    116             rightColumn[1] * 16 +
    117             rightColumn[2] * 32 +
    118             leftColumn[3] * 64 +
    119             rightColumn[3] * 128
    120     );
    121     if (brailleChar == "⠀") brailleChar = "_";  // Remove empty braille characters
    122     return brailleChar;
    123 }
    124 
    125 function checkRows() {
    126     // Check for full rows
    127     for (let index = field.length - 1; index >= 0; index--) {
    128         row = field[index];
    129         if (row.reduce((a, b) => a + b) == 4) {
    130             score++;
    131             speed *= 0.97;
    132             field.splice(index, 1);
    133             field.push([0, 0, 0, 0]);
    134             document.title = "Score: " + score;
    135         }
    136     }
    137 
    138     // Check for game over
    139     console.log(field[20].reduce((a, b) => a + b) > 0);
    140     if (field[20].reduce((a, b) => a + b) > 0) {
    141         playing = false;
    142         if (score > (localStorage.getItem("highscore") || -1)) localStorage.setItem("highscore", score);
    143         setUrl("GameOver!........Score:" + score + "........HighScore:" + localStorage.getItem("highscore"));
    144         document.title = "url-tetris";
    145         window.clearTimeout(timer);
    146     }
    147 }
    148 
    149 function renderField(f) {
    150     if (f.length % 2 == 1) f.push([0, 0, 0, 0]);
    151     let urlsting = "";
    152     for (let index = 0; index < f.length; index += 2) {
    153         urlsting += getBraille(f[index], f[index + 1]);
    154     }
    155     setUrl(urlsting);
    156 }
    157 
    158 function renderTetrominoes() {
    159     if (playing) {
    160         let newField = field;
    161         fallingTetrominoes.forEach((e) => {
    162             newField = e.render(newField);
    163         });
    164         renderField(newField);
    165     }
    166 }
    167 
    168 function addTetromino(field, tetrominoIndex, posX, posY, rotation) {
    169     // rotation -> from 0 to 4 clockwise
    170     let newField = [];
    171     let success = true;
    172 
    173     field.forEach((e1) => {
    174         let line = [];
    175         e1.forEach((e2) => {
    176             line.push(e2);
    177         });
    178         newField.push(line);
    179     });
    180     let newTetromino = [
    181         [0, 0, 0, 0],
    182         [0, 0, 0, 0],
    183         [0, 0, 0, 0],
    184         [0, 0, 0, 0],
    185     ];
    186     tetrominoes[tetrominoIndex].forEach((e1, x) => {
    187         e1.forEach((e2, y) => {
    188             switch (rotation) {
    189                 case 0:
    190                     newTetromino[x][y] = e2;
    191                     break;
    192                 case 1:
    193                     newTetromino[y][3 - x] = e2;
    194                     break;
    195                 case 2:
    196                     newTetromino[3 - x][3 - y] = e2;
    197                     break;
    198                 case 3:
    199                     newTetromino[3 - y][x] = e2;
    200                     break;
    201             }
    202         });
    203     });
    204 
    205     let collision = false;
    206 
    207     newTetromino.forEach((e1, x) => {
    208         e1.forEach((e2, y) => {
    209             if (e2 == 1) {
    210                 if (y + posX < 0 || x + posY < 0 || x + posY > 3 || newField[y + posX][x + posY] == 1) {
    211                     success = false;
    212                     if (y + posX < 0 || newField[y + posX][x + posY] == 1) {
    213                         collision = true;
    214                     }
    215                 } else {
    216                     newField[y + posX][x + posY] = 1;
    217                 }
    218             }
    219         });
    220     });
    221 
    222     return { success, collision, field: newField };
    223 }
    224 
    225 function spawnTetromino() {
    226     fallingTetrominoes.push(new FallingTetromino(Math.floor(Math.random() * 7), 22, 0));
    227 }
    228 
    229 function upPressed() {
    230     console.log("up");
    231     fallingTetrominoes.forEach((e) => e.goUp());
    232     renderTetrominoes();
    233 }
    234 
    235 function downPressed() {
    236     console.log("down");
    237     fallingTetrominoes.forEach((e) => e.goDown());
    238     renderTetrominoes();
    239 }
    240 
    241 function leftPressed() {
    242     console.log("left");
    243     fallingTetrominoes.forEach((e) => e.goLeft());
    244     renderTetrominoes();
    245 }
    246 
    247 function rightPressed() {
    248     console.log("right");
    249     fallingTetrominoes.forEach((e) => e.rotate());
    250     renderTetrominoes();
    251 }
    252 
    253 function startGame() {
    254     speed = 600;
    255     field = [];
    256     fallingTetrominoes = [];
    257     score = 0;
    258     playing = true;
    259     window.clearTimeout(timer);
    260     for (let index = 0; index < 30; index++) {
    261         field.push([0, 0, 0, 0]);
    262     }
    263     spawnTetromino();
    264 
    265     frame();
    266 }
    267 
    268 function frame() {
    269     if (playing) {
    270         fallingTetrominoes.forEach((e) => e.goLeft());
    271         renderTetrominoes();
    272         timer = window.setTimeout(frame, speed);
    273     }
    274 }
    275 
    276 document.addEventListener("keydown", (e) => {
    277     if (e.code === "ArrowUp" && playing) upPressed();
    278     else if (e.code === "ArrowDown" && playing) downPressed();
    279     else if (e.code === "ArrowLeft" && playing) leftPressed();
    280     else if (e.code === "ArrowRight" && playing) rightPressed();
    281     else if (e.code === "KeyW" && playing) upPressed();
    282     else if (e.code === "KeyS" && playing) downPressed();
    283     else if (e.code === "KeyA" && playing) leftPressed();
    284     else if (e.code === "KeyD" && playing) rightPressed();
    285     else if (e.code === "Space") startGame();
    286 });