{"id":178,"date":"2023-12-07T13:36:36","date_gmt":"2023-12-07T13:36:36","guid":{"rendered":"https:\/\/gillesviaene.be\/?page_id=178"},"modified":"2023-12-07T13:38:58","modified_gmt":"2023-12-07T13:38:58","slug":"tetris","status":"publish","type":"page","link":"https:\/\/gillesviaene.be\/?page_id=178","title":{"rendered":"Tetris"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"178\" class=\"elementor elementor-178\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-ef4b7da elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"ef4b7da\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-eb57c60\" data-id=\"eb57c60\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-8904b8a elementor-widget elementor-widget-html\" data-id=\"8904b8a\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!DOCTYPE html>\r\n<html>\r\n<head>\r\n  <title>Basic Tetris HTML Game<\/title>\r\n  <meta charset=\"UTF-8\">\r\n  <style>\r\n  html, body {\r\n    height: 100%;\r\n    margin: 0;\r\n  }\r\n\r\n  body {\r\n    background: black;\r\n    display: flex;\r\n    align-items: center;\r\n    justify-content: center;\r\n  }\r\n\r\n  canvas {\r\n    border: 1px solid white;\r\n  }\r\n  <\/style>\r\n<\/head>\r\n<body>\r\n<canvas width=\"320\" height=\"640\" id=\"game\"><\/canvas>\r\n<script>\r\n\/\/ https:\/\/tetris.fandom.com\/wiki\/Tetris_Guideline\r\n\r\n\/\/ get a random integer between the range of [min,max]\r\n\/\/ @see https:\/\/stackoverflow.com\/a\/1527820\/2124254\r\nfunction getRandomInt(min, max) {\r\n  min = Math.ceil(min);\r\n  max = Math.floor(max);\r\n\r\n  return Math.floor(Math.random() * (max - min + 1)) + min;\r\n}\r\n\r\n\/\/ generate a new tetromino sequence\r\n\/\/ @see https:\/\/tetris.fandom.com\/wiki\/Random_Generator\r\nfunction generateSequence() {\r\n  const sequence = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'];\r\n\r\n  while (sequence.length) {\r\n    const rand = getRandomInt(0, sequence.length - 1);\r\n    const name = sequence.splice(rand, 1)[0];\r\n    tetrominoSequence.push(name);\r\n  }\r\n}\r\n\r\n\/\/ get the next tetromino in the sequence\r\nfunction getNextTetromino() {\r\n  if (tetrominoSequence.length === 0) {\r\n    generateSequence();\r\n  }\r\n\r\n  const name = tetrominoSequence.pop();\r\n  const matrix = tetrominos[name];\r\n\r\n  \/\/ I and O start centered, all others start in left-middle\r\n  const col = playfield[0].length \/ 2 - Math.ceil(matrix[0].length \/ 2);\r\n\r\n  \/\/ I starts on row 21 (-1), all others start on row 22 (-2)\r\n  const row = name === 'I' ? -1 : -2;\r\n\r\n  return {\r\n    name: name,      \/\/ name of the piece (L, O, etc.)\r\n    matrix: matrix,  \/\/ the current rotation matrix\r\n    row: row,        \/\/ current row (starts offscreen)\r\n    col: col         \/\/ current col\r\n  };\r\n}\r\n\r\n\/\/ rotate an NxN matrix 90deg\r\n\/\/ @see https:\/\/codereview.stackexchange.com\/a\/186834\r\nfunction rotate(matrix) {\r\n  const N = matrix.length - 1;\r\n  const result = matrix.map((row, i) =>\r\n    row.map((val, j) => matrix[N - j][i])\r\n  );\r\n\r\n  return result;\r\n}\r\n\r\n\/\/ check to see if the new matrix\/row\/col is valid\r\nfunction isValidMove(matrix, cellRow, cellCol) {\r\n  for (let row = 0; row < matrix.length; row++) {\r\n    for (let col = 0; col < matrix[row].length; col++) {\r\n      if (matrix[row][col] && (\r\n          \/\/ outside the game bounds\r\n          cellCol + col < 0 ||\r\n          cellCol + col >= playfield[0].length ||\r\n          cellRow + row >= playfield.length ||\r\n          \/\/ collides with another piece\r\n          playfield[cellRow + row][cellCol + col])\r\n        ) {\r\n        return false;\r\n      }\r\n    }\r\n  }\r\n\r\n  return true;\r\n}\r\n\r\n\/\/ place the tetromino on the playfield\r\nfunction placeTetromino() {\r\n  for (let row = 0; row < tetromino.matrix.length; row++) {\r\n    for (let col = 0; col < tetromino.matrix[row].length; col++) {\r\n      if (tetromino.matrix[row][col]) {\r\n\r\n        \/\/ game over if piece has any part offscreen\r\n        if (tetromino.row + row < 0) {\r\n          return showGameOver();\r\n        }\r\n\r\n        playfield[tetromino.row + row][tetromino.col + col] = tetromino.name;\r\n      }\r\n    }\r\n  }\r\n\r\n  \/\/ check for line clears starting from the bottom and working our way up\r\n  for (let row = playfield.length - 1; row >= 0; ) {\r\n    if (playfield[row].every(cell => !!cell)) {\r\n\r\n      \/\/ drop every row above this one\r\n      for (let r = row; r >= 0; r--) {\r\n        for (let c = 0; c < playfield[r].length; c++) {\r\n          playfield[r][c] = playfield[r-1][c];\r\n        }\r\n      }\r\n    }\r\n    else {\r\n      row--;\r\n    }\r\n  }\r\n\r\n  tetromino = getNextTetromino();\r\n}\r\n\r\n\/\/ show the game over screen\r\nfunction showGameOver() {\r\n  cancelAnimationFrame(rAF);\r\n  gameOver = true;\r\n\r\n  context.fillStyle = 'black';\r\n  context.globalAlpha = 0.75;\r\n  context.fillRect(0, canvas.height \/ 2 - 30, canvas.width, 60);\r\n\r\n  context.globalAlpha = 1;\r\n  context.fillStyle = 'white';\r\n  context.font = '36px monospace';\r\n  context.textAlign = 'center';\r\n  context.textBaseline = 'middle';\r\n  context.fillText('GAME OVER!', canvas.width \/ 2, canvas.height \/ 2);\r\n}\r\n\r\nconst canvas = document.getElementById('game');\r\nconst context = canvas.getContext('2d');\r\nconst grid = 32;\r\nconst tetrominoSequence = [];\r\n\r\n\/\/ keep track of what is in every cell of the game using a 2d array\r\n\/\/ tetris playfield is 10x20, with a few rows offscreen\r\nconst playfield = [];\r\n\r\n\/\/ populate the empty state\r\nfor (let row = -2; row < 20; row++) {\r\n  playfield[row] = [];\r\n\r\n  for (let col = 0; col < 10; col++) {\r\n    playfield[row][col] = 0;\r\n  }\r\n}\r\n\r\n\/\/ how to draw each tetromino\r\n\/\/ @see https:\/\/tetris.fandom.com\/wiki\/SRS\r\nconst tetrominos = {\r\n  'I': [\r\n    [0,0,0,0],\r\n    [1,1,1,1],\r\n    [0,0,0,0],\r\n    [0,0,0,0]\r\n  ],\r\n  'J': [\r\n    [1,0,0],\r\n    [1,1,1],\r\n    [0,0,0],\r\n  ],\r\n  'L': [\r\n    [0,0,1],\r\n    [1,1,1],\r\n    [0,0,0],\r\n  ],\r\n  'O': [\r\n    [1,1],\r\n    [1,1],\r\n  ],\r\n  'S': [\r\n    [0,1,1],\r\n    [1,1,0],\r\n    [0,0,0],\r\n  ],\r\n  'Z': [\r\n    [1,1,0],\r\n    [0,1,1],\r\n    [0,0,0],\r\n  ],\r\n  'T': [\r\n    [0,1,0],\r\n    [1,1,1],\r\n    [0,0,0],\r\n  ]\r\n};\r\n\r\n\/\/ color of each tetromino\r\nconst colors = {\r\n  'I': 'cyan',\r\n  'O': 'yellow',\r\n  'T': 'purple',\r\n  'S': 'green',\r\n  'Z': 'red',\r\n  'J': 'blue',\r\n  'L': 'orange'\r\n};\r\n\r\nlet count = 0;\r\nlet tetromino = getNextTetromino();\r\nlet rAF = null;  \/\/ keep track of the animation frame so we can cancel it\r\nlet gameOver = false;\r\n\r\n\/\/ game loop\r\nfunction loop() {\r\n  rAF = requestAnimationFrame(loop);\r\n  context.clearRect(0,0,canvas.width,canvas.height);\r\n\r\n  \/\/ draw the playfield\r\n  for (let row = 0; row < 20; row++) {\r\n    for (let col = 0; col < 10; col++) {\r\n      if (playfield[row][col]) {\r\n        const name = playfield[row][col];\r\n        context.fillStyle = colors[name];\r\n\r\n        \/\/ drawing 1 px smaller than the grid creates a grid effect\r\n        context.fillRect(col * grid, row * grid, grid-1, grid-1);\r\n      }\r\n    }\r\n  }\r\n\r\n  \/\/ draw the active tetromino\r\n  if (tetromino) {\r\n\r\n    \/\/ tetromino falls every 35 frames\r\n    if (++count > 35) {\r\n      tetromino.row++;\r\n      count = 0;\r\n\r\n      \/\/ place piece if it runs into anything\r\n      if (!isValidMove(tetromino.matrix, tetromino.row, tetromino.col)) {\r\n        tetromino.row--;\r\n        placeTetromino();\r\n      }\r\n    }\r\n\r\n    context.fillStyle = colors[tetromino.name];\r\n\r\n    for (let row = 0; row < tetromino.matrix.length; row++) {\r\n      for (let col = 0; col < tetromino.matrix[row].length; col++) {\r\n        if (tetromino.matrix[row][col]) {\r\n\r\n          \/\/ drawing 1 px smaller than the grid creates a grid effect\r\n          context.fillRect((tetromino.col + col) * grid, (tetromino.row + row) * grid, grid-1, grid-1);\r\n        }\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\n\/\/ listen to keyboard events to move the active tetromino\r\ndocument.addEventListener('keydown', function(e) {\r\n  if (gameOver) return;\r\n\r\n  \/\/ left and right arrow keys (move)\r\n  if (e.which === 37 || e.which === 39) {\r\n    const col = e.which === 37\r\n      ? tetromino.col - 1\r\n      : tetromino.col + 1;\r\n\r\n    if (isValidMove(tetromino.matrix, tetromino.row, col)) {\r\n      tetromino.col = col;\r\n    }\r\n  }\r\n\r\n  \/\/ up arrow key (rotate)\r\n  if (e.which === 38) {\r\n    const matrix = rotate(tetromino.matrix);\r\n    if (isValidMove(matrix, tetromino.row, tetromino.col)) {\r\n      tetromino.matrix = matrix;\r\n    }\r\n  }\r\n\r\n  \/\/ down arrow key (drop)\r\n  if(e.which === 40) {\r\n    const row = tetromino.row + 1;\r\n\r\n    if (!isValidMove(tetromino.matrix, row, tetromino.col)) {\r\n      tetromino.row = row - 1;\r\n\r\n      placeTetromino();\r\n      return;\r\n    }\r\n\r\n    tetromino.row = row;\r\n  }\r\n});\r\n\r\n\/\/ start the game\r\nrAF = requestAnimationFrame(loop);\r\n<\/script>\r\n<\/body>\r\n<\/html>\r\n\r\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\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":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"","footnotes":""},"class_list":["post-178","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Tetris -<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/gillesviaene.be\/?page_id=178\" \/>\n<meta property=\"og:locale\" content=\"nl_NL\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tetris -\" \/>\n<meta property=\"og:description\" content=\"Basic Tetris HTML Game\" \/>\n<meta property=\"og:url\" content=\"https:\/\/gillesviaene.be\/?page_id=178\" \/>\n<meta property=\"article:modified_time\" content=\"2023-12-07T13:38:58+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verwachte leestijd\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minuut\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/gillesviaene.be\/?page_id=178\",\"url\":\"https:\/\/gillesviaene.be\/?page_id=178\",\"name\":\"Tetris -\",\"isPartOf\":{\"@id\":\"https:\/\/gillesviaene.be\/#website\"},\"datePublished\":\"2023-12-07T13:36:36+00:00\",\"dateModified\":\"2023-12-07T13:38:58+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/gillesviaene.be\/?page_id=178#breadcrumb\"},\"inLanguage\":\"nl-NL\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/gillesviaene.be\/?page_id=178\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/gillesviaene.be\/?page_id=178#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/gillesviaene.be\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tetris\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/gillesviaene.be\/#website\",\"url\":\"https:\/\/gillesviaene.be\/\",\"name\":\"\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/gillesviaene.be\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"nl-NL\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tetris -","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/gillesviaene.be\/?page_id=178","og_locale":"nl_NL","og_type":"article","og_title":"Tetris -","og_description":"Basic Tetris HTML Game","og_url":"https:\/\/gillesviaene.be\/?page_id=178","article_modified_time":"2023-12-07T13:38:58+00:00","twitter_card":"summary_large_image","twitter_misc":{"Verwachte leestijd":"1 minuut"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/gillesviaene.be\/?page_id=178","url":"https:\/\/gillesviaene.be\/?page_id=178","name":"Tetris -","isPartOf":{"@id":"https:\/\/gillesviaene.be\/#website"},"datePublished":"2023-12-07T13:36:36+00:00","dateModified":"2023-12-07T13:38:58+00:00","breadcrumb":{"@id":"https:\/\/gillesviaene.be\/?page_id=178#breadcrumb"},"inLanguage":"nl-NL","potentialAction":[{"@type":"ReadAction","target":["https:\/\/gillesviaene.be\/?page_id=178"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/gillesviaene.be\/?page_id=178#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/gillesviaene.be\/"},{"@type":"ListItem","position":2,"name":"Tetris"}]},{"@type":"WebSite","@id":"https:\/\/gillesviaene.be\/#website","url":"https:\/\/gillesviaene.be\/","name":"","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/gillesviaene.be\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"nl-NL"}]}},"_links":{"self":[{"href":"https:\/\/gillesviaene.be\/index.php?rest_route=\/wp\/v2\/pages\/178","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gillesviaene.be\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/gillesviaene.be\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/gillesviaene.be\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gillesviaene.be\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=178"}],"version-history":[{"count":4,"href":"https:\/\/gillesviaene.be\/index.php?rest_route=\/wp\/v2\/pages\/178\/revisions"}],"predecessor-version":[{"id":183,"href":"https:\/\/gillesviaene.be\/index.php?rest_route=\/wp\/v2\/pages\/178\/revisions\/183"}],"wp:attachment":[{"href":"https:\/\/gillesviaene.be\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=178"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}