443 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			443 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html>
 | |
| <html>
 | |
| <head>
 | |
|     <meta charset="UTF-8">
 | |
|     <title>2048</title>
 | |
|     <meta property="og:title" content="2048">
 | |
|     <meta property="og:description" content="Spiele das klassische 2048-Puzzle-Spiel">
 | |
|     <meta property="og:type" content="website">
 | |
|     <meta property="og:url" content="https://tools.ponywave.de/2048">
 | |
|     <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> |