Tetris. played by AI.
Tetris AI
<!DOCTYPE html> <html> <head> <title>Tetris AI</title> <style> body { margin: 0; padding: 0; } canvas { display: block; margin: auto; background: #111; } </style> </head> <body> <canvas id="canvas" width="300" height="600"></canvas> <script type="text/javascript"> // Define variables var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var width = canvas.width / 10; var height = canvas.height / 20; var board = []; var score = 0; var interval; var currentPiece; var nextPiece; var aiMoves = []; var moveCounter = 0; // Define tetromino shapes var shapes = [ { color: "#00FFFF", blocks: [[0, 1], [1, 1], [2, 1], [3, 1]] }, // I { color: "#FFA500", blocks: [[0, 0], [0, 1], [1, 1], [2, 1]] }, // L { color: "#FFFF00", blocks: [[0, 1], [1, 1], [2, 1], [2, 0]] }, // J { color: "#FF0000", blocks: [[0, 1], [1, 1], [1, 0], [2, 0]] }, // Z { color: "#800080", blocks: [[0, 0], [1, 0], [1, 1], [2, 1]] }, // S { color: "#00FF00", blocks: [[0, 1], [1, 1], [1, 0], [2, 0]] }, // T { color: "#FF00FF", blocks: [[0, 0], [1, 0], [2, 0], [1, 1]] } // O ]; // Create game board function init() { for (var i = 0; i < 20; i++) { board[i] = []; for (var j = 0; j < 10; j++) { board[i][j] = ""; } } } // Randomly select next piece function getNextPiece() { var shape = Math.floor(Math.random() * shapes.length); var blocks = shapes[shape].blocks; var color = shapes[shape].color; var piece = { blocks: blocks, color: color }; return piece; } // Draw tetromino piece function drawPiece(piece) { ctx.fillStyle = piece.color; for (var i = 0; i < piece.blocks.length; i++) { var x = piece.blocks[i][0] + currentPiece.x; var y = piece.blocks[i][1] + currentPiece.y; ctx.fillRect(x * width, y * height, width, height); ctx.strokeRect(x * width, y * height, width, height); } } // Erase tetromino piece function erasePiece(piece) { ctx.clearRect(0, 0, canvas.width, canvas.height); for (var i = 0; i < board.length; i++) { for (var j = 0; j < board[i].length; j++) { if (board[i][j] !== "") { ctx.fillStyle = board[i][j]; ctx.fillRect(j * width, i * height, width, height); ctx.strokeRect(j * width, i * height, width, height); } } } } // Move piece down function moveDown() { erasePiece(currentPiece); if (canMove(0, 1, currentPiece.blocks)) { currentPiece.y++; } else { lockPiece(); currentPiece = nextPiece; nextPiece = getNextPiece(); } drawPiece(currentPiece); } // Lock piece in place function lockPiece() { for (var i = 0; i < currentPiece.blocks.length; i++) { var x = currentPiece.blocks[i][0] + currentPiece.x; var y = currentPiece.blocks[i][1] + currentPiece.y; board[y][x] = currentPiece.color; } checkLine(); } // Check if piece can move in given direction function canMove(dx, dy, blocks) { for (var i = 0; i < blocks.length; i++) { var x = blocks[i][0] + currentPiece.x + dx; var y = blocks[i][1] + currentPiece.y + dy; if (x < 0 || x >= 10 || y >= 20 || board[y][x] !== "") { return false; } } return true; } // Check if any lines are complete function checkLine() { var count = 0; for (var i = 0; i < board.length; i++) { if (board[i].every((val) => val !== "")) { board.splice(i, 1); board.unshift(new Array(10).fill("")); count++; } } switch (count) { case 1: score += 100; break; case 2: score += 300; break; case 3: score += 500; break; case 4: score += 800; break; default: break; } if (score >= 1000 && !interval) { interval = setInterval(moveAI, 500); } } // Make a move for the AI function makeAIMove() { var move = aiMoves[moveCounter]; switch (move) { case "left": erasePiece(currentPiece); if (canMove(-1, 0, currentPiece.blocks)) { currentPiece.x--; } drawPiece(currentPiece); break; case "right": erasePiece(currentPiece); if (canMove(1, 0, currentPiece.blocks)) { currentPiece.x++; } drawPiece(currentPiece); break; case "down": moveDown(); break; case "rotate": erasePiece(currentPiece); rotatePiece(); drawPiece(currentPiece); break; default: break; } moveCounter++; if (moveCounter === aiMoves.length) { clearInterval(interval); interval = null; moveCounter = 0; aiMoves = []; } } // Move piece left function moveLeft() { erasePiece(currentPiece); if (canMove(-1, 0, currentPiece.blocks)) { currentPiece.x--; } drawPiece(currentPiece); } // Move piece right function moveRight() { erasePiece(currentPiece); if (canMove(1, 0, currentPiece.blocks)) { currentPiece.x++; } drawPiece(currentPiece); } // Rotate piece function rotatePiece() { var rotatedBlocks = []; for (var i = 0; i < currentPiece.blocks.length; i++) { var x = currentPiece.blocks[i][0]; var y = currentPiece.blocks[i][1]; rotatedBlocks.push([-y, x]); } if (canMove(0, 0, rotatedBlocks)) { currentPiece.blocks = rotatedBlocks; } } // Move piece down until it can't go further function dropPiece() { while (canMove(0, 1, currentPiece.blocks)) { moveDown(); } } // Handle keyboard input document.addEventListener("keydown", function (e) { switch (e.keyCode) { case 37: // left arrow moveLeft(); break; case 38: // up arrow erasePiece(currentPiece); rotatePiece(); drawPiece(currentPiece); break; case 39: // right arrow moveRight(); break; case 40: // down arrow moveDown(); break; case 32: // spacebar dropPiece(); break; default: break; } }); // AI plays the game function moveAI() { var bestMove = getBestMove(); aiMoves = aiMoves.concat(bestMove.moves); makeAIMove(); if (bestMove.score > score) { score = bestMove.score; document.getElementById( "score" ).innerText = `Score: ${score} (AI)`; } } // Get the best move for the AI function getBestMove() { var moves = [ { name: "down", score: 0 }, { name: "left", score: 0 }, { name: "right", score: 0 }, { name: "rotate", score: 0 }, ]; for (var i = 0; i < moves.length; i++) { var move = moves[i]; erasePiece(currentPiece); switch (move.name) { case "left": if (canMove(-1, 0, currentPiece.blocks)) { currentPiece.x--; move.score = getHighestScore(0); } break; case "right": if (canMove(1, 0, currentPiece.blocks)) { currentPiece.x++; move.score = getHighestScore(0); } break; case "rotate": rotatePiece(); move.score = getHighestScore(0); break; case "down": move.score = getHighestScore(1); break; default: break; } moves[i] = move; erasePiece(currentPiece); } var bestMove = moves.reduce((a, b) => (a.score > b.score ? a : b)); return bestMove; } // Get the highest score for a given move function getHighestScore(depth) { var highestScore = score; var dropped = false; while (canMove(0, 1, currentPiece.blocks)) { moveDown(); dropped = true; } if (depth > 0 || dropped) { lockPiece(); currentPiece = nextPiece; nextPiece = getNextPiece(); if (depth > 0) { checkLine(); highestScore = score; } else { highestScore = getBestMove().score; } } return highestScore; } // Start game init(); currentPiece = getNextPiece(); nextPiece = getNextPiece(); drawPiece(currentPiece); document.getElementById( "score" ).innerText = `Score: ${score} (Human)`; // Show joke console.log( "Why did the Tetris piece refuse to fit in? It had commitment issues." ); </script> </body> </html>