{"id":1267,"date":"2024-06-28T20:51:50","date_gmt":"2024-06-28T18:51:50","guid":{"rendered":"https:\/\/www.gmvisuals.sk\/?page_id=1267"},"modified":"2024-06-28T23:05:45","modified_gmt":"2024-06-28T21:05:45","slug":"tetris","status":"publish","type":"page","link":"https:\/\/www.gmvisuals.sk\/index.php\/tetris\/","title":{"rendered":"Tetris"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1267\" class=\"elementor elementor-1267\">\n\t\t\t\t<div class=\"elementor-element elementor-element-111a9d1 e-flex e-con-boxed e-con e-parent\" data-id=\"111a9d1\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-dcdfe43 e-con-full e-flex e-con e-child\" data-id=\"dcdfe43\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-19b21ce elementor-widget elementor-widget-html\" data-id=\"19b21ce\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html>\n<head>\n  <title>Basic Tetris HTML Game<\/title>\n  <meta charset=\"UTF-8\">\n  <style>\n  html, body {\n    height: 100%;\n    margin: 0;\n  }\n\n  body {\n    background: white;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n  }\n\n  canvas {\n    border: 1px solid black;\n  }\n  <\/style>\n<\/head>\n<body>\n<canvas width=\"320\" height=\"640\" id=\"game\"><\/canvas>\n<script>\n\/\/ https:\/\/tetris.fandom.com\/wiki\/Tetris_Guideline\n\n\/\/ get a random integer between the range of [min,max]\n\/\/ @see https:\/\/stackoverflow.com\/a\/1527820\/2124254\nfunction getRandomInt(min, max) {\n  min = Math.ceil(min);\n  max = Math.floor(max);\n\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\n\/\/ generate a new tetromino sequence\n\/\/ @see https:\/\/tetris.fandom.com\/wiki\/Random_Generator\nfunction generateSequence() {\n  const sequence = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'];\n\n  while (sequence.length) {\n    const rand = getRandomInt(0, sequence.length - 1);\n    const name = sequence.splice(rand, 1)[0];\n    tetrominoSequence.push(name);\n  }\n}\n\n\/\/ get the next tetromino in the sequence\nfunction getNextTetromino() {\n  if (tetrominoSequence.length === 0) {\n    generateSequence();\n  }\n\n  const name = tetrominoSequence.pop();\n  const matrix = tetrominos[name];\n\n  \/\/ I and O start centered, all others start in left-middle\n  const col = playfield[0].length \/ 2 - Math.ceil(matrix[0].length \/ 2);\n\n  \/\/ I starts on row 21 (-1), all others start on row 22 (-2)\n  const row = name === 'I' ? -1 : -2;\n\n  return {\n    name: name,      \/\/ name of the piece (L, O, etc.)\n    matrix: matrix,  \/\/ the current rotation matrix\n    row: row,        \/\/ current row (starts offscreen)\n    col: col         \/\/ current col\n  };\n}\n\n\/\/ rotate an NxN matrix 90deg\n\/\/ @see https:\/\/codereview.stackexchange.com\/a\/186834\nfunction rotate(matrix) {\n  const N = matrix.length - 1;\n  const result = matrix.map((row, i) =>\n    row.map((val, j) => matrix[N - j][i])\n  );\n\n  return result;\n}\n\n\/\/ check to see if the new matrix\/row\/col is valid\nfunction isValidMove(matrix, cellRow, cellCol) {\n  for (let row = 0; row < matrix.length; row++) {\n    for (let col = 0; col < matrix[row].length; col++) {\n      if (matrix[row][col] && (\n          \/\/ outside the game bounds\n          cellCol + col < 0 ||\n          cellCol + col >= playfield[0].length ||\n          cellRow + row >= playfield.length ||\n          \/\/ collides with another piece\n          playfield[cellRow + row][cellCol + col])\n        ) {\n        return false;\n      }\n    }\n  }\n\n  return true;\n}\n\n\/\/ place the tetromino on the playfield\nfunction placeTetromino() {\n  for (let row = 0; row < tetromino.matrix.length; row++) {\n    for (let col = 0; col < tetromino.matrix[row].length; col++) {\n      if (tetromino.matrix[row][col]) {\n\n        \/\/ game over if piece has any part offscreen\n        if (tetromino.row + row < 0) {\n          return showGameOver();\n        }\n\n        playfield[tetromino.row + row][tetromino.col + col] = tetromino.name;\n      }\n    }\n  }\n\n  \/\/ check for line clears starting from the bottom and working our way up\n  for (let row = playfield.length - 1; row >= 0; ) {\n    if (playfield[row].every(cell => !!cell)) {\n\n      \/\/ drop every row above this one\n      for (let r = row; r >= 0; r--) {\n        for (let c = 0; c < playfield[r].length; c++) {\n          playfield[r][c] = playfield[r-1][c];\n        }\n      }\n    }\n    else {\n      row--;\n    }\n  }\n\n  tetromino = getNextTetromino();\n}\n\n\/\/ show the game over screen\nfunction showGameOver() {\n  cancelAnimationFrame(rAF);\n  gameOver = true;\n\n  context.fillStyle = 'black';\n  context.globalAlpha = 0.75;\n  context.fillRect(0, canvas.height \/ 2 - 30, canvas.width, 60);\n\n  context.globalAlpha = 1;\n  context.fillStyle = 'white';\n  context.font = '36px monospace';\n  context.textAlign = 'center';\n  context.textBaseline = 'middle';\n  context.fillText('GAME OVER!', canvas.width \/ 2, canvas.height \/ 2);\n}\n\nconst canvas = document.getElementById('game');\nconst context = canvas.getContext('2d');\nconst grid = 32;\nconst tetrominoSequence = [];\n\n\/\/ keep track of what is in every cell of the game using a 2d array\n\/\/ tetris playfield is 10x20, with a few rows offscreen\nconst playfield = [];\n\n\/\/ populate the empty state\nfor (let row = -2; row < 20; row++) {\n  playfield[row] = [];\n\n  for (let col = 0; col < 10; col++) {\n    playfield[row][col] = 0;\n  }\n}\n\n\/\/ how to draw each tetromino\n\/\/ @see https:\/\/tetris.fandom.com\/wiki\/SRS\nconst tetrominos = {\n  'I': [\n    [0,0,0,0],\n    [1,1,1,1],\n    [0,0,0,0],\n    [0,0,0,0]\n  ],\n  'J': [\n    [1,0,0],\n    [1,1,1],\n    [0,0,0],\n  ],\n  'L': [\n    [0,0,1],\n    [1,1,1],\n    [0,0,0],\n  ],\n  'O': [\n    [1,1],\n    [1,1],\n  ],\n  'S': [\n    [0,1,1],\n    [1,1,0],\n    [0,0,0],\n  ],\n  'Z': [\n    [1,1,0],\n    [0,1,1],\n    [0,0,0],\n  ],\n  'T': [\n    [0,1,0],\n    [1,1,1],\n    [0,0,0],\n  ]\n};\n\n\/\/ color of each tetromino\nconst colors = {\n  'I': 'cyan',\n  'O': 'yellow',\n  'T': 'purple',\n  'S': 'green',\n  'Z': 'red',\n  'J': 'blue',\n  'L': 'orange'\n};\n\nlet count = 0;\nlet tetromino = getNextTetromino();\nlet rAF = null;  \/\/ keep track of the animation frame so we can cancel it\nlet gameOver = false;\n\n\/\/ game loop\nfunction loop() {\n  rAF = requestAnimationFrame(loop);\n  context.clearRect(0,0,canvas.width,canvas.height);\n\n  \/\/ draw the playfield\n  for (let row = 0; row < 20; row++) {\n    for (let col = 0; col < 10; col++) {\n      if (playfield[row][col]) {\n        const name = playfield[row][col];\n        context.fillStyle = colors[name];\n\n        \/\/ drawing 1 px smaller than the grid creates a grid effect\n        context.fillRect(col * grid, row * grid, grid-1, grid-1);\n      }\n    }\n  }\n\n  \/\/ draw the active tetromino\n  if (tetromino) {\n\n    \/\/ tetromino falls every 35 frames\n    if (++count > 35) {\n      tetromino.row++;\n      count = 0;\n\n      \/\/ place piece if it runs into anything\n      if (!isValidMove(tetromino.matrix, tetromino.row, tetromino.col)) {\n        tetromino.row--;\n        placeTetromino();\n      }\n    }\n\n    context.fillStyle = colors[tetromino.name];\n\n    for (let row = 0; row < tetromino.matrix.length; row++) {\n      for (let col = 0; col < tetromino.matrix[row].length; col++) {\n        if (tetromino.matrix[row][col]) {\n\n          \/\/ drawing 1 px smaller than the grid creates a grid effect\n          context.fillRect((tetromino.col + col) * grid, (tetromino.row + row) * grid, grid-1, grid-1);\n        }\n      }\n    }\n  }\n}\n\n\/\/ listen to keyboard events to move the active tetromino\ndocument.addEventListener('keydown', function(e) {\n  if (gameOver) return;\n\n  \/\/ left and right arrow keys (move)\n  if (e.which === 37 || e.which === 39) {\n    const col = e.which === 37\n      ? tetromino.col - 1\n      : tetromino.col + 1;\n\n    if (isValidMove(tetromino.matrix, tetromino.row, col)) {\n      tetromino.col = col;\n    }\n  }\n\n  \/\/ up arrow key (rotate)\n  if (e.which === 38) {\n    const matrix = rotate(tetromino.matrix);\n    if (isValidMove(matrix, tetromino.row, tetromino.col)) {\n      tetromino.matrix = matrix;\n    }\n  }\n\n  \/\/ down arrow key (drop)\n  if(e.which === 40) {\n    const row = tetromino.row + 1;\n\n    if (!isValidMove(tetromino.matrix, row, tetromino.col)) {\n      tetromino.row = row - 1;\n\n      placeTetromino();\n      return;\n    }\n\n    tetromino.row = row;\n  }\n});\n\n\/\/ start the game\nrAF = requestAnimationFrame(loop);\n<\/script>\n<\/body>\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Basic Tetris HTML Game<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-1267","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.gmvisuals.sk\/index.php\/wp-json\/wp\/v2\/pages\/1267","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gmvisuals.sk\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.gmvisuals.sk\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.gmvisuals.sk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gmvisuals.sk\/index.php\/wp-json\/wp\/v2\/comments?post=1267"}],"version-history":[{"count":17,"href":"https:\/\/www.gmvisuals.sk\/index.php\/wp-json\/wp\/v2\/pages\/1267\/revisions"}],"predecessor-version":[{"id":1299,"href":"https:\/\/www.gmvisuals.sk\/index.php\/wp-json\/wp\/v2\/pages\/1267\/revisions\/1299"}],"wp:attachment":[{"href":"https:\/\/www.gmvisuals.sk\/index.php\/wp-json\/wp\/v2\/media?parent=1267"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}