Tetris
Tetris
Why did the tetromino go to the gym?
To get fit in shape!
<!DOCTYPE html>
<html>
<head>
<title>Tetris</title>
<style>
body {
margin: 0;
padding: 0;
}
canvas {
display: block;
margin: 0 auto;
background-color: #111;
}
</style>
</head>
<body>
<canvas id="tetris" width="240" height="400"></canvas>
<script>
const canvas = document.getElementById('tetris');
const context = canvas.getContext('2d');
// Draw a square on the canvas
function drawSquare(x, y, color) {
context.fillStyle = color;
context.fillRect(x * 24, y * 24, 24, 24);
context.strokeStyle = '#333';
context.strokeRect(x * 24, y * 24, 24, 24);
}
// Create the game board with an empty grid
let board = [];
for (let row = 0; row < 20; row++) {
board[row] = [];
for (let col = 0; col < 10; col++) {
board[row][col] = '#111';
}
}
// Draw the game board
function drawBoard() {
for (let row = 0; row < 20; row++) {
for (let col = 0; col < 10; col++) {
drawSquare(col, row, board[row][col]);
}
}
}
drawBoard();
// Tetromino shapes and colors
const tetrominos = [
['#f00', '#0f0', '#00f', '#ff0'], // T
['#0f0', '#00f', '#f00', '#ff0'], // S
['#00f', '#f00', '#0f0', '#ff0'], // Z
['#ff0', '#00f', '#f00', '#0f0'], // L
['#f00', '#ff0', '#0f0', '#00f'], // J
['#0f0', '#f00', '#ff0', '#00f'], // O
['#00f', '#0f0', '#f00', '#ff0'] // I
];
// Tetromino shapes and their rotations
const tetrominoShapes = [
[[0, 0, 0], [1, 1, 1], [0, 1, 0]], // T
[[0, 2, 2], [2, 2, 0], [0, 0, 0]], // S
[[3, 3, 0], [0, 3, 3], [0, 0, 0]], // Z
[[4, 0, 0], [4, 4, 4], [0, 0, 0]], // L
[[0, 0, 5], [5, 5, 5], [0, 0, 0]], // J
[[6, 6], [6, 6]], // O
[[0, 7, 0, 0], [0, 7, 0, 0], [0, 7, 0, 0], [0, 7, 0, 0]] // I
];
// Create a random tetromino shape and set its starting position
let currentTetromino = {
shape: tetrominoShapes[Math.floor(Math.random() * tetrominoShapes.length)],
color: tetrominos[Math.floor(Math.random() * tetrominos.length)],
x: 3,
y: -2
};
// Draw a tetromino shape on the canvas
function drawTetromino(tetromino, x, y) {
for (let row = 0; row < tetromino.length; row++) {
for (let col = 0; col < tetromino[row].length; col++) {
if (tetromino[row][col]) {
drawSquare(col + x, row + y, tetromino.color);
}
}
}
}
// Clear the complete rows and move the rows above down
function clearLines() {
for (let row = 0; row < board.length; row++) {
if (board[row].every(col => col !== '#111')) {
board.splice(row, 1);
board.unshift(['#111', '#111', '#111', '#111', '#111', '#111', '#111', '#111', '#111', '#111']);
row--;
}
}
}
// Rotate a tetromino shape clockwise
function rotateTetromino(tetromino) {
let rotatedTetromino = [];
for (let row = 0; row < tetromino.length; row++) {
rotatedTetromino[row] = [];
for (let col = 0; col < tetromino[row].length; col++) {
rotatedTetromino[row][col] = tetromino[tetromino.length - col - 1][row];
}
}
return rotatedTetromino;
}
// Check if the new position of the tetromino is valid
function isValidMove(tetromino, board, x, y) {
for (let row = 0; row < tetromino.length; row++) {
for (let col = 0; col < tetromino[row].length; col++) {
if (tetromino[row][col]) {
let newX = x + col;
let newY = y + row;
if (newX < 0 || newX >= board[0].length || newY >= board.length) {
return false;
}
if (newY >= 0 && board[newY][newX] !== '#111') {
return false;
}
}
}
}
return true;
}
// Move the current tetromino down one row
function moveTetrominoDown() {
if (isValidMove(currentTetromino.shape, board, currentTetromino.x, currentTetromino.y + 1)) {
currentTetromino.y++;
drawBoard();
drawTetromino(currentTetromino, currentTetromino.x, currentTetromino.y);
} else {
// Add the current tetromino to the board
for (let row = 0; row < currentTetromino.shape.length; row++) {
for (let col = 0; col < currentTetromino.shape[row].length; col++) {
if (currentTetromino.shape[row][col]) {
board[currentTetromino.y + row][currentTetromino.x + col] = currentTetromino.color;
}
}
}
clearLines();
// Create a new tetromino and set its starting position
currentTetromino = {
shape: tetrominoShapes[Math.floor(Math.random() * tetrominoShapes.length)],
color: tetrominos[Math.floor(Math.random() * tetrominos.length)],
x: 3,
y: -2
};
if (!isValidMove(currentTetromino.shape, board, currentTetromino.x, currentTetromino.y)) {
// Game over
context.fillStyle = '#fff';
context.font = 'bold 30px Arial';
context.fillText('Game Over!', 50, 200);
} else {
drawBoard();
drawTetromino(currentTetromino, currentTetromino.x, currentTetromino.y);
}
}
}
// Move the current tetromino left one column
function moveTetrominoLeft() {
if (isValidMove(currentTetromino.shape, board, currentTetromino.x - 1, currentTetromino.y)) {
currentTetromino.x--;
drawBoard();
drawTetromino(currentTetromino, currentTetromino.x, currentTetromino.y);
}
}
// Move the current tetromino right one column
function moveTetrominoRight() {
if (isValidMove(currentTetromino.shape, board, currentTetromino.x + 1, currentTetromino.y)) {
currentTetromino.x++;
drawBoard();
drawTetromino(currentTetromino, currentTetromino.x, currentTetromino.y);
}
}
// Rotate the current tetromino
function rotateTetrominoOnce() {
let rotatedTetromino = rotateTetromino(currentTetromino.shape);
if (isValidMove(rotatedTetromino, board, currentTetromino.x, currentTetromino.y)) {
currentTetromino.shape = rotatedTetromino;
drawBoard();
drawTetromino(currentTetromino, currentTetromino.x, currentTetromino.y);
}
}
// Keyboard controls for the game
document.addEventListener('keydown', event => {
switch (event.keyCode) {
case 37: // Left arrow
moveTetrominoLeft();
break;
case 38: // Up arrow
rotateTetrominoOnce();
break;
case 39: // Right arrow
moveTetrominoRight();
break;
case 40: // Down arrow
moveTetrominoDown();
break;
}
});
// Game loop
setInterval(moveTetrominoDown, 500);
</script>
<p>Why did the tetromino go to the gym?<br> To get fit in shape!</p>
</body>
</html>