<style>
.game-container {
display: flex;
flex-direction: column;
align-items: center;
background-color: #1a1a2e;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
padding: 1rem;
font-family: 'Inter', sans-serif;
color: #e0e0e0;
}
#gameCanvas {
border: 2px solid #4a90e2;
background-color: #0f0f1a;
border-radius: 8px;
touch-action: none;
}
.game-info {
display: flex;
justify-content: space-between;
width: 100%;
margin-top: 1rem;
padding: 0 1rem;
}
.game-controls {
display: flex;
justify-content: space-around;
width: 100%;
margin-top: 1rem;
}
.control-button {
background-color: #4a90e2;
border: none;
color: white;
padding: 1rem;
font-size: 1.5rem;
border-radius: 50%;
cursor: pointer;
transition: background-color 0.3s, transform 0.1s;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
width: 60px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
}
.control-button:hover {
background-color: #63b3ed;
}
.control-button:active {
background-color: #2b6cb0;
transform: scale(0.95);
}
</style>
<div class="game-container">
<div class="game-info">
<div class="score">Score: <span id="score">0</span></div>
<div class="level">Level: <span id="level">1</span></div>
</div>
<canvas id="gameCanvas"></canvas>
<div class="game-controls">
<button id="leftBtn" class="control-button">←</button>
<button id="rotateBtn" class="control-button">↻</button>
<button id="rightBtn" class="control-button">→</button>
<button id="downBtn" class="control-button">↓</button>
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const levelElement = document.getElementById('level');
const leftBtn = document.getElementById('leftBtn');
const rotateBtn = document.getElementById('rotateBtn');
const rightBtn = document.getElementById('rightBtn');
const downBtn = document.getElementById('downBtn');
const TILE_SIZE = 30;
const COLUMNS = 10;
const ROWS = 20;
canvas.width = COLUMNS * TILE_SIZE;
canvas.height = ROWS * TILE_SIZE;
const SHAPES = [
// I
[[1, 1, 1, 1]],
// O
[[1, 1], [1, 1]],
// T
[[0, 1, 0], [1, 1, 1]],
// S
[[0, 1, 1], [1, 1, 0]],
// Z
[[1, 1, 0], [0, 1, 1]],
// L
[[0, 0, 1], [1, 1, 1]],
// J
[[1, 0, 0], [1, 1, 1]]
];
const COLORS = ['cyan', 'yellow', 'purple', 'green', 'red', 'orange', 'blue'];
let board = Array.from({ length: ROWS }, () => Array(COLUMNS).fill(0));
let currentPiece, currentColor, currentX, currentY;
let score = 0;
let level = 1;
let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;
function getRandomPiece() {
const index = Math.floor(Math.random() * SHAPES.length);
return {
shape: SHAPES[index],
color: COLORS[index]
};
}
function spawnPiece() {
const newPiece = getRandomPiece();
currentPiece = newPiece.shape;
currentColor = newPiece.color;
currentX = Math.floor(COLUMNS / 2) - Math.floor(currentPiece[0].length / 2);
currentY = 0;
if (checkCollision()) {
// Game over
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#fff';
ctx.font = '24px Inter';
ctx.textAlign = 'center';
ctx.fillText('Game Over', canvas.width / 2, canvas.height / 2 - 20);
ctx.fillText('Score: ' + score, canvas.width / 2, canvas.height / 2 + 10);
setTimeout(() => {
board = Array.from({ length: ROWS }, () => Array(COLUMNS).fill(0));
score = 0;
level = 1;
dropInterval = 1000;
updateUI();
spawnPiece();
}, 3000);
}
}
function checkCollision(x = currentX, y = currentY, piece = currentPiece) {
for (let r = 0; r < piece.length; r++) {
for (let c = 0; c < piece[r].length; c++) {
if (piece[r][c] !== 0) {
const newX = x + c;
const newY = y + r;
if (newX < 0 || newX >= COLUMNS || newY >= ROWS || (board[newY] && board[newY][newX] !== 0)) {
return true;
}
}
}
}
return false;
}
function mergePiece() {
for (let r = 0; r < currentPiece.length; r++) {
for (let c = 0; c < currentPiece[r].length; c++) {
if (currentPiece[r][c] !== 0) {
board[currentY + r][currentX + c] = 1;
}
}
}
}
function clearLines() {
let linesCleared = 0;
let newBoard = [];
for (let r = 0; r < ROWS; r++) {
if (board[r].every(cell => cell !== 0)) {
linesCleared++;
} else {
newBoard.push(board[r]);
}
}
// Fill the top with empty rows
while (newBoard.length < ROWS) {
newBoard.unshift(Array(COLUMNS).fill(0));
}
board = newBoard;
if (linesCleared > 0) {
let multiplier = 0;
if (linesCleared === 1) multiplier = 1;
else if (linesCleared === 2) multiplier = 2;
else if (linesCleared === 3) multiplier = 4;
else if (linesCleared === 4) multiplier = 8;
score += (linesCleared * COLUMNS * 10) * multiplier;
if (score >= level * 10000) {
level++;
dropInterval /= 1.5;
}
updateUI();
}
}
function rotatePiece() {
const newPiece = currentPiece[0].map((_, colIndex) => currentPiece.map(row => row[colIndex]).reverse());
if (!checkCollision(currentX, currentY, newPiece)) {
currentPiece = newPiece;
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw board
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLUMNS; c++) {
if (board[r][c] !== 0) {
ctx.fillStyle = '#4a90e2';
ctx.fillRect(c * TILE_SIZE, r * TILE_SIZE, TILE_SIZE, TILE_SIZE);
ctx.strokeStyle = '#2b6cb0';
ctx.lineWidth = 2;
ctx.strokeRect(c * TILE_SIZE, r * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
}
// Draw current piece
if (currentPiece) {
ctx.fillStyle = currentColor;
for (let r = 0; r < currentPiece.length; r++) {
for (let c = 0; c < currentPiece[r].length; c++) {
if (currentPiece[r][c] !== 0) {
ctx.fillRect((currentX + c) * TILE_SIZE, (currentY + r) * TILE_SIZE, TILE_SIZE, TILE_SIZE);
ctx.strokeStyle = '#0f0f1a';
ctx.lineWidth = 2;
ctx.strokeRect((currentX + c) * TILE_SIZE, (currentY + r) * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
}
}
}
function updateUI() {
scoreElement.textContent = score;
levelElement.textContent = level;
}
function gameLoop(time = 0) {
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
currentY++;
if (checkCollision()) {
currentY--;
mergePiece();
clearLines();
spawnPiece();
}
dropCounter = 0;
}
draw();
requestAnimationFrame(gameLoop);
}
// --- Button Controls ---
leftBtn.addEventListener('click', () => {
currentX--;
if (checkCollision()) {
currentX++;
}
draw();
});
rightBtn.addEventListener('click', () => {
currentX++;
if (checkCollision()) {
currentX--;
}
draw();
});
rotateBtn.addEventListener('click', () => {
rotatePiece();
draw();
});
downBtn.addEventListener('click', () => {
currentY++;
if (checkCollision()) {
currentY--;
mergePiece();
clearLines();
spawnPiece();
}
dropCounter = 0;
draw();
});
// Initial setup
spawnPiece();
updateUI();
gameLoop();
})();
</script>