Created
February 9, 2025 06:17
-
-
Save KevinWang15/39852939163fbf68c257705703fcd17c to your computer and use it in GitHub Desktop.
Tetris by Gemini Advanced 2.0 Pro
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Tetris</title> | |
<style> | |
body { | |
background-color: #222; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
height: 100vh; | |
margin: 0; | |
} | |
#game-canvas { | |
border: 4px solid white; | |
} | |
#score, #level, #lines { | |
color: white; | |
font-family: sans-serif; | |
margin-left: 20px; /* Add some spacing from the canvas */ | |
font-size: 1.2em; | |
} | |
#next-piece-canvas { | |
border: 2px solid white; | |
margin-left: 20px; /* Add some spacing from the canvas */ | |
} | |
#game-info{ | |
display: flex; | |
flex-direction: column; | |
} | |
#game-container { | |
display: flex; | |
flex-direction: row; | |
align-items: flex-start; | |
} | |
.hidden { | |
display: none; | |
} | |
#game-over-screen { | |
position: absolute; /* Important for overlay */ | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
background-color: rgba(0, 0, 0, 0.7); | |
color: white; | |
padding: 20px; | |
border: 2px solid white; | |
text-align: center; | |
z-index: 10; /* Make sure it's on top */ | |
} | |
.button { | |
background-color: #4CAF50; /* Green */ | |
border: none; | |
color: white; | |
padding: 15px 32px; | |
text-align: center; | |
text-decoration: none; | |
display: inline-block; | |
font-size: 16px; | |
margin: 4px 2px; | |
cursor: pointer; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="game-container"> | |
<canvas id="game-canvas" width="300" height="600"></canvas> | |
<div id = "game-info"> | |
<canvas id="next-piece-canvas" width="120" height="120"></canvas> | |
<div id="score">Score: 0</div> | |
<div id="level">Level: 1</div> | |
<div id="lines">Lines: 0</div> | |
</div> | |
</div> | |
<div id="game-over-screen" class="hidden"> | |
<h2>Game Over</h2> | |
<p id="final-score"></p> | |
<button id="restart-button" class="button">Restart</button> | |
</div> | |
<script> | |
const canvas = document.getElementById('game-canvas'); | |
const context = canvas.getContext('2d'); | |
const nextPieceCanvas = document.getElementById('next-piece-canvas'); | |
const nextPieceContext = nextPieceCanvas.getContext('2d'); | |
const scoreElement = document.getElementById('score'); | |
const levelElement = document.getElementById('level'); | |
const linesElement = document.getElementById('lines'); | |
const gameOverScreen = document.getElementById('game-over-screen'); | |
const finalScoreElement = document.getElementById('final-score'); | |
const restartButton = document.getElementById('restart-button'); | |
const blockSize = 30; | |
const boardWidth = 10; | |
const boardHeight = 20; | |
const nextPieceBoardWidth = 4; | |
const nextPieceBoardHeight = 4; | |
// Game state | |
let board = []; | |
let currentPiece; | |
let nextPiece; | |
let score = 0; | |
let level = 1; | |
let linesCleared = 0; | |
let dropCounter = 0; | |
let dropInterval = 1000; // Initial drop interval (1 second) | |
let lastTime = 0; | |
let isGameOver = false; | |
// Initialize the game board | |
function createBoard(width, height) { | |
return Array(height).fill(null).map(() => Array(width).fill(0)); | |
} | |
// Tetromino shapes (I, J, L, O, S, Z, T) | |
const tetrominoes = [ | |
// I | |
[ | |
[0, 0, 0, 0], | |
[1, 1, 1, 1], | |
[0, 0, 0, 0], | |
[0, 0, 0, 0] | |
], | |
// J | |
[ | |
[2, 0, 0], | |
[2, 2, 2], | |
[0, 0, 0] | |
], | |
// L | |
[ | |
[0, 0, 3], | |
[3, 3, 3], | |
[0, 0, 0] | |
], | |
// O | |
[ | |
[4, 4], | |
[4, 4] | |
], | |
// S | |
[ | |
[0, 5, 5], | |
[5, 5, 0], | |
[0, 0, 0] | |
], | |
// Z | |
[ | |
[6, 6, 0], | |
[0, 6, 6], | |
[0, 0, 0] | |
], | |
// T | |
[ | |
[0, 7, 0], | |
[7, 7, 7], | |
[0, 0, 0] | |
] | |
]; | |
const colors = [ | |
null, | |
'cyan', // I | |
'blue', // J | |
'orange', // L | |
'yellow', // O | |
'green', // S | |
'red', // Z | |
'purple' // T | |
]; | |
// Create a new random tetromino | |
function createPiece() { | |
const randomIndex = Math.floor(Math.random() * tetrominoes.length); | |
const piece = tetrominoes[randomIndex]; | |
return { | |
shape: piece, | |
x: Math.floor(boardWidth / 2) - Math.floor(piece[0].length / 2), | |
y: 0, | |
color: randomIndex + 1 | |
}; | |
} | |
function drawNextPiece() { | |
nextPieceContext.clearRect(0, 0, nextPieceCanvas.width, nextPieceCanvas.height); // Clear previous piece | |
if (!nextPiece) return; // No next piece yet | |
nextPiece.shape.forEach((row, y) => { | |
row.forEach((value, x) => { | |
if (value !== 0) { | |
nextPieceContext.fillStyle = colors[nextPiece.color]; | |
nextPieceContext.fillRect((x) * blockSize, (y) * blockSize, blockSize, blockSize); | |
nextPieceContext.strokeRect((x) * blockSize, (y) * blockSize, blockSize, blockSize); | |
} | |
}); | |
}); | |
} | |
// Draw a single block | |
function drawBlock(x, y, color, ctx) { | |
ctx.fillStyle = color; | |
ctx.fillRect(x * blockSize, y * blockSize, blockSize, blockSize); | |
ctx.strokeRect(x * blockSize, y * blockSize, blockSize, blockSize); | |
} | |
// Draw the board and the current piece | |
function draw() { | |
context.fillStyle = '#000'; | |
context.fillRect(0, 0, canvas.width, canvas.height); | |
board.forEach((row, y) => { | |
row.forEach((value, x) => { | |
if (value !== 0) { | |
drawBlock(x, y, colors[value], context); | |
} | |
}); | |
}); | |
if (currentPiece) { | |
currentPiece.shape.forEach((row, y) => { | |
row.forEach((value, x) => { | |
if (value !== 0) { | |
drawBlock(currentPiece.x + x, currentPiece.y + y, colors[currentPiece.color], context); | |
} | |
}); | |
}); | |
} | |
drawNextPiece(); | |
updateScore(); | |
} | |
// Move the piece down | |
function drop() { | |
currentPiece.y++; | |
if (collide(board, currentPiece)) { | |
currentPiece.y--; | |
merge(board, currentPiece); | |
resetPiece(); | |
clearLines(); | |
if (collide(board, currentPiece)) { | |
// Game over | |
gameOver(); | |
return; | |
} | |
} | |
dropCounter = 0; | |
} | |
// Move the piece left or right | |
function move(dir) { | |
currentPiece.x += dir; | |
if (collide(board, currentPiece)) { | |
currentPiece.x -= dir; | |
} | |
} | |
// Rotate the piece | |
function rotate(piece) { | |
const rotated = []; | |
for (let i = 0; i < piece.shape[0].length; i++) { | |
rotated.push([]); | |
for (let j = piece.shape.length - 1; j >= 0; j--) { | |
rotated[i].push(piece.shape[j][i]); | |
} | |
} | |
const tempPiece = { ...piece, shape: rotated }; | |
//wall kicks, test different offsets | |
const offsets = [0, -1, 1, -2, 2]; // Common offsets | |
for (const offset of offsets) { | |
tempPiece.x = piece.x + offset; // Apply offset | |
if (!collide(board, tempPiece)) { | |
currentPiece.shape = rotated; | |
currentPiece.x +=offset; | |
return; | |
} | |
} | |
} | |
// Check for collisions | |
function collide(board, piece) { | |
for (let y = 0; y < piece.shape.length; y++) { | |
for (let x = 0; x < piece.shape[y].length; x++) { | |
if (piece.shape[y][x] !== 0 && | |
(board[y + piece.y] && board[y + piece.y][x + piece.x]) !== 0) { | |
return true; // Collision detected | |
} | |
// Check for out-of-bounds | |
if (piece.shape[y][x] !== 0 && | |
((y + piece.y >= boardHeight) || //bottom | |
(x + piece.x < 0 ) || //left | |
(x + piece.x >= boardWidth))) //right | |
{ | |
return true; | |
} | |
} | |
} | |
return false; // No collision | |
} | |
// Merge the piece into the board | |
function merge(board, piece) { | |
piece.shape.forEach((row, y) => { | |
row.forEach((value, x) => { | |
if (value !== 0) { | |
board[y + piece.y][x + piece.x] = piece.color; | |
} | |
}); | |
}); | |
} | |
// Reset the piece to the top | |
function resetPiece() { | |
currentPiece = nextPiece; | |
nextPiece = createPiece(); | |
currentPiece.y = 0; | |
currentPiece.x = Math.floor(boardWidth / 2) - Math.floor(currentPiece.shape[0].length / 2); | |
} | |
function clearLines() { | |
let linesToClear = []; | |
board.forEach((row, y) => { | |
if (row.every(value => value !== 0)) { | |
linesToClear.push(y); | |
} | |
}); | |
linesToClear.forEach(y => { | |
board.splice(y, 1); //remove the line | |
board.unshift(Array(boardWidth).fill(0)); //add an empty line at the top | |
linesCleared++; //global lines cleared count | |
score += 100 * level; | |
}); | |
//level and speed | |
if (linesCleared >= level * 10) { | |
level++; | |
dropInterval *= 0.8; // Speed up the game (e.g., 20% faster) | |
} | |
} | |
//update score | |
function updateScore() { | |
scoreElement.textContent = `Score: ${score}`; | |
levelElement.textContent = `Level: ${level}`; | |
linesElement.textContent = `Lines: ${linesCleared}`; | |
} | |
//game over | |
function gameOver(){ | |
isGameOver = true; | |
finalScoreElement.textContent = `Final Score: ${score}`; | |
gameOverScreen.classList.remove('hidden'); | |
} | |
// Game loop | |
function update(time = 0) { | |
if (isGameOver) return; // Stop the loop if game over | |
const deltaTime = time - lastTime; | |
lastTime = time; | |
dropCounter += deltaTime; | |
if (dropCounter > dropInterval) { | |
drop(); | |
} | |
draw(); | |
requestAnimationFrame(update); | |
} | |
function startGame() { | |
board = createBoard(boardWidth, boardHeight); | |
currentPiece = createPiece(); | |
nextPiece = createPiece(); // Initialize nextPiece | |
score = 0; | |
level = 1; | |
linesCleared = 0; | |
dropInterval = 1000; | |
isGameOver = false; | |
gameOverScreen.classList.add('hidden'); //hide game over screen | |
update(); | |
} | |
// Event listeners for keyboard input | |
document.addEventListener('keydown', event => { | |
if (event.key === 'ArrowLeft' || event.key === 'a') { | |
move(-1); | |
} else if (event.key === 'ArrowRight' || event.key === 'd') { | |
move(1); | |
} else if (event.key === 'ArrowDown' || event.key === 's') { | |
drop(); | |
} else if (event.key === 'ArrowUp' || event.key === 'w') { | |
rotate(currentPiece); | |
} | |
}); | |
restartButton.addEventListener('click', startGame); | |
startGame(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment