VCHS Computer Science
Computer Science at Valley Catholic High School
Pages
Home
APCSA
APCSP
Ten Commandments
Jump
Tetris Part 4
Source Code
<!-- CSS --> <style> #grid { background-color: black; position: relative; border-right: solid 1px black; border-bottom: solid 1px black; } .block { position: absolute; border: solid 1px silver; } </style> <!-- HTML --> <div id="grid" style="height: 400px; width: 250px;"><div class="block" style="height: 25px; width: 25px; background-color: royalblue; left: 100px; top: 0px;"></div><div class="block" style="height: 25px; width: 25px; background-color: royalblue; left: 125px; top: 0px;"></div><div class="block" style="height: 25px; width: 25px; background-color: royalblue; left: 125px; top: 25px;"></div><div class="block" style="height: 25px; width: 25px; background-color: royalblue; left: 150px; top: 25px;"></div></div> <!-- JavaScript --> <script> (function IIFE() { // Immediately Invoked Function Expression "use strict"; // Opt into strict mode. (Opt out of sloppy mode.) /*** GLOBAL VARIABLE ***/ let currentShape; let timerId; let gameIsOver = false; /*** GLOBAL CONSTANTS ***/ const frozenBlocks = []; // Unit Constants const unitConst = Object.freeze({ units: "px", qtyPer: 25 }); // Block Constants const blockConst = { unitsWide: unitConst.qtyPer, unitsTall: unitConst.qtyPer, length: unitConst.qtyPer + unitConst.units }; blockConst.width = blockConst.length; blockConst.height = blockConst.length; Object.freeze(blockConst); // Grid Constants const gridConst = { blocksWide: 10, blocksTall: 16 }; gridConst.width = (gridConst.blocksWide * blockConst.unitsWide) + unitConst.units; gridConst.height = (gridConst.blocksTall * blockConst.unitsTall) + unitConst.units; Object.freeze(gridConst); // Element Constants const elemConst = Object.freeze({ grid: getSizedGrid() }); // Shape Constants const shapeConst = Object.freeze({ LColor: "orange", JColor: "red", SColor: "royalblue", ZColor: "gold", TColor: "green", IColor: "hotpink", OColor: "purple" }); /*** FUNCTIONS ***/ function isBlockedAt(pos) { const n = frozenBlocks.length; if (pos.left < 0) return true; if (pos.left > gridConst.blocksWide - 1) return true; if (pos.top < 0) return true; for (let i = 0; i < n; ++i) { const block = frozenBlocks[i]; if (block.getLeft() === pos.left && block.getTop() === pos.top) return true; } return false; } /** Gets, sets the size of, and returns the grid element. */ function getSizedGrid() { const grid = document.getElementById("grid"); grid.style.height = gridConst.height; grid.style.width = gridConst.width; return grid; } /** Constructs a single block object. */ function Block(options) { // Initialize the new block. const thisObject = this; const element = createBlockElement(); let left = options.left; let top = options.top; updatePosition(); /* Private Functions */ function updateLeft() { element.style.left = (left * blockConst.unitsWide) + unitConst.units; } function updateTop() { element.style.top = (top * blockConst.unitsTall) + unitConst.units; } function updatePosition() { updateLeft(); updateTop(); } function createBlockElement() { const block = document.createElement("div"); block.classList.add("block"); block.style.height = blockConst.height; block.style.width = blockConst.width; block.style.backgroundColor = options.bgColor; return block; } function canMoveLeft() { return left > 0 && !isBlockedAt({left: left - 1, top: top}); } function canMoveRight() { return left < gridConst.blocksWide - 1 && !isBlockedAt({left: left + 1, top: top}); } function canMoveDown() { return top < gridConst.blocksTall - 1 && !isBlockedAt({left: left, top: top + 1}); } function moveLeft() { --left; updateLeft(); } function moveRight() { ++left; updateLeft(); } function moveDown() { ++top; updateTop(); } /* Public Methods */ this.addTo = function(otherElement) { otherElement.append(element); return thisObject; }; this.moveLeft = function() { if (canMoveLeft()) moveLeft(); return thisObject; }; this.moveRight = function() { if (canMoveRight()) moveRight(); return thisObject; }; this.moveDown = function() { if (canMoveDown()) moveDown(); return thisObject; }; this.canMoveRight = canMoveRight; this.canMoveLeft = canMoveLeft; this.canMoveDown = canMoveDown; this.getLeft = function() { return left; } this.getTop = function() { return top; } this.getPosition = function() { return {left: left, top: top}; } this.setLeft = function(value) { left = value; updateLeft(); } this.setTop = function(value) { top = value; updateTop(); } } function blockComparator(b1, b2) { const topDiff = b1.getTop() - b2.getTop(); return (topDiff === 0) ? b1.getLeft() - b2.getLeft() : topDiff; } function onKeyDown(event) { const key = event.key; if (gameIsOver) { event.preventDefault(); } else if (key === "ArrowLeft") { currentShape.moveLeft(); event.preventDefault(); } else if (key === "ArrowRight") { currentShape.moveRight(); event.preventDefault(); } else if (key === "ArrowDown") { currentShape.moveDown(); event.preventDefault(); } else if (key === "ArrowUp") { currentShape.pivot(); event.preventDefault(); } } /** Base class for a shape object consisting of blocks. * The value of pIndex is the index of the pivot block * around which the other blocks may be rotated. */ function Shape(blocks, pIndex) { const thisObject = this; const numBlocks = blocks.length; const pivotBlock = (pIndex === undefined) ? undefined : blocks[pIndex]; blocks.forEach(function(block) { block.addTo(elemConst.grid); }); function canMoveRight() { if (gameIsOver) return false; for (let i = 0; i < numBlocks; ++i) { if (!blocks[i].canMoveRight()) return false; } return true; } function canMoveLeft() { if (gameIsOver) return false; for (let i = 0; i < numBlocks; ++i) { if (!blocks[i].canMoveLeft()) return false; } return true; } function canMoveDown() { if (gameIsOver) return false; for (let i = 0; i < numBlocks; ++i) { if (!blocks[i].canMoveDown()) return false; } return true; } function canPivot() { if (gameIsOver) return false; const n = blocks.length; for (let i = 0; i < n; ++i) { const block = blocks[i]; const pos = getPivotedPosition(block); if (isBlockedAt(pos)) return false; } return true; } function overlapsOtherShape() { const n = blocks.length; for (let i = 0; i < n; ++i) { const block = blocks[i]; const pos = block.getPosition(); if (isBlockedAt(pos)) return true; } return false; } function moveDown() { for (let i = 0; i < numBlocks; ++i) blocks[i].moveDown(); } function moveLeft() { for (let i = 0; i < numBlocks; ++i) blocks[i].moveLeft(); } function moveRight() { for (let i = 0; i < numBlocks; ++i) blocks[i].moveRight(); } function freeze() { blocks.forEach(function(block) { frozenBlocks.push(block); frozenBlocks.sort(blockComparator); }); currentShape = getNextShape(); } function getPivotedPosition(block) { const B = [block.getTop(), block.getLeft()]; const P = [pivotBlock.getTop(), pivotBlock.getLeft()]; const Vr = [B[0] - P[0], B[1] - P[1]]; const Vt = [-1 * Vr[1], Vr[0]]; return {top: Vt[0] + P[0], left: Vt[1] + P[1]}; } function pivot(block) { // Credit: https://www.youtube.com/watch?v=Atlr5vvdchY if (block === pivotBlock || pivotBlock === undefined) return; const pos = getPivotedPosition(block); block.setTop(pos.top); block.setLeft(pos.left); } this.moveDown = function() { if (canMoveDown()) moveDown(); else freeze(); return thisObject; }; this.moveLeft = function() { if (canMoveLeft()) moveLeft(); return thisObject; }; this.moveRight = function() { if (canMoveRight()) moveRight(); return thisObject; }; this.overlapsOtherShape = overlapsOtherShape; this.canMoveRight = canMoveRight; this.canMoveLeft = canMoveLeft; this.canMoveDown = canMoveDown; this.freeze = freeze; this.pivot = function() { if (canPivot()) blocks.forEach(pivot); return thisObject; } } /* Shape Subclasses */ function LShape() { Shape.call(this, getBlocks(), 1); function getBlocks() { const color = shapeConst.LColor; const blocks = []; blocks.push(new Block({bgColor: color, left: 3, top: 1})); blocks.push(new Block({bgColor: color, left: 4, top: 1})); blocks.push(new Block({bgColor: color, left: 5, top: 1})); blocks.push(new Block({bgColor: color, left: 5, top: 0})); return blocks; } } function JShape() { Shape.call(this, getBlocks(), 1); function getBlocks() { const color = shapeConst.JColor; const blocks = []; blocks.push(new Block({bgColor: color, left: 3, top: 0})); blocks.push(new Block({bgColor: color, left: 4, top: 0})); blocks.push(new Block({bgColor: color, left: 5, top: 0})); blocks.push(new Block({bgColor: color, left: 5, top: 1})); return blocks; } } function SShape() { Shape.call(this, getBlocks(), 2); function getBlocks() { const color = shapeConst.SColor; const blocks = []; blocks.push(new Block({bgColor: color, left: 4, top: 0})); blocks.push(new Block({bgColor: color, left: 5, top: 0})); blocks.push(new Block({bgColor: color, left: 5, top: 1})); blocks.push(new Block({bgColor: color, left: 6, top: 1})); return blocks; } } function ZShape() { Shape.call(this, getBlocks(), 2); function getBlocks() { const color = shapeConst.ZColor; const blocks = []; blocks.push(new Block({bgColor: color, left: 4, top: 1})); blocks.push(new Block({bgColor: color, left: 5, top: 1})); blocks.push(new Block({bgColor: color, left: 5, top: 0})); blocks.push(new Block({bgColor: color, left: 6, top: 0})); return blocks; } } function TShape() { Shape.call(this, getBlocks(), 1); function getBlocks() { const color = shapeConst.TColor; const blocks = []; blocks.push(new Block({bgColor: color, left: 3, top: 1})); blocks.push(new Block({bgColor: color, left: 4, top: 1})); blocks.push(new Block({bgColor: color, left: 5, top: 1})); blocks.push(new Block({bgColor: color, left: 4, top: 0})); return blocks; } } function OShape() { Shape.call(this, getBlocks(), undefined); function getBlocks() { const color = shapeConst.OColor; const blocks = []; blocks.push(new Block({bgColor: color, left: 4, top: 0})); blocks.push(new Block({bgColor: color, left: 5, top: 0})); blocks.push(new Block({bgColor: color, left: 4, top: 1})); blocks.push(new Block({bgColor: color, left: 5, top: 1})); return blocks; } } function IShape() { Shape.call(this, getBlocks(), 2); function getBlocks() { const color = shapeConst.IColor; const blocks = []; blocks.push(new Block({bgColor: color, left: 3, top: 0})); blocks.push(new Block({bgColor: color, left: 4, top: 0})); blocks.push(new Block({bgColor: color, left: 5, top: 0})); blocks.push(new Block({bgColor: color, left: 6, top: 0})); return blocks; } } function getNextShape() { const fxns = [LShape, JShape, SShape, ZShape, TShape, OShape, IShape]; const index = Math.floor(Math.random() * fxns.length); const fxn = fxns[index]; const nextShape = new fxn(); if (nextShape.overlapsOtherShape()) { gameIsOver = true; clearInterval(timerId); } return nextShape; } function startGame() { currentShape = getNextShape(); timerId = setInterval(function() { currentShape.moveDown(); }, 1000); } /*** MAIN ***/ window.addEventListener("keydown", onKeyDown); startGame(); })(); </script>
Newer Post
Older Post
Home