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 });