From 7fda24a6ccfb52c04f47800fba99dc18ed5b9a28 Mon Sep 17 00:00:00 2001 From: bitsearch Date: Sun, 1 Mar 2026 16:25:19 +0100 Subject: [PATCH] retrait du bouton restart --- Transcendence/srcs/frontend/src/tetris.css | 8 +- Transcendence/srcs/frontend/src/tetris.html | 1 - Transcendence/srcs/frontend/src/tetris.js | 7 +- .../srcs/frontend/src/tetris.js.backup | 401 ++++++++++++++++++ Transcendence/srcs/frontend/src/ui.js | 12 +- 5 files changed, 421 insertions(+), 8 deletions(-) create mode 100644 Transcendence/srcs/frontend/src/tetris.js.backup diff --git a/Transcendence/srcs/frontend/src/tetris.css b/Transcendence/srcs/frontend/src/tetris.css index 9c37b1e..71c5182 100644 --- a/Transcendence/srcs/frontend/src/tetris.css +++ b/Transcendence/srcs/frontend/src/tetris.css @@ -178,14 +178,14 @@ button { } #btn-restart { - color: var(--accent); - border-color: var(--accent); + color: var(--accent2); + border-color: var(--accent2); } #btn-restart:hover:not(:disabled){ - background: var(--accent); + background: var(--accent2); color: var(--bg); - box-shadow: 0 0 15px var(--accent); + box-shadow: 0 0 15px var(--accent2); } #btn-pause { diff --git a/Transcendence/srcs/frontend/src/tetris.html b/Transcendence/srcs/frontend/src/tetris.html index 1263ea6..25fcda6 100644 --- a/Transcendence/srcs/frontend/src/tetris.html +++ b/Transcendence/srcs/frontend/src/tetris.html @@ -41,7 +41,6 @@
-
diff --git a/Transcendence/srcs/frontend/src/tetris.js b/Transcendence/srcs/frontend/src/tetris.js index e5b74e0..cf6962b 100644 --- a/Transcendence/srcs/frontend/src/tetris.js +++ b/Transcendence/srcs/frontend/src/tetris.js @@ -1,6 +1,6 @@ // ───────────────────────────────────────────── // LOGIQUE TETRIS -// ───────────────────────────────────────────── +// ─────────────────────────────────────────── class Tetris { constructor(onRender, onGameOver, onBlockPlaced = null, onLinesCleared = null) { @@ -72,6 +72,11 @@ class Tetris { document.removeEventListener('keydown', this._keyHandler); } + restart() { + this.stop(); + this.start(); + } + pause() { if (!this.isRunning) return; this.isPaused = !this.isPaused; diff --git a/Transcendence/srcs/frontend/src/tetris.js.backup b/Transcendence/srcs/frontend/src/tetris.js.backup new file mode 100644 index 0000000..e3f6467 --- /dev/null +++ b/Transcendence/srcs/frontend/src/tetris.js.backup @@ -0,0 +1,401 @@ +// ───────────────────────────────────────────── +// LOGIQUE TETRIS +// ───────────────────────────────────────────── + +class Tetris { + constructor(onRender, onGameOver, onBlockPlaced = null, onLinesCleared = null) { + this.onRender = onRender; + this.onGameOver = onGameOver; + this.onBlockPlaced = onBlockPlaced; + this.onLinesCleared = onLinesCleared; + + this.grid = this._createGrid(10, 20); + this.bufferGrid = this._createGrid(10, 5); + this.currentPiece = null; + this.storedPiece = null; + this.nextPiece = null; + + this.score = 0; + this.initialTimeToDown = 1000; + this.timeToDown = 1000; + this.hardening = 1000; + this.count = 0; + this.decrementTTD = 100; + + this.lastLandingCol = 4; + + this.isRunning = false; + this.isPaused = false; + this.canStore = true; + + this.animationFrameId = null; + this.lastTime = 0; + this.accumulator = 0; + + this._keyHandler = this._handleKey.bind(this); + } + + configure({ timeToDown, hardening, decrementTTD }) { + if (timeToDown !== undefined) this.initialTimeToDown = this.timeToDown = timeToDown; + if (hardening !== undefined) this.hardening = hardening; + if (decrementTTD !== undefined) this.decrementTTD = decrementTTD; + } + + _createGrid(w, h) { + return Array.from({ length: h }, () => Array(w).fill(0)); + } + + start() { + if (this.isRunning) return; + this.isRunning = true; + this.isPaused = false; + this.grid = this._createGrid(10, 20); + this.score = 0; + this.count = 0; + this.timeToDown = this.initialTimeToDown; + this.storedPiece = null; + this.canStore = true; + this._spawnNewPiece(); + document.addEventListener('keydown', this._keyHandler); + this._startGameLoop(); + } + + stop() { + this.isRunning = false; + this.isPaused = false; + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + this.accumulator = 0; + this.lastTime = 0; + document.removeEventListener('keydown', this._keyHandler); + } + + pause() { + if (!this.isRunning) return; + this.isPaused = !this.isPaused; + if (!this.isPaused) { + this.lastTime = 0; + this._startGameLoop(); + } + } + + _startGameLoop() { + this.lastTime = 0; + this.accumulator = 0; + + const gameLoop = (currentTime) => { + if (!this.isRunning) return; + + if (this.isPaused) { + this.animationFrameId = requestAnimationFrame(gameLoop); + return; + } + + if (this.lastTime === 0) { + this.lastTime = currentTime; + this.animationFrameId = requestAnimationFrame(gameLoop); + return; + } + + const deltaTime = currentTime - this.lastTime; + this.lastTime = currentTime; + this.accumulator += deltaTime; + + while (this.isRunning && this.accumulator >= this.timeToDown) { + this._tick(); + this.accumulator -= this.timeToDown; + if (this.accumulator > this.timeToDown * 3) { + this.accumulator = 0; + break; + } + } + + this.onRender(); + this.animationFrameId = requestAnimationFrame(gameLoop); + }; + + this.animationFrameId = requestAnimationFrame(gameLoop); + } + + _tick() { + if (!this.currentPiece) return; + if (this._canMoveDown()) { + this.currentPiece.moveDown(); + } else { + this._lockPiece(); + this.verifierLignes(); + this._makeHarder(); + this._spawnNewPiece(); + this.canStore = true; + if (!this._canSpawn()) this._gameOver(true); + } + } + + _handleKey(e) { + if (!this.isRunning || !this.currentPiece) return; + + switch (e.key) { + case 'ArrowLeft': + e.preventDefault(); + if (!this.isPaused && this._canMoveLeft()) this.currentPiece.moveLeft(); + break; + case 'ArrowRight': + e.preventDefault(); + if (!this.isPaused && this._canMoveRight()) this.currentPiece.moveRight(); + break; + case 'ArrowDown': + e.preventDefault(); + if (!this.isPaused && this._canMoveDown()) { + this.currentPiece.moveDown(); + this.score += 1; + this.accumulator = 0; + } + break; + case ' ': + e.preventDefault(); + if (!this.isPaused) this._hardDrop(); + break; + case 'q': case 'Q': + e.preventDefault(); + if (!this.isPaused) this._rotatePiece(-1); + break; + case 'w': case 'W': + e.preventDefault(); + if (!this.isPaused) this._rotatePiece(1); + break; + case 'c': case 'C': + e.preventDefault(); + if (!this.isPaused) this._storePiece(); + break; + } + + this.onRender(); + } + + _hardDrop() { + if (!this.currentPiece) return; + let dist = 0; + while (this._canMoveDown()) { this.currentPiece.moveDown(); dist++; } + this.score += dist * 2; + this._lockPiece(); + this.verifierLignes(); + this._makeHarder(); + this._spawnNewPiece(); + this.canStore = true; + this.accumulator = 0; + if (!this._canSpawn()) this._gameOver(true); + } + + _rotatePiece(direction) { + if (!this.currentPiece) return; + const originalPos = { ...this.currentPiece.getPosition() }; + + if (direction === -1) this.currentPiece.rotateLeft(); + else this.currentPiece.rotateRight(); + + if (!this._isValidPosition()) { + this.currentPiece.moveRight(); + if (this._isValidPosition()) return; + + this.currentPiece.moveLeft(); + this.currentPiece.moveLeft(); + if (this._isValidPosition()) return; + + this.currentPiece.moveLeft(); + if (this._isValidPosition()) return; + + this.currentPiece.moveRight(); + this.currentPiece.moveRight(); + this.currentPiece.position.y--; + if (this._isValidPosition()) return; + + this.currentPiece.position.y = originalPos.y; + this.currentPiece.position.x = originalPos.x; + if (direction === -1) this.currentPiece.rotateRight(); + else this.currentPiece.rotateLeft(); + } + } + + _storePiece() { + if (!this.canStore || !this.currentPiece) return; + + if (this.storedPiece === null) { + this.storedPiece = this.currentPiece; + this._spawnNewPiece(); + } else { + const temp = this.storedPiece; + this.storedPiece = this.currentPiece; + this.currentPiece = temp; + this.currentPiece.position.x = 3; + this.currentPiece.position.y = 0; + } + this.canStore = false; + this.accumulator = 0; + } + + _spawnNewPiece() { + this.currentPiece = this.nextPiece || this._createRandomPiece(); + this.nextPiece = this._createRandomPiece(); + this._updateBufferGrid(); + } + + _createRandomPiece() { + const types = [PieceT, PieceL, PieceReverseL, PieceI, PieceZ, PieceReverseZ, PieceO]; + return new types[Math.floor(Math.random() * types.length)](3, 0); + } + + _updateBufferGrid() { + this.bufferGrid = this._createGrid(10, 5); + if (!this.nextPiece) return; + const shape = this.nextPiece.getShape(); + const offsetX = Math.floor((10 - shape[0].length) / 2); + for (let y = 0; y < shape.length; y++) + for (let x = 0; x < shape[y].length; x++) + if (shape[y][x] !== 0) + this.bufferGrid[y + 1][x + offsetX] = this.nextPiece.getColor(); + } + + verifierLignes() { + let cleared = 0; + for (let y = this.grid.length - 1; y >= 0; y--) { + if (this.grid[y].every(c => c !== 0)) { + this.grid.splice(y, 1); + this.grid.unshift(Array(10).fill(0)); + cleared++; + y++; + } + } + const points = [0, 100, 300, 500, 800]; + this.score += points[cleared]; + this.count += points[cleared]; + if (this.onLinesCleared && cleared > 0) + this.onLinesCleared(cleared, this.lastLandingCol); + } + + _makeHarder() { + if (this.count >= this.hardening) { + this.count = 0; + this.timeToDown = Math.max(100, this.timeToDown - this.decrementTTD); + } + } + + _canMoveDown() { + if (!this.currentPiece) return false; + const { x, y } = this.currentPiece.getPosition(); + const shape = this.currentPiece.getShape(); + for (let row = 0; row < shape.length; row++) + for (let col = 0; col < shape[row].length; col++) + if (shape[row][col] !== 0) { + const ny = y + row + 1; + const nx = x + col; + if (ny < 0) continue; // encore au-dessus de la grille + if (ny >= this.grid.length || this.grid[ny][nx] !== 0) return false; + } + return true; + } + + _canMoveLeft() { + if (!this.currentPiece) return false; + const { x, y } = this.currentPiece.getPosition(); + const shape = this.currentPiece.getShape(); + for (let row = 0; row < shape.length; row++) + for (let col = 0; col < shape[row].length; col++) + if (shape[row][col] !== 0) { + if (y + row < 0) continue; // au-dessus de la grille + const nx = x + col - 1; + if (nx < 0 || this.grid[y + row][nx] !== 0) return false; + } + return true; + } + + _canMoveRight() { + if (!this.currentPiece) return false; + const { x, y } = this.currentPiece.getPosition(); + const shape = this.currentPiece.getShape(); + for (let row = 0; row < shape.length; row++) + for (let col = 0; col < shape[row].length; col++) + if (shape[row][col] !== 0) { + if (y + row < 0) continue; // au-dessus de la grille + const nx = x + col + 1; + if (nx >= this.grid[0].length || this.grid[y + row][nx] !== 0) return false; + } + return true; + } + + _isValidPosition() { + if (!this.currentPiece) return false; + const { x, y } = this.currentPiece.getPosition(); + const shape = this.currentPiece.getShape(); + for (let row = 0; row < shape.length; row++) + for (let col = 0; col < shape[row].length; col++) + if (shape[row][col] !== 0) { + const gx = x + col; + const gy = y + row; + if (gx < 0 || gx >= this.grid[0].length || + gy < 0 || gy >= this.grid.length || + this.grid[gy][gx] !== 0) return false; + } + return true; + } + + _canSpawn() { return this._isValidPosition(); } + + _lockPiece() { + if (!this.currentPiece) return; + const { x, y } = this.currentPiece.getPosition(); + const shape = this.currentPiece.getShape(); + const color = this.currentPiece.getColor(); + for (let row = 0; row < shape.length; row++) + for (let col = 0; col < shape[row].length; col++) + if (shape[row][col] !== 0 && y + row >= 0) + this.grid[y + row][x + col] = color; + this.lastLandingCol = x + Math.floor(shape[0].length / 2); + if (this.onBlockPlaced) this.onBlockPlaced(this.grid.map(r => [...r])); + } + + addGarbageLines(lines) { + if (!this.isRunning || !lines.length) return; + this.grid.splice(0, lines.length); + for (const line of lines) this.grid.push([...line]); // ...line pour faire une copie independante + // La grille a remonté de lines.length lignes — on remonte la pièce du même décalage + // pour qu'elle reste dans la même position relative aux blocs verrouillés. + if (this.currentPiece) { + this.currentPiece.position.y -= lines.length; + } + if (this.grid[0].some(c => c !== 0)) { this._gameOver(false); return; } + if (!this._isValidPositionAllowTop()) this._gameOver(false); + } + + // Comme _isValidPosition mais tolère gy < 0 (zone tampon au-dessus de la grille après garbage) + _isValidPositionAllowTop() { + if (!this.currentPiece) return true; + const { x, y } = this.currentPiece.getPosition(); + const shape = this.currentPiece.getShape(); + for (let row = 0; row < shape.length; row++) + for (let col = 0; col < shape[row].length; col++) + if (shape[row][col] !== 0) { + const gy = y + row; + const gx = x + col; + if (gy < 0) continue; // au-dessus de la grille : OK + if (gx < 0 || gx >= this.grid[0].length || + gy >= this.grid.length || + this.grid[gy][gx] !== 0) return false; + } + return true; + } + + _gameOver(validBlock = false) { + } +} + _gameOver(validBlock = false) { + this.stop(); + this.onGameOver(this.score, validBlock); + } + + restart() { + this.stop(); + this.start(); + } +} diff --git a/Transcendence/srcs/frontend/src/ui.js b/Transcendence/srcs/frontend/src/ui.js index aed5f9c..47f9f4a 100644 --- a/Transcendence/srcs/frontend/src/ui.js +++ b/Transcendence/srcs/frontend/src/ui.js @@ -168,5 +168,13 @@ inputTTD.addEventListener('change', applySettings); inputHardening.addEventListener('change', applySettings); inputDecrement.addEventListener('change', applySettings); -render(); -updateButtons(); +btnRestart.addEventListener('click', () => { + if (duel && duel.isReady) { + // In duel mode, we don't restart from client side - let server handle it + return; + } else { + game.restart(); + updateButtons(); + render(); + } +});