3dimojitetris / index.html
manmohanai's picture
create 3d tetris game which we can rotate. The blocks should have emoji like simley etc. - Initial Deployment
52ed030 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Emoji Tetris</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
perspective: 1000px;
overflow: hidden;
background: linear-gradient(135deg, #1a1a2e, #16213e);
}
.game-container {
transform-style: preserve-3d;
transition: transform 0.5s ease;
}
.block {
transform-style: preserve-3d;
transition: all 0.2s ease;
position: relative;
}
.block-face {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
backface-visibility: hidden;
border: 1px solid rgba(255, 255, 255, 0.1);
box-sizing: border-box;
}
.front { transform: translateZ(25px); }
.back { transform: rotateY(180deg) translateZ(25px); }
.right { transform: rotateY(90deg) translateZ(25px); }
.left { transform: rotateY(-90deg) translateZ(25px); }
.top { transform: rotateX(90deg) translateZ(25px); }
.bottom { transform: rotateX(-90deg) translateZ(25px); }
@keyframes fall {
from { transform: translateY(-100px); }
to { transform: translateY(0); }
}
.falling {
animation: fall 0.3s ease-out;
}
</style>
</head>
<body class="min-h-screen flex flex-col items-center justify-center text-white font-mono">
<h1 class="text-4xl font-bold mb-4 text-center bg-clip-text text-transparent bg-gradient-to-r from-pink-500 to-violet-500">
3D Emoji Tetris
</h1>
<div class="mb-4 flex gap-4">
<div class="text-center">
<p class="text-xl">Score</p>
<p id="score" class="text-3xl font-bold">0</p>
</div>
<div class="text-center">
<p class="text-xl">Level</p>
<p id="level" class="text-3xl font-bold">1</p>
</div>
</div>
<div class="relative w-full max-w-md h-96 flex items-center justify-center">
<div id="game" class="game-container w-64 h-96 relative">
<!-- Game grid will be generated here -->
</div>
<div id="game-over" class="absolute inset-0 bg-black bg-opacity-70 flex flex-col items-center justify-center hidden">
<h2 class="text-3xl font-bold mb-4">Game Over!</h2>
<p id="final-score" class="text-xl mb-6">Your score: 0</p>
<button id="restart" class="px-6 py-2 bg-pink-600 rounded-lg hover:bg-pink-700 transition">
Play Again
</button>
</div>
</div>
<div class="mt-6 text-center">
<p class="mb-2">Controls:</p>
<div class="grid grid-cols-3 gap-2 max-w-xs mx-auto">
<div class="bg-gray-800 p-2 rounded">← Left</div>
<div class="bg-gray-800 p-2 rounded">→ Right</div>
<div class="bg-gray-800 p-2 rounded">↑ Rotate</div>
<div class="bg-gray-800 p-2 rounded">↓ Down</div>
<div class="bg-gray-800 p-2 rounded">Space Drop</div>
<div class="bg-gray-800 p-2 rounded">R 3D Rotate</div>
</div>
</div>
<div id="next-piece" class="mt-6 text-center">
<p class="mb-2">Next Piece:</p>
<div id="next-piece-display" class="w-24 h-24 mx-auto grid grid-cols-4 gap-1"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Game constants
const COLS = 10;
const ROWS = 20;
const BLOCK_SIZE = 30;
const EMOJIS = ['😀', '😎', '🤩', '😍', '🤪', '🧐', '🥳', '🤯', '👻', '🤠', '👽', '🤖'];
// Game variables
let grid = Array(ROWS).fill().map(() => Array(COLS).fill(0));
let currentPiece = null;
let nextPiece = null;
let gameOver = false;
let score = 0;
let level = 1;
let gameRotation = 0;
let dropInterval = 1000;
let dropStart = null;
let animationFrameId = null;
// DOM elements
const gameEl = document.getElementById('game');
const scoreEl = document.getElementById('score');
const levelEl = document.getElementById('level');
const gameOverEl = document.getElementById('game-over');
const finalScoreEl = document.getElementById('final-score');
const restartBtn = document.getElementById('restart');
const nextPieceDisplay = document.getElementById('next-piece-display');
// Initialize game
function init() {
createGrid();
generateNewPiece();
generateNextPiece();
updateScore();
gameLoop();
addEventListeners();
}
// Create game grid
function createGrid() {
gameEl.innerHTML = '';
gameEl.style.width = `${COLS * BLOCK_SIZE}px`;
gameEl.style.height = `${ROWS * BLOCK_SIZE}px`;
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
const cell = document.createElement('div');
cell.className = 'absolute border border-gray-800 bg-gray-900';
cell.style.width = `${BLOCK_SIZE}px`;
cell.style.height = `${BLOCK_SIZE}px`;
cell.style.left = `${x * BLOCK_SIZE}px`;
cell.style.top = `${y * BLOCK_SIZE}px`;
cell.dataset.x = x;
cell.dataset.y = y;
gameEl.appendChild(cell);
}
}
}
// Game pieces
const PIECES = [
{ shape: [[1,1,1,1]], color: 'bg-red-500', emoji: EMOJIS[0] }, // I
{ shape: [[1,1,1], [0,1,0]], color: 'bg-blue-500', emoji: EMOJIS[1] }, // T
{ shape: [[1,1,0], [0,1,1]], color: 'bg-green-500', emoji: EMOJIS[2] }, // Z
{ shape: [[0,1,1], [1,1,0]], color: 'bg-yellow-500', emoji: EMOJIS[3] }, // S
{ shape: [[1,1], [1,1]], color: 'bg-purple-500', emoji: EMOJIS[4] }, // O
{ shape: [[1,1,1], [1,0,0]], color: 'bg-pink-500', emoji: EMOJIS[5] }, // L
{ shape: [[1,1,1], [0,0,1]], color: 'bg-indigo-500', emoji: EMOJIS[6] } // J
];
// Generate a new random piece
function generateNewPiece() {
if (nextPiece) {
currentPiece = nextPiece;
} else {
const randomIndex = Math.floor(Math.random() * PIECES.length);
currentPiece = {
...PIECES[randomIndex],
x: Math.floor(COLS / 2) - Math.floor(PIECES[randomIndex].shape[0].length / 2),
y: 0,
rotation: 0
};
}
generateNextPiece();
// Check if game over
if (collision()) {
gameOver = true;
gameOverEl.classList.remove('hidden');
finalScoreEl.textContent = `Your score: ${score}`;
}
}
// Generate next piece for preview
function generateNextPiece() {
const randomIndex = Math.floor(Math.random() * PIECES.length);
nextPiece = {
...PIECES[randomIndex],
x: Math.floor(COLS / 2) - Math.floor(PIECES[randomIndex].shape[0].length / 2),
y: 0,
rotation: 0
};
updateNextPieceDisplay();
}
// Update next piece display
function updateNextPieceDisplay() {
nextPieceDisplay.innerHTML = '';
const shape = nextPiece.shape;
// Center the piece in the display
const offsetX = Math.floor((4 - shape[0].length) / 2);
const offsetY = Math.floor((4 - shape.length) / 2);
for (let y = 0; y < 4; y++) {
for (let x = 0; x < 4; x++) {
const cell = document.createElement('div');
cell.className = 'w-6 h-6 flex items-center justify-center';
if (y - offsetY >= 0 && y - offsetY < shape.length &&
x - offsetX >= 0 && x - offsetX < shape[0].length &&
shape[y - offsetY][x - offsetX]) {
cell.innerHTML = `<div class="w-full h-full ${nextPiece.color} flex items-center justify-center">${nextPiece.emoji}</div>`;
}
nextPieceDisplay.appendChild(cell);
}
}
}
// Draw the game
function draw() {
// Clear the grid
document.querySelectorAll('#game > div').forEach(cell => {
cell.innerHTML = '';
cell.className = 'absolute border border-gray-800 bg-gray-900';
cell.style.width = `${BLOCK_SIZE}px`;
cell.style.height = `${BLOCK_SIZE}px`;
});
// Draw locked pieces
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
if (grid[y][x]) {
const cell = document.querySelector(`[data-x="${x}"][data-y="${y}"]`);
if (cell) {
cell.innerHTML = create3DBlock(grid[y][x].color, grid[y][x].emoji);
cell.classList.add(grid[y][x].color);
}
}
}
}
// Draw current piece
if (currentPiece) {
const shape = rotateShape(currentPiece.shape, currentPiece.rotation);
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[y].length; x++) {
if (shape[y][x]) {
const posX = currentPiece.x + x;
const posY = currentPiece.y + y;
if (posY >= 0 && posX >= 0 && posX < COLS) {
const cell = document.querySelector(`[data-x="${posX}"][data-y="${posY}"]`);
if (cell) {
cell.innerHTML = create3DBlock(currentPiece.color, currentPiece.emoji);
cell.classList.add(currentPiece.color, 'falling');
}
}
}
}
}
}
}
// Create 3D block with emoji
function create3DBlock(color, emoji) {
return `
<div class="block w-full h-full">
<div class="block-face front ${color}">${emoji}</div>
<div class="block-face back ${color}">${emoji}</div>
<div class="block-face right ${color}">${emoji}</div>
<div class="block-face left ${color}">${emoji}</div>
<div class="block-face top ${color}">${emoji}</div>
<div class="block-face bottom ${color}">${emoji}</div>
</div>
`;
}
// Rotate shape
function rotateShape(shape, rotation) {
rotation = rotation % 4;
if (rotation < 0) rotation += 4;
if (rotation === 0) return shape;
let rotated = shape;
for (let i = 0; i < rotation; i++) {
rotated = rotated[0].map((_, index) =>
rotated.map(row => row[index]).reverse()
);
}
return rotated;
}
// Check for collisions
function collision() {
const shape = rotateShape(currentPiece.shape, currentPiece.rotation);
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[y].length; x++) {
if (shape[y][x]) {
const posX = currentPiece.x + x;
const posY = currentPiece.y + y;
if (posY >= ROWS || posX < 0 || posX >= COLS || (posY >= 0 && grid[posY][posX])) {
return true;
}
}
}
}
return false;
}
// Lock piece in place
function lockPiece() {
const shape = rotateShape(currentPiece.shape, currentPiece.rotation);
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[y].length; x++) {
if (shape[y][x]) {
const posY = currentPiece.y + y;
if (posY >= 0) {
grid[posY][currentPiece.x + x] = {
color: currentPiece.color,
emoji: currentPiece.emoji
};
}
}
}
}
// Check for completed lines
checkLines();
// Generate new piece
generateNewPiece();
}
// Check for completed lines
function checkLines() {
let linesCleared = 0;
for (let y = ROWS - 1; y >= 0; y--) {
if (grid[y].every(cell => cell !== 0)) {
// Remove the line
grid.splice(y, 1);
// Add new empty line at top
grid.unshift(Array(COLS).fill(0));
linesCleared++;
y++; // Check the same row again
}
}
if (linesCleared > 0) {
// Update score
score += [0, 40, 100, 300, 1200][linesCleared] * level;
// Increase level every 10 lines
const newLevel = Math.floor(score / 1000) + 1;
if (newLevel > level) {
level = newLevel;
dropInterval = Math.max(100, 1000 - (level - 1) * 100);
}
updateScore();
}
}
// Update score display
function updateScore() {
scoreEl.textContent = score;
levelEl.textContent = level;
}
// Game loop
function gameLoop(timestamp) {
if (gameOver) return;
if (!dropStart) dropStart = timestamp;
const delta = timestamp - dropStart;
if (delta > dropInterval) {
moveDown();
dropStart = null;
}
draw();
animationFrameId = requestAnimationFrame(gameLoop);
}
// Move piece down
function moveDown() {
currentPiece.y++;
if (collision()) {
currentPiece.y--;
lockPiece();
}
}
// Move piece left
function moveLeft() {
currentPiece.x--;
if (collision()) {
currentPiece.x++;
}
}
// Move piece right
function moveRight() {
currentPiece.x++;
if (collision()) {
currentPiece.x--;
}
}
// Rotate piece
function rotatePiece() {
currentPiece.rotation++;
if (collision()) {
// Try wall kicks
const originalX = currentPiece.x;
const kicks = [-1, 1, -2, 2];
for (const kick of kicks) {
currentPiece.x += kick;
if (!collision()) return;
currentPiece.x = originalX;
}
currentPiece.rotation--;
}
}
// Drop piece instantly
function hardDrop() {
while (!collision()) {
currentPiece.y++;
}
currentPiece.y--;
lockPiece();
}
// Rotate game view
function rotateGameView() {
gameRotation = (gameRotation + 90) % 360;
gameEl.style.transform = `rotateY(${gameRotation}deg)`;
}
// Event listeners
function addEventListeners() {
document.addEventListener('keydown', e => {
if (gameOver) return;
switch (e.key) {
case 'ArrowLeft':
moveLeft();
break;
case 'ArrowRight':
moveRight();
break;
case 'ArrowDown':
moveDown();
break;
case 'ArrowUp':
rotatePiece();
break;
case ' ':
hardDrop();
break;
case 'r':
case 'R':
rotateGameView();
break;
}
});
restartBtn.addEventListener('click', () => {
// Reset game
grid = Array(ROWS).fill().map(() => Array(COLS).fill(0));
currentPiece = null;
nextPiece = null;
gameOver = false;
score = 0;
level = 1;
dropInterval = 1000;
gameRotation = 0;
gameEl.style.transform = 'rotateY(0deg)';
gameOverEl.classList.add('hidden');
// Start new game
generateNewPiece();
generateNextPiece();
updateScore();
dropStart = null;
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
gameLoop();
});
}
// Start the game
init();
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=manmohanai/3dimojitetris" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>