<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Retro Snake 80s</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body {
background-color: #111;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
color: #0f0;
font-family: 'Press Start 2P', cursive;
overflow: hidden;
}
#game-container {
position: relative;
border: 5px solid #333;
border-radius: 20px;
padding: 20px;
background: #000;
box-shadow: 0 0 50px rgba(0, 255, 0, 0.2);
}
canvas {
background-color: #000;
border: 2px solid #0f0;
box-shadow: 0 0 20px #0f0, inset 0 0 20px #0f0;
display: block;
}
.ui {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
text-shadow: 2px 2px #003300;
}
.scanlines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
rgba(255,255,255,0),
rgba(255,255,255,0) 50%,
rgba(0,0,0,0.2) 50%,
rgba(0,0,0,0.2)
);
background-size: 100% 4px;
pointer-events: none;
z-index: 10;
border-radius: 15px;
}
.flicker {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 255, 0, 0.02);
opacity: 0;
pointer-events: none;
z-index: 11;
animation: flicker 0.15s infinite;
}
@keyframes flicker {
0% { opacity: 0.02; }
50% { opacity: 0.05; }
100% { opacity: 0.02; }
}
#game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
display: none;
z-index: 20;
background: rgba(0, 0, 0, 0.9);
padding: 20px;
border: 2px solid #0f0;
}
button {
background: #0f0;
color: #000;
border: none;
padding: 10px 20px;
font-family: 'Press Start 2P', cursive;
font-size: 12px;
cursor: pointer;
margin-top: 20px;
}
button:hover {
background: #fff;
}
</style>
</head>
<body>
<div id="game-container">
<div class="scanlines"></div>
<div class="flicker"></div>
<div class="ui">
<div id="score-text">SCORE: 0</div>
<div id="highscore-text">HI: 0</div>
</div>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div id="game-over">
<h1 style="color: red; text-shadow: 2px 2px #500;">GAME OVER</h1>
<p>SCORE FINAL : <span id="final-score">0</span></p>
<button onclick="resetGame()">REJOUER</button>
<p>(Appuyez sur Entrée pour rejouer)</p>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score-text');
const highScoreEl = document.getElementById('highscore-text');
const gameOverScreen = document.getElementById('game-over');
const finalScoreEl = document.getElementById('final-score');
const gridSize = 20;
const tileCount = canvas.width / gridSize;
let score = 0;
let highScore = localStorage.getItem('snakeHighScore') || 0;
highScoreEl.innerText = 'HI: ' + highScore;
let snake = [];
let food = { x: 15, y: 15 };
let dx = 0;
let dy = 0;
let gameInterval;
let isGameRunning = false; // boucle en cours
let isStarted = false; // la partie a-t-elle commencé ?
// File d’entrées : directions successives {dx, dy}
const inputQueue = [];
function initGame() {
snake = [
{ x: 10, y: 10 },
{ x: 9, y: 10 },
{ x: 8, y: 10 }
];
score = 0;
dx = 1;
dy = 0;
inputQueue.length = 0;
scoreEl.innerText = 'SCORE: ' + score;
gameOverScreen.style.display = 'none';
isGameRunning = false;
isStarted = false;
if (gameInterval) clearInterval(gameInterval);
createFood();
draw(); // plateau en attente
}
function startGameLoop() {
if (isGameRunning) return;
isGameRunning = true;
isStarted = true;
gameInterval = setInterval(gameLoop, 100);
}
function resetGame() {
initGame();
}
function gameLoop() {
if (!isGameRunning) return;
update();
draw();
}
function update() {
// Appliquer la direction en attente au début du tick
// Consommer au plus UNE direction de la file au début du tick
if (inputQueue.length > 0) {
const nextDir = inputQueue.shift();
dx = nextDir.dx;
dy = nextDir.dy;
}
let head = {
x: snake[0].x + dx,
y: snake[0].y + dy
};
// Wrap autour
if (head.x < 0) {
head.x = tileCount - 1;
} else if (head.x >= tileCount) {
head.x = 0;
}
if (head.y < 0) {
head.y = tileCount - 1;
} else if (head.y >= tileCount) {
head.y = 0;
}
// Collision avec soi-même
for (let i = 0; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
endGame();
return;
}
}
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score += 1;
scoreEl.innerText = 'SCORE: ' + score;
createFood();
} else {
snake.pop();
}
}
function draw() {
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = '#111';
for (let i = 0; i < tileCount; i++) {
ctx.beginPath();
ctx.moveTo(i * gridSize, 0);
ctx.lineTo(i * gridSize, canvas.height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i * gridSize);
ctx.lineTo(canvas.width, i * gridSize);
ctx.stroke();
}
ctx.fillStyle = '#ff0055';
ctx.shadowBlur = 15;
ctx.shadowColor = "#ff0055";
ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize - 2, gridSize - 2);
ctx.shadowBlur = 0;
snake.forEach((part, index) => {
if (index === 0) {
ctx.shadowBlur = 10;
ctx.shadowColor = "#0f0";
ctx.fillStyle = '#ccffcc';
} else {
ctx.shadowBlur = 0;
ctx.fillStyle = '#0f0';
}
ctx.fillRect(part.x * gridSize, part.y * gridSize, gridSize - 2, gridSize - 2);
});
// Message d’attente
if (!isStarted && gameOverScreen.style.display === 'none') {
ctx.shadowBlur = 6;
ctx.shadowColor = '#0f0';
ctx.fillStyle = '#0f0';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.font = '12px "Press Start 2P", cursive';
// plus haut que le serpent : proche du bord supérieur du canvas
ctx.fillText('APPUYEZ SUR ESPACE', canvas.width / 2, 4);
ctx.shadowBlur = 0;
}
}
function createFood() {
food = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
snake.forEach(part => {
if (part.x === food.x && part.y === food.y) createFood();
});
}
function endGame() {
isGameRunning = false;
clearInterval(gameInterval);
finalScoreEl.innerText = score;
gameOverScreen.style.display = 'block';
if (score > highScore) {
highScore = score;
localStorage.setItem('snakeHighScore', highScore);
highScoreEl.innerText = 'HI: ' + highScore;
}
}
document.addEventListener('keydown', function(event) {
const key = event.key;
// ESPACE : démarrer la partie
if (key === ' ' || event.code === 'Space') {
if (!isStarted) {
event.preventDefault();
startGameLoop();
return;
}
}
// ENTRÉE : relancer après un game over
if (key === 'Enter') {
if (gameOverScreen.style.display === 'block') {
event.preventDefault();
resetGame();
return;
}
}
// Contrôles de direction (uniquement si la partie est lancée)
if (!isStarted) return;
const LEFT_KEY = 37;
const RIGHT_KEY = 39;
const UP_KEY = 38;
const DOWN_KEY = 40;
const keyCode = event.keyCode;
const goingUp = dy === -1;
const goingDown = dy === 1;
const goingRight = dx === 1;
const goingLeft = dx === -1;
let newDx = dx;
let newDy = dy;
if (keyCode === LEFT_KEY && !goingRight) {
newDx = -1;
newDy = 0;
}
if (keyCode === UP_KEY && !goingDown) {
newDx = 0;
newDy = -1;
}
if (keyCode === RIGHT_KEY && !goingLeft) {
newDx = 1;
newDy = 0;
}
if (keyCode === DOWN_KEY && !goingUp) {
newDx = 0;
newDy = 1;
}
// Si la direction change, on l’ajoute dans la file d’entrées
if (newDx !== dx || newDy !== dy) {
inputQueue.push({ dx: newDx, dy: newDy });
}
});
// Premier affichage
// Attendre que les polices soient prêtes, puis démarrer le jeu
if (document.fonts && document.fonts.ready) {
document.fonts.ready.then(() => {
initGame();
});
} else {
// Fallback pour les navigateurs plus anciens
window.addEventListener('load', initGame);
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Arcade Breakout Retro</title>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
<style>
/* --- STYLE RETRO --- */
body {
background-color: #111;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
color: white;
font-family: 'Press Start 2P', cursive;
overflow: hidden;
}
/* Conteneur du jeu pour l'effet CRT */
#game-container {
position: relative;
box-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
border: 4px solid #333;
border-radius: 10px;
}
canvas {
background-color: #000;
display: block;
}
/* Effet de scanlines (lignes TV) */
.scanlines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
rgba(255,255,255,0),
rgba(255,255,255,0) 50%,
rgba(0,0,0,0.2) 50%,
rgba(0,0,0,0.2)
);
background-size: 100% 4px;
pointer-events: none; /* Laisse passer les clics */
z-index: 10;
}
/* Petit effet de lueur interne */
.glow {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: inset 0 0 50px rgba(0,0,0,0.7);
pointer-events: none;
z-index: 11;
}
#start-message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: #0ff;
text-shadow: 2px 2px #f0f;
z-index: 20;
line-height: 1.5;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="myCanvas" width="800" height="600"></canvas>
<div class="scanlines"></div>
<div class="glow"></div>
<div id="start-message">CLIQUEZ POUR JOUER<br><span style="font-size: 0.6em; color: #fff;">SOURIS OU FLÈCHES POUR BOUGER</span></div>
</div>
<script>
// --- LOGIQUE DU JEU ---
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
const startMsg = document.getElementById("start-message");
// Variables de la balle
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 4;
let dy = -4;
const ballRadius = 8;
// Variables de la raquette
const paddleHeight = 15;
const paddleWidth = 100;
let paddleX = (canvas.width - paddleWidth) / 2;
// Contrôles
let rightPressed = false;
let leftPressed = false;
let gameRunning = false;
// Variables des briques
const brickRowCount = 5;
const brickColumnCount = 9;
const brickWidth = 70;
const brickHeight = 20;
const brickPadding = 15;
const brickOffsetTop = 50;
const brickOffsetLeft = 25;
// Score et Vies
let score = 0;
let lives = 3;
// Création du tableau de briques
const bricks = [];
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (let r = 0; r < brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 };
}
}
// Écouteurs d'événements
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);
canvas.addEventListener("click", startGame, false);
startMsg.addEventListener("click", startGame, false);
function startGame() {
if (!gameRunning) {
gameRunning = true;
startMsg.style.display = "none";
draw();
}
}
function keyDownHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
rightPressed = true;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
rightPressed = false;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
leftPressed = false;
}
}
function mouseMoveHandler(e) {
const relativeX = e.clientX - canvas.getBoundingClientRect().left;
if (relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth / 2;
}
}
function collisionDetection() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const b = bricks[c][r];
if (b.status === 1) {
if (x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) {
dy = -dy;
b.status = 0;
score++;
// Augmenter la vitesse légèrement à chaque brique cassée
if(score % 5 === 0) {
dx = dx > 0 ? dx + 0.5 : dx - 0.5;
dy = dy > 0 ? dy + 0.5 : dy - 0.5;
}
if (score === brickRowCount * brickColumnCount) {
alert("C'EST GAGNÉ, BRAVO !");
document.location.reload();
}
}
}
}
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#fff"; // Blanc pur pour la balle
ctx.shadowBlur = 10;
ctx.shadowColor = "#fff";
ctx.fill();
ctx.closePath();
ctx.shadowBlur = 0; // Reset shadow
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
ctx.fillStyle = "#0ff"; // Cyan néon
ctx.shadowBlur = 15;
ctx.shadowColor = "#0ff";
ctx.fill();
ctx.closePath();
ctx.shadowBlur = 0;
}
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status === 1) {
const brickX = (c * (brickWidth + brickPadding)) + brickOffsetLeft;
const brickY = (r * (brickHeight + brickPadding)) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
// Couleurs alternées par rangée
if (r === 0) ctx.fillStyle = "#ff0000"; // Rouge
else if (r === 1) ctx.fillStyle = "#ff7f00"; // Orange
else if (r === 2) ctx.fillStyle = "#ffff00"; // Jaune
else if (r === 3) ctx.fillStyle = "#00ff00"; // Vert
else ctx.fillStyle = "#0000ff"; // Bleu
ctx.fill();
ctx.closePath();
}
}
}
}
function drawScore() {
ctx.font = "16px 'Press Start 2P'";
ctx.fillStyle = "#fff";
ctx.fillText("Score: " + score, 8, 25);
}
function drawLives() {
ctx.font = "16px 'Press Start 2P'";
ctx.fillStyle = "#fff";
ctx.fillText("Vies: " + lives, canvas.width - 130, 25);
}
function draw() {
// Effacer le canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks();
drawBall();
drawPaddle();
drawScore();
drawLives();
collisionDetection();
// Rebond murs latéraux
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
// Rebond plafond
if (y + dy < ballRadius) {
dy = -dy;
} else if (y + dy > canvas.height - ballRadius - paddleHeight + 5) { // Niveau raquette
// Collision raquette
if (x > paddleX && x < paddleX + paddleWidth) {
// Effet d'angle selon où on tape la raquette
let collidePoint = x - (paddleX + paddleWidth/2);
collidePoint = collidePoint / (paddleWidth/2);
// Angle maximum de 60 degrés (Pi/3)
let angle = collidePoint * (Math.PI/3);
// Vitesse constante, change juste la direction
let speed = Math.sqrt(dx*dx + dy*dy);
dx = speed * Math.sin(angle);
dy = -speed * Math.cos(angle);
}
else if (y + dy > canvas.height - ballRadius) {
// Perdu une vie
lives--;
if (!lives) {
alert("GAME OVER");
document.location.reload();
} else {
x = canvas.width / 2;
y = canvas.height - 30;
dx = 4;
dy = -4;
paddleX = (canvas.width - paddleWidth) / 2;
}
}
}
// Déplacement balle
x += dx;
y += dy;
// Déplacement raquette clavier
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
} else if (leftPressed && paddleX > 0) {
paddleX -= 7;
}
if(gameRunning) {
requestAnimationFrame(draw);
}
}
// Premier rendu statique
drawBricks();
drawPaddle();
drawScore();
drawLives();
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Neon Tetris Retro</title>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
<style>
body {
background-color: #111;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
color: white;
font-family: 'Press Start 2P', cursive;
overflow: hidden;
user-select: none;
}
#game-wrapper {
position: relative;
display: flex;
gap: 20px;
padding: 20px;
border: 4px solid #444;
border-radius: 10px;
box-shadow: 0 0 30px rgba(0, 255, 255, 0.2);
background-color: #000;
}
canvas {
background-color: #111;
border: 2px solid #333;
display: block;
}
#ui-panel {
display: flex;
flex-direction: column;
width: 180px;
}
.stat-label {
font-size: 0.6em;
color: #aaa;
margin-top: 15px;
margin-bottom: 5px;
text-transform: uppercase;
letter-spacing: 2px;
}
.stat-value {
font-size: 1.2em;
color: #0ff;
text-shadow: 2px 2px #f0f;
margin-bottom: 10px;
}
.controls-section {
margin-top: auto;
padding-top: 20px;
border-top: 2px dashed #333;
}
.control-row {
display: flex;
align-items: center;
margin-bottom: 12px;
font-size: 0.6em;
color: #ddd;
}
.key-cap {
display: inline-flex;
justify-content: center;
align-items: center;
width: 24px;
height: 24px;
border: 2px solid #fff;
border-radius: 4px;
margin-right: 10px;
color: #0ff;
background: #222;
box-shadow: 0 0 5px rgba(0, 255, 255, 0.5);
font-size: 1.2em;
font-weight: bold;
}
.scanlines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
rgba(255,255,255,0),
rgba(255,255,255,0) 50%,
rgba(0,0,0,0.1) 50%,
rgba(0,0,0,0.1)
);
background-size: 100% 4px;
pointer-events: none;
z-index: 10;
}
#overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 20;
text-align: center;
cursor: pointer;
}
#overlay h1 {
color: #ff0055;
text-shadow: 3px 3px #0ff;
margin-bottom: 20px;
font-size: 2.5em;
}
.blink {
animation: blinker 1s linear infinite;
color: #0f0;
margin-top: 30px;
font-size: 0.8em;
}
@keyframes blinker {
50% { opacity: 0; }
}
</style>
</head>
<body>
<div id="game-wrapper">
<canvas id="tetris" width="240" height="400"></canvas>
<div id="ui-panel">
<div class="stat-label">SCORE</div>
<div id="score" class="stat-value">0</div>
<div class="stat-label">LIGNES</div>
<div id="lines" class="stat-value">0</div>
<div class="stat-label">NIVEAU</div>
<div id="level" class="stat-value">1</div>
<div class="controls-section">
<div class="stat-label" style="margin-bottom:15px; color:#fff;">COMMANDES</div>
<div class="control-row"><div class="key-cap">←</div><div class="key-cap">→</div><span>BOUGER</span></div>
<div class="control-row"><div class="key-cap">↑</div><span>PIVOTER</span></div>
<div class="control-row"><div class="key-cap">↓</div><span>VITESSE</span></div>
</div>
</div>
<div class="scanlines"></div>
<div id="overlay">
<h1>TETRIS</h1>
<p style="color:#fff; font-size:0.7em; margin-bottom:10px;">MODE ARCADE</p>
<div class="blink">CLIQUEZ POUR JOUER</div>
</div>
</div>
<script>
const canvas = document.getElementById('tetris');
const context = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const linesElement = document.getElementById('lines');
const levelElement = document.getElementById('level');
const overlay = document.getElementById('overlay');
context.scale(20, 20);
const colors = [
null, '#FF0D72', '#0DC2FF', '#0DFF72', '#F538FF', '#FF8E0D', '#FFE138', '#3877FF',
];
function createPiece(type) {
if (type === 'I') return [[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]];
if (type === 'L') return [[0, 2, 0], [0, 2, 0], [0, 2, 2]];
if (type === 'J') return [[0, 3, 0], [0, 3, 0], [3, 3, 0]];
if (type === 'O') return [[4, 4], [4, 4]];
if (type === 'Z') return [[5, 5, 0], [0, 5, 5], [0, 0, 0]];
if (type === 'S') return [[0, 6, 6], [6, 6, 0], [0, 0, 0]];
if (type === 'T') return [[0, 7, 0], [7, 7, 7], [0, 0, 0]];
}
function createMatrix(w, h) {
const matrix = [];
while (h--) matrix.push(new Array(w).fill(0));
return matrix;
}
function draw() {
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width, canvas.height);
drawMatrix(arena, {x: 0, y: 0});
drawMatrix(player.matrix, player.pos);
}
function drawMatrix(matrix, offset) {
matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
context.fillStyle = colors[value];
context.fillRect(x + offset.x, y + offset.y, 1, 1);
context.lineWidth = 0.05;
context.strokeStyle = 'white';
context.strokeRect(x + offset.x, y + offset.y, 1, 1);
context.fillStyle = 'rgba(0,0,0,0.2)';
context.fillRect(x + offset.x + 0.1, y + offset.y + 0.8, 0.8, 0.1);
context.fillRect(x + offset.x + 0.8, y + offset.y + 0.1, 0.1, 0.8);
}
});
});
}
function merge(arena, player) {
player.matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) arena[y + player.pos.y][x + player.pos.x] = value;
});
});
}
function rotate(matrix, dir) {
for (let y = 0; y < matrix.length; ++y) {
for (let x = 0; x < y; ++x) {
[matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]];
}
}
if (dir > 0) matrix.forEach(row => row.reverse());
else matrix.reverse();
}
function collide(arena, player) {
const [m, o] = [player.matrix, player.pos];
for (let y = 0; y < m.length; ++y) {
for (let x = 0; x < m[y].length; ++x) {
if (m[y][x] !== 0 && (arena[y + o.y] && arena[y + o.y][x + o.x]) !== 0) return true;
}
}
return false;
}
function arenaSweep() {
let rowCount = 1;
outer: for (let y = arena.length - 1; y > 0; --y) {
for (let x = 0; x < arena[y].length; ++x) {
if (arena[y][x] === 0) continue outer;
}
const row = arena.splice(y, 1)[0].fill(0);
arena.unshift(row);
++y;
player.score += rowCount * 10;
player.lines += 1;
rowCount *= 2;
if(player.lines % 10 === 0) {
player.level++;
dropInterval = dropInterval > 100 ? dropInterval - 50 : dropInterval;
}
}
}
function playerReset() {
const piecesStr = 'ILJOTSZ';
player.matrix = createPiece(piecesStr[piecesStr.length * Math.random() | 0]);
player.pos.y = 0;
player.pos.x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0);
if (collide(arena, player)) gameOver();
}
function gameOver() {
isGameOver = true;
overlay.style.display = 'flex';
overlay.innerHTML = `<h1>PERDU</h1><p style="color:#fff; margin-bottom:15px">SCORE FINAL: ${player.score}</p><div class='blink'>CLIQUEZ POUR REJOUER</div>`;
}
function playerDrop() {
player.pos.y++;
if (collide(arena, player)) {
player.pos.y--;
merge(arena, player);
playerReset();
arenaSweep();
updateScore();
}
dropCounter = 0;
}
function playerMove(dir) {
player.pos.x += dir;
if (collide(arena, player)) player.pos.x -= dir;
}
function playerRotate(dir) {
const pos = player.pos.x;
let offset = 1;
rotate(player.matrix, dir);
while (collide(arena, player)) {
player.pos.x += offset;
offset = -(offset + (offset > 0 ? 1 : -1));
if (offset > player.matrix[0].length) {
rotate(player.matrix, -dir);
player.pos.x = pos;
return;
}
}
}
let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;
function update(time = 0) {
if(isPaused || isGameOver) return;
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) playerDrop();
draw();
requestAnimationFrame(update);
}
function updateScore() {
scoreElement.innerText = player.score;
linesElement.innerText = player.lines;
levelElement.innerText = player.level;
}
const arena = createMatrix(12, 20);
const player = { pos: {x: 0, y: 0}, matrix: null, score: 0, lines: 0, level: 1 };
let isPaused = true;
let isGameOver = false;
document.addEventListener('keydown', event => {
if(isPaused || isGameOver) return;
if (event.keyCode === 37) playerMove(-1);
else if (event.keyCode === 39) playerMove(1);
else if (event.keyCode === 40) playerDrop();
else if (event.keyCode === 38) playerRotate(1);
});
overlay.addEventListener('click', () => {
if(isGameOver) {
arena.forEach(row => row.fill(0));
player.score = 0;
player.lines = 0;
player.level = 1;
dropInterval = 1000;
updateScore();
isGameOver = false;
}
if(isPaused || isGameOver === false) {
isPaused = false;
overlay.style.display = 'none';
playerReset();
update();
}
});
</script>
</body>
</html>