can you make a bot that plays it
Awesome idea! I've added an AI bot that plays Tetris for you! 🤖 Hit the "🤖 Bot ON" button to watch it think and place pieces automatically. You can toggle it off anytime to play yourself. The bot looks ahead and scores each possible position to find the best move!
can you make the bot hidden and only turns on if you press b
Done! The bot button is now hidden — just press **B** on your keyboard to secretly toggle the bot on and off. A tiny message will flash on screen so you know it's active! 🤫🤖
can you also make it so the highset score is saved nomatter who plays it
Done! The high score is now saved in your browser's memory using localStorage — so it stays even if you close the tab or someone else plays! 🏆 See if you (or the bot!) can beat it!
nothing is showing
Oops, let me fix that! I'll rebuild the whole thing fresh so everything shows up correctly. 🎮
still doesent work
Let me completely rebuild it from scratch — I think something got tangled up in the code! 🛠️
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Tetris!</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #000; display: flex; justify-content: center; align-items: center; min-height: 100vh; font-family: 'Segoe UI', sans-serif; color: #fff; } #wrapper { display: flex; gap: 24px; align-items: flex-start; padding: 20px; } #gameCanvas { border: 3px solid #e94560; box-shadow: 0 0 24px #e9456088; background: #0f0f23; display: block; } #sidebar { display: flex; flex-direction: column; gap: 18px; min-width: 130px; } .panel { background: #16213e; border: 2px solid #e94560; border-radius: 10px; padding: 14px; text-align: center; } .panel h3 { font-size: 13px; color: #e94560; text-transform: uppercase; letter-spacing: 2px; margin-bottom: 8px; } .panel p { font-size: 26px; font-weight: bold; color: #fff; } .panel p.highlight { color: #ffd700; } #nextCanvas { display: block; margin: 0 auto; background: #0f0f23; border-radius: 6px; } #startBtn { background: #e94560; color: #fff; border: none; border-radius: 8px; padding: 12px 0; width: 100%; font-size: 16px; font-weight: bold; cursor: pointer; letter-spacing: 1px; } #startBtn:hover { background: #c73652; } .controls { font-size: 12px; color: #aaa; line-height: 1.8; text-align: left; } .controls span { color: #e94560; font-weight: bold; } #overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.88); display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 10; } #overlay h1 { font-size: 48px; color: #e94560; text-shadow: 0 0 20px #e94560; margin-bottom: 10px; } #overlay p { font-size: 20px; color: #fff; margin-bottom: 30px; } #overlay button { background: #e94560; color: #fff; border: none; border-radius: 10px; padding: 14px 40px; font-size: 20px; font-weight: bold; cursor: pointer; margin: 6px; } #overlay button:hover { background: #c73652; } .new-record { color: #ffd700; font-size: 22px; font-weight: bold; margin-bottom: 8px; text-shadow: 0 0 12px #ffd700; } #botToast { position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%); background: rgba(0,229,255,0.15); border: 1.5px solid #00e5ff; color: #00e5ff; border-radius: 8px; padding: 8px 22px; font-size: 14px; font-weight: bold; letter-spacing: 2px; pointer-events: none; opacity: 0; transition: opacity 0.4s; z-index: 100; } #botToast.show { opacity: 1; } </style> </head> <body> <div id="overlay"> <h1>🎮 TETRIS!</h1> <p>Stack the blocks, clear the lines!</p> <button id="playBtn">▶ Play!</button> </div> <div id="wrapper"> <canvas id="gameCanvas" width="300" height="600"></canvas> <div id="sidebar"> <div class="panel"> <h3>Score</h3> <p id="scoreVal">0</p> </div> <div class="panel"> <h3>🏆 Best</h3> <p id="highScoreVal" class="highlight">0</p> </div> <div class="panel"> <h3>Level</h3> <p id="levelVal">1</p> </div> <div class="panel"> <h3>Lines</h3> <p id="linesVal">0</p> </div> <div class="panel"> <h3>Next</h3> <canvas id="nextCanvas" width="100" height="100"></canvas> </div> <button id="startBtn">↻ Restart</button> <div class="panel controls"> <span>← →</span> Move<br> <span>↑</span> Rotate<br> <span>↓</span> Soft Drop<br> <span>Space</span> Hard Drop<br> <span>P</span> Pause </div> </div> </div> <div id="botToast">🤖 BOT ON</div> <script> (function() { var canvas = document.getElementById('gameCanvas'); var ctx = canvas.getContext('2d'); var nCanvas = document.getElementById('nextCanvas'); var nCtx = nCanvas.getContext('2d'); var COLS = 10, ROWS = 20, SZ = 30; var COLORS = [ null, '#00e5ff', '#ff9100', '#2979ff', '#ffea00', '#00e676', '#d500f9', '#ff1744' ]; var SHAPES = [ [], [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], [[2,0,0],[2,2,2],[0,0,0]], [[0,0,3],[3,3,3],[0,0,0]], [[4,4],[4,4]], [[0,5,5],[5,5,0],[0,0,0]], [[0,6,0],[6,6,6],[0,0,0]], [[7,7,0],[0,7,7],[0,0,0]] ]; var board, piece, nextPiece, score, level, lines, gameOver, paused, animId, dropCounter, dropInterval, lastTime; var botEnabled = false; var botMoveTimer = 0; var botTarget = null; var botMoveInterval = 80; var toastTimeout = null; var highScore = 0; try { highScore = parseInt(localStorage.getItem('tetrisHighScore') || '0', 10); } catch(e) {} document.getElementById('highScoreVal').textContent = highScore; function saveHighScore() { try { localStorage.setItem('tetrisHighScore', highScore); } catch(e) {} } function updateHighScore() { if (score > highScore) { highScore = score; saveHighScore(); } document.getElementById('highScoreVal').textContent = highScore; } function createBoard() { var b = []; for (var r = 0; r < ROWS; r++) { var row = []; for (var c = 0; c < COLS; c++) row.push(0); b.push(row); } return b; } function randomPiece() { var id = Math.floor(Math.random() * 7) + 1; var shape = []; for (var r = 0; r < SHAPES[id].length; r++) shape.push(SHAPES[id][r].slice()); var p = { id: id, shape: shape, x: Math.floor(COLS / 2) - Math.floor(shape[0].length / 2), y: 0 }; return p; } function collide(b, p, ox, oy, shape) { ox = ox || 0; oy = oy || 0; var s = shape || p.shape; for (var r = 0; r < s.length; r++) { for (var c = 0; c < s[r].length; c++) { if (!s[r][c]) continue; var nx = p.x + c + ox; var ny = p.y + r + oy; if (nx < 0 || nx >= COLS || ny >= ROWS) return true; if (ny >= 0 && b[ny][nx]) return true; } } return false; } function rotate(shape) { var N = shape.length; var M = shape[0].length; var result = []; for (var i = 0; i < M; i++) { var row = []; for (var j = 0; j < N; j++) row.push(0); result.push(row); } for (var r = 0; r < N; r++) for (var c = 0; c < M; c++) result[c][N - 1 - r] = shape[r][c]; return result; } function placePiece() { for (var r = 0; r < piece.shape.length; r++) { for (var c = 0; c < piece.shape[r].length; c++) { if (piece.shape[r][c] && piece.y + r >= 0) { board[piece.y + r][piece.x + c] = piece.shape[r][c]; } } } } function clearLines() { var cleared = 0; for (var r = ROWS - 1; r >= 0; r--) { var full = true; for (var c = 0; c < COLS; c++) { if (!board[r][c]) { full = false; break; } } if (full) { board.splice(r, 1); var empty = []; for (var cc = 0; cc < COLS; cc++) empty.push(0); board.unshift(empty); cleared++; r++; } } if (cleared) { var pts = [0, 100, 300, 500, 800]; score += (pts[cleared] || 800) * level; lines += cleared; level = Math.floor(lines / 10) + 1; dropInterval = Math.max(80, 1000 - (level - 1) * 90); updateUI(); } } function updateUI() { document.getElementById('scoreVal').textContent = score; document.getElementById('levelVal').textContent = level; document.getElementById('linesVal').textContent = lines; updateHighScore(); } function drawBlock(color, x, y, sz, context) { context.fillStyle = color; context.fillRect(x * sz + 1, y * sz + 1, sz - 2, sz - 2); context.fillStyle = 'rgba(255,255,255,0.25)'; context.fillRect(x * sz + 2, y * sz + 2, sz - 4, 6); } function getGhostY() { var gy = 0; while (!collide(board, piece, 0, gy + 1)) gy++; return gy; } function drawBoard() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.strokeStyle = '#1e1e3a'; ctx.lineWidth = 0.5; for (var r = 0; r < ROWS; r++) for (var c = 0; c < COLS; c++) ctx.strokeRect(c * SZ, r * SZ, SZ, SZ); for (var rr = 0; rr < ROWS; rr++) for (var cc = 0; cc < COLS; cc++) if (board[rr][cc]) drawBlock(COLORS[board[rr][cc]], cc, rr, SZ, ctx); var ghostY = getGhostY(); for (var r2 = 0; r2 < piece.shape.length; r2++) { for (var c2 = 0; c2 < piece.shape[r2].length; c2++) { if (piece.shape[r2][c2]) { ctx.fillStyle = 'rgba(255,255,255,0.12)'; ctx.fillRect((piece.x + c2) * SZ + 1, (piece.y + r2 + ghostY) * SZ + 1, SZ - 2, SZ - 2); } } } for (var r3 = 0; r3 < piece.shape.length; r3++) for (var c3 = 0; c3 < piece.shape[r3].length; c3++) if (piece.shape[r3][c3]) drawBlock(COLORS[piece.shape[r3][c3]], piece.x + c3, piece.y + r3, SZ, ctx); } function drawNext() { nCtx.clearRect(0, 0, nCanvas.width, nCanvas.height); var s = nextPiece.shape; var bsz = 20; var offX = Math.floor((5 - s[0].length) / 2); var offY = Math.floor((5 - s.length) / 2); for (var r = 0; r < s.length; r++) for (var c = 0; c < s[r].length; c++) if (s[r][c]) drawBlock(COLORS[s[r][c]], offX + c, offY + r, bsz, nCtx); } function spawnPiece() { piece = nextPiece; nextPiece = randomPiece(); drawNext(); botTarget = null; if (collide(board, piece)) { endGame(); return; } if (botEnabled) botTarget = computeBotTarget(); } function endGame() { gameOver = true; cancelAnimationFrame(animId); updateHighScore(); var isNew = score > 0 && score >= highScore; setTimeout(function() { var overlay = document.getElementById('overlay'); overlay.innerHTML = '<h1>💀 Game Over</h1>' + (isNew ? '<div class="new-record">🏆 New High Score!</div>' : '') + '<p>Score: <b>' + score + '</b> | Level: <b>' + level + '</b></p>' + '<p style="font-size:15px;color:#ffd700;margin-bottom:20px;">Best: <b>' + highScore + '</b></p>' + '<button id="againBtn">▶ Play Again!</button>'; overlay.style.display = 'flex'; document.getElementById('againBtn').addEventListener('click', startGame); }, 300); } function startGame() { document.getElementById('overlay').style.display = 'none'; board = createBoard(); score = 0; level = 1; lines = 0; dropCounter = 0; dropInterval = 1000; lastTime = 0; paused = false; gameOver = false; botMoveTimer = 0; botTarget = null; nextPiece = randomPiece(); spawnPiece(); updateUI(); cancelAnimationFrame(animId); animId = requestAnimationFrame(loop); } function loop(time) { if (!time) time = 0; if (gameOver) return; var delta = time - lastTime; lastTime = time; if (!paused) { dropCounter += delta; if (botEnabled) { botMoveTimer += delta; if (botMoveTimer >= botMoveInterval) { botMoveTimer = 0; doBotStep(); } } if (dropCounter >= dropInterval) { moveDown(); dropCounter = 0; } drawBoard(); } animId = requestAnimationFrame(loop); } function moveDown() { if (!collide(board, piece, 0, 1)) { piece.y++; } else { placePiece(); clearLines(); spawnPiece(); dropCounter = 0; } } function hardDrop() { var gy = getGhostY(); piece.y += gy; score += gy * 2; updateUI(); placePiece(); clearLines(); spawnPiece(); dropCounter = 0; } // ── BOT ────────────────────────────────────────────────────────────────── function showBotToast(on) { var toast = document.getElementById('botToast'); toast.textContent = on ? '🤖 BOT ON' : '🤖 BOT OFF'; toast.classList.add('show'); if (toastTimeout) clearTimeout(toastTimeout); toastTimeout = setTimeout(function() { toast.classList.remove('show'); }, 1800); } function toggleBot() { botEnabled = !botEnabled; showBotToast(botEnabled); if (botEnabled && piece) botTarget = computeBotTarget(); else botTarget = null; } function dropY(b, shape, px) { var tempP = { shape: shape, x: px, y: piece.y }; while (!collide(b, tempP, 0, 1)) tempP.y++; return tempP.y; } function simulatePlace(b, shape, px, py) { var nb = []; for (var r = 0; r < ROWS; r++) nb.push(b[r].slice()); for (var rr = 0; rr < shape.length; rr++) for (var cc = 0; cc < shape[rr].length; cc++) if (shape[rr][cc] && py + rr >= 0 && py + rr < ROWS && px + cc >= 0 && px + cc < COLS) nb[py + rr][px + cc] = shape[rr][cc]; return nb; } function scoreBoard(b) { var heights = []; var holes = 0, bumpiness = 0, completedLines = 0; for (var c = 0; c < COLS; c++) { var h = 0, found = false; for (var r = 0; r < ROWS; r++) { if (b[r][c] && !found) { h = ROWS - r; found = true; } if (found && !b[r][c]) holes++; } heights.push(h); } var aggH = 0; for (var i = 0; i < heights.length; i++) aggH += heights[i]; for (var j = 0; j < COLS - 1; j++) bumpiness += Math.abs(heights[j] - heights[j+1]); for (var rr = 0; rr < ROWS; rr++) { var full = true; for (var cc = 0; cc < COLS; cc++) if (!b[rr][cc]) { full = false; break; } if (full) completedLines++; } return -0.51 * aggH + 0.76 * completedLines - 0.36 * holes - 0.18 * bumpiness; } function getRotations(shape) { var rotations = [], seen = [], cur = []; for (var r = 0; r < shape.length; r++) cur.push(shape[r].slice()); for (var i = 0; i < 4; i++) { var key = JSON.stringify(cur); var dup = false; for (var k = 0; k < seen.length; k++) if (seen[k] === key) { dup = true; break; } if (dup) break; seen.push(key); var copy = []; for (var rr = 0; rr < cur.length; rr++) copy.push(cur[rr].slice()); rotations.push(copy); cur = rotate(cur); } return rotations; } function computeBotTarget() { if (!piece) return null; var rotations = getRotations(piece.shape); var bestScore = -Infinity, bestX = piece.x, bestShape = piece.shape; for (var ri = 0; ri < rotations.length; ri++) { var shape = rotations[ri]; var w = shape[0].length; for (var px = -1; px <= COLS - w + 1; px++) { var tempP = { shape: shape, x: px, y: piece.y }; if (collide(board, tempP, 0, 0)) continue; var py = dropY(board, shape, px); var sim = simulatePlace(board, shape, px, py); var s = scoreBoard(sim); if (s > bestScore) { bestScore = s; bestX = px; bestShape = shape; } } } return { x: bestX, shape: bestShape }; } function doBotStep() { if (!botTarget) { botTarget = computeBotTarget(); return; } var tKey = JSON.stringify(botTarget.shape); var cKey = JSON.stringify(piece.shape); if (cKey !== tKey) { var rot = rotate(piece.shape); var kick = 0; if (collide(board, piece, 0, 0, rot)) kick = piece.x > COLS / 2 ? -1 : 1; if (!collide(board, piece, kick, 0, rot)) { piece.shape = rot; piece.x += kick; } return; } if (piece.x < botTarget.x) { if (!collide(board, piece, 1, 0)) piece.x++; return; } if (piece.x > botTarget.x) { if (!collide(board, piece, -1, 0)) piece.x--; return; } hardDrop(); botTarget = null; } // ── KEYBOARD ───────────────────────────────────────────────────────────── document.addEventListener('keydown', function(e) { if (gameOver) return; if (e.key === 'b' || e.key === 'B') { toggleBot(); return; } if (e.key === 'p' || e.key === 'P') { paused = !paused; if (!paused) { lastTime = performance.now(); animId = requestAnimationFrame(loop); } return; } if (paused || botEnabled) return; if (e.key === 'ArrowLeft') { if (!collide(board, piece, -1, 0)) piece.x--; } else if (e.key === 'ArrowRight') { if (!collide(board, piece, 1, 0)) piece.x++; } else if (e.key === 'ArrowDown') { moveDown(); score += 1; updateUI(); dropCounter = 0; } else if (e.key === 'ArrowUp') { var rot = rotate(piece.shape); var kick = 0; if (collide(board, piece, 0, 0, rot)) kick = piece.x > COLS / 2 ? -1 : 1; if (!collide(board, piece, kick, 0, rot)) { piece.shape = rot; piece.x += kick; } } else if (e.key === ' ') { e.preventDefault(); hardDrop(); } }); // ── BUTTONS ────────────────────────────────────────────────────────────── document.getElementById('playBtn').addEventListener('click', startGame); document.getElementById('startBtn').addEventListener('click', startGame); })(); </script> </body> </html>
No pictures yet. Add one, then ask your buddy to put it in your app!