439 lines
15 KiB
HTML
439 lines
15 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>2048</title>
|
|
<script defer src="https://stats.ponywave.de/script" data-website-id="9ef713d2-adb9-4906-9df5-708d8a8b9131" data-tag="2048"></script>
|
|
<style>
|
|
body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
font-family: Arial, sans-serif;
|
|
background-color: #faf8ef;
|
|
}
|
|
|
|
#grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 10px;
|
|
background-color: #bbada0;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.cell {
|
|
width: 80px;
|
|
height: 80px;
|
|
background-color: rgba(238, 228, 218, 0.35);
|
|
border-radius: 3px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
transition: all 0.15s ease;
|
|
}
|
|
|
|
@keyframes newTile {
|
|
from { transform: scale(0); }
|
|
to { transform: scale(1); }
|
|
}
|
|
|
|
@keyframes mergedTile {
|
|
0% { transform: scale(1); }
|
|
50% { transform: scale(1.2); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
.new-tile {
|
|
animation: newTile 0.15s ease-out;
|
|
}
|
|
|
|
.merged-tile {
|
|
animation: mergedTile 0.15s ease-out;
|
|
}
|
|
|
|
.number {
|
|
background-color: #eee4da;
|
|
color: #776e65;
|
|
}
|
|
|
|
.number-2 { background-color: #eee4da; }
|
|
.number-4 { background-color: #ede0c8; }
|
|
.number-8 { background-color: #f2b179; color: white; }
|
|
.number-16 { background-color: #f59563; color: white; }
|
|
.number-32 { background-color: #f67c5f; color: white; }
|
|
.number-64 { background-color: #f65e3b; color: white; }
|
|
.number-128 { background-color: #edcf72; color: white; }
|
|
.number-256 { background-color: #edcc61; color: white; }
|
|
.number-512 { background-color: #edc850; color: white; }
|
|
.number-1024 { background-color: #edc53f; color: white; }
|
|
.number-2048 { background-color: #edc22e; color: white; }
|
|
|
|
#score {
|
|
font-size: 24px;
|
|
margin: 10px 0;
|
|
}
|
|
|
|
button {
|
|
padding: 10px 20px;
|
|
margin: 10px;
|
|
background-color: #8f7a66;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
#anleitung {
|
|
background-color: #bbada0;
|
|
padding: 20px;
|
|
border-radius: 5px;
|
|
margin: 20px;
|
|
max-width: 500px;
|
|
color: white;
|
|
}
|
|
|
|
/* Game Over Popup */
|
|
.game-over-popup {
|
|
display: none;
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background: #bbada0;
|
|
padding: 2rem;
|
|
border-radius: 10px;
|
|
text-align: center;
|
|
z-index: 100;
|
|
box-shadow: 0 0 20px rgba(0,0,0,0.3);
|
|
color: white;
|
|
}
|
|
|
|
.overlay {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0,0,0,0.5);
|
|
z-index: 99;
|
|
}
|
|
|
|
/* Highscore Anzeige */
|
|
#highscore {
|
|
font-size: 20px;
|
|
margin-bottom: 10px;
|
|
color: #776e65;
|
|
}
|
|
</style>
|
|
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<h1>2048</h1>
|
|
<div id="score">Punktestand: 0</div>
|
|
<div id="highscore">Highscore: 0</div>
|
|
|
|
<div id="grid"></div>
|
|
|
|
<div id="anleitung">
|
|
<h2>Spielanleitung</h2>
|
|
<p>🎮 <strong>Spielziel:</strong> Kombiniere Zahlenkacheln, um die 2048-Kachel zu erreichen!</p>
|
|
<p>🕹️ <strong>Steuerung:</strong> Benutze die Pfeiltasten:</p>
|
|
<ul>
|
|
<li>← →: Kacheln nach links/rechts bewegen</li>
|
|
<li>↑ ↓: Kacheln nach oben/unten bewegen</li>
|
|
</ul>
|
|
<p>⚡ <strong>Spielmechanik:</strong></p>
|
|
<ul>
|
|
<li>Gleiche Zahlen verschmelzen bei Kollision</li>
|
|
<li>Jeder Zug generiert eine neue 2er- oder 4er-Kachel</li>
|
|
<li>Spielende wenn keine Züge mehr möglich</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<button onclick="startNewGame()">Neues Spiel</button>
|
|
<button onclick="takeScreenshot()">Screenshot machen 📸</button>
|
|
|
|
<div class="game-over-popup" id="gameOverPopup">
|
|
<h2>Game Over!</h2>
|
|
<p>Dein Punktestand: <span id="finalScore">0</span></p>
|
|
<button onclick="startNewGame()">Neues Spiel</button>
|
|
</div>
|
|
<div class="overlay" id="overlay"></div>
|
|
|
|
<script>
|
|
class Game2048 {
|
|
constructor() {
|
|
this.grid = Array(4).fill().map(() => Array(4).fill(0));
|
|
this.previousGrid = JSON.parse(JSON.stringify(this.grid));
|
|
this.score = 0;
|
|
this.highscore = localStorage.getItem('2048-highscore') || 0;
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.updateHighscoreDisplay();
|
|
this.addNewNumber();
|
|
this.addNewNumber();
|
|
this.updateDisplay();
|
|
this.setupControls();
|
|
}
|
|
|
|
addNewNumber() {
|
|
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 > 0) {
|
|
const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
|
|
this.grid[randomCell.x][randomCell.y] = Math.random() < 0.9 ? 2 : 4;
|
|
}
|
|
}
|
|
|
|
updateDisplay() {
|
|
const gridContainer = document.getElementById('grid');
|
|
gridContainer.innerHTML = '';
|
|
|
|
for (let i = 0; i < 4; i++) {
|
|
for (let j = 0; j < 4; j++) {
|
|
const cell = document.createElement('div');
|
|
cell.className = 'cell';
|
|
if (this.grid[i][j] !== 0) {
|
|
cell.classList.add('number', `number-${this.grid[i][j]}`);
|
|
cell.textContent = this.grid[i][j];
|
|
}
|
|
gridContainer.appendChild(cell);
|
|
}
|
|
}
|
|
|
|
document.getElementById('score').textContent = `Score: ${this.score}`;
|
|
}
|
|
|
|
showGameOver() {
|
|
document.getElementById('gameOverPopup').style.display = 'block';
|
|
document.getElementById('overlay').style.display = 'block';
|
|
document.getElementById('finalScore').textContent = this.score;
|
|
}
|
|
|
|
updateHighscore() {
|
|
if (this.score > this.highscore) {
|
|
this.highscore = this.score;
|
|
localStorage.setItem('2048-highscore', this.highscore);
|
|
this.updateHighscoreDisplay();
|
|
}
|
|
}
|
|
|
|
updateHighscoreDisplay() {
|
|
document.getElementById('highscore').textContent = `Highscore: ${this.highscore}`;
|
|
}
|
|
|
|
resetGame() {
|
|
this.grid = Array(4).fill().map(() => Array(4).fill(0));
|
|
this.score = 0;
|
|
this.previousGrid = JSON.parse(JSON.stringify(this.grid));
|
|
document.getElementById('gameOverPopup').style.display = 'none';
|
|
document.getElementById('overlay').style.display = 'none';
|
|
this.init();
|
|
}
|
|
|
|
move(direction) {
|
|
let moved = false;
|
|
this.previousGrid = JSON.parse(JSON.stringify(this.grid));
|
|
|
|
switch(direction) {
|
|
case 'ArrowUp': moved = this.moveUp(); break;
|
|
case 'ArrowDown': moved = this.moveDown(); break;
|
|
case 'ArrowLeft': moved = this.moveLeft(); break;
|
|
case 'ArrowRight': moved = this.moveRight(); break;
|
|
}
|
|
|
|
if (moved) {
|
|
this.addNewNumber();
|
|
this.updateDisplay();
|
|
this.updateHighscore();
|
|
|
|
if (this.isGameOver()) {
|
|
this.showGameOver();
|
|
}
|
|
}
|
|
}
|
|
|
|
updateDisplay() {
|
|
const gridContainer = document.getElementById('grid');
|
|
gridContainer.innerHTML = '';
|
|
|
|
for (let i = 0; i < 4; i++) {
|
|
for (let j = 0; j < 4; j++) {
|
|
const cell = document.createElement('div');
|
|
cell.className = 'cell';
|
|
|
|
if (this.grid[i][j] !== 0) {
|
|
const prevVal = this.previousGrid[i][j];
|
|
const currentVal = this.grid[i][j];
|
|
|
|
cell.classList.add('number', `number-${currentVal}`);
|
|
cell.textContent = currentVal;
|
|
|
|
// Animation-Klassen hinzufügen
|
|
if (prevVal === 0) {
|
|
cell.classList.add('new-tile');
|
|
} else if (currentVal > prevVal) {
|
|
cell.classList.add('merged-tile');
|
|
}
|
|
}
|
|
gridContainer.appendChild(cell);
|
|
}
|
|
}
|
|
|
|
document.getElementById('score').textContent = `Punktestand: ${this.score}`;
|
|
}
|
|
|
|
moveLeft() {
|
|
let moved = false;
|
|
for (let i = 0; i < 4; i++) {
|
|
let row = this.grid[i].filter(x => x !== 0);
|
|
for (let j = 0; j < row.length - 1; j++) {
|
|
if (row[j] === row[j + 1]) {
|
|
row[j] *= 2;
|
|
this.score += row[j];
|
|
row.splice(j + 1, 1);
|
|
moved = true;
|
|
}
|
|
}
|
|
const newRow = row.concat(Array(4 - row.length).fill(0));
|
|
if (JSON.stringify(this.grid[i]) !== JSON.stringify(newRow)) moved = true;
|
|
this.grid[i] = newRow;
|
|
}
|
|
return moved;
|
|
}
|
|
|
|
moveRight() {
|
|
let moved = false;
|
|
for (let i = 0; i < 4; i++) {
|
|
let row = this.grid[i].filter(x => x !== 0);
|
|
for (let j = row.length - 1; j > 0; j--) {
|
|
if (row[j] === row[j - 1]) {
|
|
row[j] *= 2;
|
|
this.score += row[j];
|
|
row.splice(j - 1, 1);
|
|
moved = true;
|
|
}
|
|
}
|
|
const newRow = Array(4 - row.length).fill(0).concat(row);
|
|
if (JSON.stringify(this.grid[i]) !== JSON.stringify(newRow)) moved = true;
|
|
this.grid[i] = newRow;
|
|
}
|
|
return moved;
|
|
}
|
|
|
|
moveUp() {
|
|
let moved = false;
|
|
for (let j = 0; j < 4; j++) {
|
|
let col = this.grid.map(row => row[j]).filter(x => x !== 0);
|
|
for (let i = 0; i < col.length - 1; i++) {
|
|
if (col[i] === col[i + 1]) {
|
|
col[i] *= 2;
|
|
this.score += col[i];
|
|
col.splice(i + 1, 1);
|
|
moved = true;
|
|
}
|
|
}
|
|
const newCol = col.concat(Array(4 - col.length).fill(0));
|
|
if (JSON.stringify(this.grid.map(row => row[j])) !== JSON.stringify(newCol)) moved = true;
|
|
for (let i = 0; i < 4; i++) {
|
|
this.grid[i][j] = newCol[i];
|
|
}
|
|
}
|
|
return moved;
|
|
}
|
|
|
|
moveDown() {
|
|
let moved = false;
|
|
for (let j = 0; j < 4; j++) {
|
|
let col = this.grid.map(row => row[j]).filter(x => x !== 0);
|
|
for (let i = col.length - 1; i > 0; i--) {
|
|
if (col[i] === col[i - 1]) {
|
|
col[i] *= 2;
|
|
this.score += col[i];
|
|
col.splice(i - 1, 1);
|
|
moved = true;
|
|
}
|
|
}
|
|
const newCol = Array(4 - col.length).fill(0).concat(col);
|
|
if (JSON.stringify(this.grid.map(row => row[j])) !== JSON.stringify(newCol)) moved = true;
|
|
for (let i = 0; i < 4; i++) {
|
|
this.grid[i][j] = newCol[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 possible merges
|
|
for (let i = 0; i < 4; i++) {
|
|
for (let j = 0; j < 4; j++) {
|
|
const current = this.grid[i][j];
|
|
if ((i < 3 && current === this.grid[i + 1][j]) ||
|
|
(j < 3 && current === this.grid[i][j + 1])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
setupControls() {
|
|
document.addEventListener('keydown', (e) => {
|
|
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
|
|
e.preventDefault();
|
|
this.move(e.key);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function startNewGame() {
|
|
game.resetGame();
|
|
}
|
|
|
|
// Screenshot-Funktion
|
|
function takeScreenshot() {
|
|
const gameArea = document.body;
|
|
|
|
html2canvas(gameArea, {
|
|
ignoreElements: (element) => element.tagName === 'BUTTON',
|
|
backgroundColor: '#faf8ef'
|
|
}).then(canvas => {
|
|
const link = document.createElement('a');
|
|
link.download = '2048-screenshot.png';
|
|
link.href = canvas.toDataURL();
|
|
link.click();
|
|
}).catch(err => {
|
|
console.error('Fehler beim Erstellen des Screenshots:', err);
|
|
alert('Screenshot konnte nicht erstellt werden!');
|
|
});
|
|
}
|
|
|
|
// Spiel starten
|
|
new Game2048();
|
|
let game = new Game2048();
|
|
</script>
|
|
</body>
|
|
</html> |