parent
b93f098f53
commit
180706052a
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>2048</title>
|
||||
<link rel="stylesheet" href="style_2048.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>2048</h1>
|
||||
<div class="score-container">
|
||||
Score: <span id="score">0</span>
|
||||
</div>
|
||||
<button id="new-game">Nouvelle partie</button>
|
||||
</div>
|
||||
<div class="grid"></div>
|
||||
</div>
|
||||
<script src="script_2048.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
@ -0,0 +1,223 @@
|
||||
class Game2048 {
|
||||
constructor() {
|
||||
this.grid = Array(4).fill().map(() => Array(4).fill(0));
|
||||
this.score = 0;
|
||||
this.gridElement = document.querySelector('.grid');
|
||||
this.scoreElement = document.getElementById('score');
|
||||
this.setupGrid();
|
||||
this.addNumber();
|
||||
this.addNumber();
|
||||
this.setupEventListeners();
|
||||
this.showInstructions(); // Ajout de l'appel à la nouvelle méthode
|
||||
}
|
||||
|
||||
setupGrid() {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const cell = document.createElement('div');
|
||||
cell.classList.add('cell');
|
||||
this.gridElement.appendChild(cell);
|
||||
}
|
||||
this.updateGrid();
|
||||
}
|
||||
|
||||
updateGrid() {
|
||||
const cells = this.gridElement.getElementsByClassName('cell');
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let j = 0; j < 4; j++) {
|
||||
const value = this.grid[i][j];
|
||||
const cell = cells[i * 4 + j];
|
||||
const previousValue = parseInt(cell.getAttribute('data-value')) || 0;
|
||||
|
||||
cell.textContent = value || '';
|
||||
cell.setAttribute('data-value', value);
|
||||
|
||||
// Ajouter les animations
|
||||
if (value && value !== previousValue) {
|
||||
// Animation de fusion si la valeur a doublé
|
||||
if (previousValue && value === previousValue * 2) {
|
||||
cell.classList.add('merge');
|
||||
}
|
||||
// Animation de glissement selon la direction
|
||||
else if (value) {
|
||||
const lastMove = this.lastMoveDirection;
|
||||
if (lastMove) {
|
||||
cell.classList.add(`slide-${lastMove}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retirer les classes d'animation après leur exécution
|
||||
cell.addEventListener('animationend', () => {
|
||||
cell.classList.remove('merge', 'slide-left', 'slide-right', 'slide-up', 'slide-down');
|
||||
});
|
||||
}
|
||||
}
|
||||
this.scoreElement.textContent = this.score;
|
||||
}
|
||||
|
||||
addNumber() {
|
||||
const emptyCells = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let j = 0; j < 4; j++) {
|
||||
if (this.grid[i][j] === 0) {
|
||||
emptyCells.push({ x: i, y: j });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (emptyCells.length) {
|
||||
const { x, y } = emptyCells[Math.floor(Math.random() * emptyCells.length)];
|
||||
this.grid[x][y] = Math.random() < 0.9 ? 2 : 4;
|
||||
}
|
||||
}
|
||||
|
||||
move(direction) {
|
||||
let moved = false;
|
||||
const gridCopy = JSON.parse(JSON.stringify(this.grid));
|
||||
|
||||
// Convertir la direction en format plus simple
|
||||
this.lastMoveDirection = direction.toLowerCase().replace('arrow', '');
|
||||
|
||||
switch (direction) {
|
||||
case 'ArrowLeft':
|
||||
moved = this.moveLeft();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
moved = this.moveRight();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
moved = this.moveUp();
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
moved = this.moveDown();
|
||||
break;
|
||||
}
|
||||
|
||||
if (moved) {
|
||||
this.addNumber();
|
||||
this.updateGrid();
|
||||
if (this.isGameOver()) {
|
||||
alert('Game Over! Score: ' + this.score);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moveRow(row) {
|
||||
let arr = row.filter(x => x !== 0);
|
||||
for (let i = 0; i < arr.length - 1; i++) {
|
||||
if (arr[i] === arr[i + 1]) {
|
||||
arr[i] *= 2;
|
||||
this.score += arr[i];
|
||||
arr.splice(i + 1, 1);
|
||||
}
|
||||
}
|
||||
while (arr.length < 4) arr.push(0);
|
||||
return arr;
|
||||
}
|
||||
|
||||
moveLeft() {
|
||||
let moved = false;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const oldRow = [...this.grid[i]];
|
||||
const newRow = this.moveRow([...oldRow]);
|
||||
this.grid[i] = newRow;
|
||||
if (JSON.stringify(oldRow) !== JSON.stringify(newRow)) moved = true;
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
moveRight() {
|
||||
let moved = false;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const oldRow = [...this.grid[i]];
|
||||
const newRow = this.moveRow([...oldRow].reverse()).reverse();
|
||||
this.grid[i] = newRow;
|
||||
if (JSON.stringify(oldRow) !== JSON.stringify(newRow)) moved = true;
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
moveUp() {
|
||||
let moved = false;
|
||||
for (let j = 0; j < 4; j++) {
|
||||
const oldColumn = this.grid.map(row => row[j]);
|
||||
const newColumn = this.moveRow([...oldColumn]);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (this.grid[i][j] !== newColumn[i]) {
|
||||
moved = true;
|
||||
this.grid[i][j] = newColumn[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
moveDown() {
|
||||
let moved = false;
|
||||
for (let j = 0; j < 4; j++) {
|
||||
const oldColumn = this.grid.map(row => row[j]);
|
||||
const newColumn = this.moveRow([...oldColumn].reverse()).reverse();
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (this.grid[i][j] !== newColumn[i]) {
|
||||
moved = true;
|
||||
this.grid[i][j] = newColumn[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
isGameOver() {
|
||||
// Check for empty cells
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let j = 0; j < 4; j++) {
|
||||
if (this.grid[i][j] === 0) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for possible merges
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let j = 0; j < 4; j++) {
|
||||
const current = this.grid[i][j];
|
||||
if ((j < 3 && current === this.grid[i][j + 1]) ||
|
||||
(i < 3 && current === this.grid[i + 1][j])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
this.move(e.key);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('new-game').addEventListener('click', () => {
|
||||
this.grid = Array(4).fill().map(() => Array(4).fill(0));
|
||||
this.score = 0;
|
||||
this.addNumber();
|
||||
this.addNumber();
|
||||
this.updateGrid();
|
||||
});
|
||||
}
|
||||
|
||||
showInstructions() {
|
||||
const message = document.createElement('div');
|
||||
message.className = 'instruction-message';
|
||||
message.textContent = '↑ ↓ ← → Utilisez les flèches du clavier pour jouer';
|
||||
document.body.appendChild(message);
|
||||
|
||||
// Supprime le message après 60 secondes
|
||||
setTimeout(() => {
|
||||
message.remove();
|
||||
}, 60000);
|
||||
}
|
||||
}
|
||||
|
||||
// Start the game when the page loads
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new Game2048();
|
||||
});
|
@ -0,0 +1,238 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #faf8ef;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 460px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 48px;
|
||||
color: #776e65;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.score-container {
|
||||
display: inline-block;
|
||||
background: #bbada0;
|
||||
padding: 10px 20px;
|
||||
border-radius: 3px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#new-game {
|
||||
background: #8f7a66;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.grid {
|
||||
background: #bbada0;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 100px);
|
||||
/* Définition explicite de la largeur */
|
||||
grid-gap: 15px;
|
||||
position: relative;
|
||||
box-shadow: 0 0 0 2px #bbada0;
|
||||
/* Ajout d'une bordure sous forme d'ombre */
|
||||
margin: 0 auto;
|
||||
/* Centrage horizontal */
|
||||
width: fit-content;
|
||||
/* Ajustement automatique de la largeur */
|
||||
}
|
||||
|
||||
.cell {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: rgba(238, 228, 218, 0.35);
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
color: #776e65;
|
||||
transition: all 0.15s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Animations de déplacement */
|
||||
.cell.merge {
|
||||
animation: merge 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.cell.slide-left {
|
||||
animation: slideLeft 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.cell.slide-right {
|
||||
animation: slideRight 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.cell.slide-up {
|
||||
animation: slideUp 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.cell.slide-down {
|
||||
animation: slideDown 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes merge {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideLeft {
|
||||
0% {
|
||||
transform: translateX(100px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
0% {
|
||||
transform: translateX(-100px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
transform: translateY(100px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
0% {
|
||||
transform: translateY(-100px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.cell[data-value="2"] {
|
||||
background: #eee4da;
|
||||
}
|
||||
|
||||
.cell[data-value="4"] {
|
||||
background: #ede0c8;
|
||||
}
|
||||
|
||||
.cell[data-value="8"] {
|
||||
background: #f2b179;
|
||||
color: #f9f6f2;
|
||||
}
|
||||
|
||||
.cell[data-value="16"] {
|
||||
background: #f59563;
|
||||
color: #f9f6f2;
|
||||
}
|
||||
|
||||
.cell[data-value="32"] {
|
||||
background: #f67c5f;
|
||||
color: #f9f6f2;
|
||||
}
|
||||
|
||||
.cell[data-value="64"] {
|
||||
background: #f65e3b;
|
||||
color: #f9f6f2;
|
||||
}
|
||||
|
||||
.cell[data-value="128"] {
|
||||
background: #edcf72;
|
||||
color: #f9f6f2;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.cell[data-value="256"] {
|
||||
background: #edcc61;
|
||||
color: #f9f6f2;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.cell[data-value="512"] {
|
||||
background: #edc850;
|
||||
color: #f9f6f2;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.cell[data-value="1024"] {
|
||||
background: #edc53f;
|
||||
color: #f9f6f2;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.cell[data-value="2048"] {
|
||||
background: #edc22e;
|
||||
color: #f9f6f2;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.instruction-message {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: rgba(119, 110, 101, 0.9);
|
||||
color: white;
|
||||
padding: 15px 25px;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
animation: fadeOut 1s ease-in 59s forwards;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue