body {
background-color: var(--bg-color);
font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
padding: 20px;
color: #776e65;
user-select: none;
}         h1 {
font-size: 48px;
margin: 0;
font-weight: bold;
}         .header {
width: 100%;
max-width: 500px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}         .stats-container {
display: flex;
gap: 10px;
}         .score-box {
background: #bbada0;
padding: 5px 15px;
border-radius: 3px;
color: white;
text-align: center;
min-width: 60px;
}         .score-box .label {
font-size: 10px;
text-transform: uppercase;
color: #eee4da;
}         .score-box .value {
font-size: 20px;
font-weight: bold;
}         .gold-box {
background: #f1c40f;
color: #fff;
}
.gold-box .label {
color: #fff8db;
}         /* 商店面板 */
.shop-container {
width: 100%;
max-width: 500px;
background: #e5ded4;
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
box-sizing: border-box;
}         .shop-title {
font-weight: bold;
margin-bottom: 10px;
font-size: 18px;
}         .shop-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}         .shop-btn {
background: #8f7a66;
color: white;
border: none;
padding: 10px;
border-radius: 5px;
font-weight: bold;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}         .shop-btn:hover {
background: #7f6a56;
}         .shop-btn:disabled {
background: #d6cdc4;
cursor: not-allowed;
color: #f4f1ea;
}         /* 遊戲棋盤 */
.game-container {
position: relative;
background: var(--grid-bg);
border-radius: 6px;
padding: 15px;
box-sizing: border-box;
touch-action: none;
}         .grid-container {
display: grid;
gap: 15px;
height: 100%;
}         .grid-cell {
background: var(--cell-bg);
border-radius: 3px;
}         /* 方塊樣式 */
.tile {
position: absolute;
border-radius: 3px;
font-weight: bold;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.1s ease-in-out;
animation: appear 0.2s ease-in-out;
box-sizing: border-box;
}         @keyframes appear {
0% { opacity: 0; transform: scale(0); }
100% { opacity: 1; transform: scale(1); }
}         /* 2048 經典色調 */
.tile-2 { background: #eee4da; color: #776e65; font-size: 40px; }
.tile-4 { background: #ede0c8; color: #776e65; font-size: 40px; }
.tile-8 { background: #f2b179; color: #f9f6f2; font-size: 40px; }
.tile-16 { background: #f59563; color: #f9f6f2; font-size: 35px; }
.tile-32 { background: #f67c5f; color: #f9f6f2; font-size: 35px; }
.tile-64 { background: #f65e3b; color: #f9f6f2; font-size: 35px; }
.tile-128 { background: #edcf72; color: #f9f6f2; font-size: 30px; box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.23); }
.tile-256 { background: #edcc61; color: #f9f6f2; font-size: 30px; box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.31); }
.tile-512 { background: #edc850; color: #f9f6f2; font-size: 30px; box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.39); }
.tile-1024 { background: #edc53f; color: #f9f6f2; font-size: 25px; box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.47); }
.tile-2048 { background: #edc22e; color: #f9f6f2; font-size: 25px; box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55); }
.tile-super { background: #3c3a32; color: #f9f6f2; font-size: 20px; }         .game-over {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(238, 228, 218, 0.73);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 6px;
z-index: 10;
animation: appear 0.5s ease-in-out;
}         .game-over p {
font-size: 60px;
font-weight: bold;
margin: 0 0 20px 0;
}         .restart-btn {
background: #8f7a66;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 18px;
font-weight: bold;
}         .nav-buttons {
margin-top: 15px;
display: flex;
gap: 10px;
}     
2048+


💰 金幣
0


SCORE
0


BEST
0

     
🛒 產能與空間增益商店


最低生成: 2升級需 💰100


區域大小: 4x4解鎖需 💰1000

     



Game Over!
再試一次
     
重置當前局
💣 刪除存檔全重洗     
let board = [];
let score = 0;
let size = 4; 

// 從瀏覽器 LocalStorage 獲取存檔
let bestScore = parseInt(localStorage.getItem('2048_best')) || 0;
let gold = parseInt(localStorage.getItem('2048_gold')) || 0;
let minSpawnPower = parseInt(localStorage.getItem('2048_minSpawnPower')) || 1; 
let maxGridSize = parseInt(localStorage.getItem('2048_maxGridSize')) || 4;          size = maxGridSize;          const gameContainer = document.getElementById('game-container');
const gridContainer = document.getElementById('grid-container');
const tileContainer = document.getElementById('tile-container');         // 初始化局
function initGame() {
score = 0;
updateUI();

const containerSize = 400;
gameContainer.style.width = containerSize + 'px';
gameContainer.style.height = containerSize + 'px';

gridContainer.style.gridTemplateColumns = repeat(${size}, 1fr);
gridContainer.style.gridTemplateRows = repeat(${size}, 1fr);

gridContainer.innerHTML = '';
for (let i = 0; i < size * size; i++) {
const cell = document.createElement('div');
cell.className = 'grid-cell';
gridContainer.appendChild(cell);
}             board = Array(size).fill().map(() => Array(size).fill(0));
tileContainer.innerHTML = '';
document.getElementById('game-over').style.display = 'none';             addRandomTile();
addRandomTile();
drawTiles();
}         function getSpawnUpgradeCost() { return Math.pow(2, minSpawnPower) * 50; }
function getSizeUpgradeCost() { return (size === 4) ? 1000 : (size === 5) ? 5000 : Infinity; }         function updateUI() {
document.getElementById('score-val').innerText = score;
document.getElementById('best-val').innerText = bestScore;
document.getElementById('gold-val').innerText = gold;

const currentMinSpawn = Math.pow(2, minSpawnPower);
document.getElementById('spawn-lv').innerText = currentMinSpawn;
const spawnCost = getSpawnUpgradeCost();
document.getElementById('spawn-cost').innerText = spawnCost;
document.getElementById('upgrade-spawn-btn').disabled = (gold < spawnCost || minSpawnPower >= 6);             document.getElementById('grid-size-txt').innerText = ${size}x${size};
const sizeCost = getSizeUpgradeCost();
if(sizeCost === Infinity) {
document.getElementById('size-cost').innerText = "MAX";
document.getElementById('upgrade-size-btn').disabled = true;
} else {
document.getElementById('size-cost').innerText = sizeCost;
document.getElementById('upgrade-size-btn').disabled = (gold < sizeCost);
}             // 保存到前端存檔
localStorage.setItem('2048_gold', gold);
localStorage.setItem('2048_best', bestScore);
localStorage.setItem('2048_minSpawnPower', minSpawnPower);
localStorage.setItem('2048_maxGridSize', size);
}         // 升級生成數字,並將棋盤上的「舊方塊」集體升級!
function upgradeSpawn() {
const cost = getSpawnUpgradeCost();
if (gold >= cost && minSpawnPower < 6) {
gold -= cost;
minSpawnPower++; // 提高基礎生成值

const newMinSpawnValue = Math.pow(2, minSpawnPower);

// 遍歷所有舊方塊,低於新標準的全部強行變成新方塊!
let upgradeCount = 0;
for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) {
if (board[r][c] > 0 && board[r][c] < newMinSpawnValue) {
board[r][c] = newMinSpawnValue;
upgradeCount++;
}
}
}

updateUI();
drawTiles(); // 重新渲染畫面

if(upgradeCount > 0) {
alert(升級成功!最細生成變為: ${newMinSpawnValue}!\n棋盤上 ${upgradeCount} 個舊方塊已自動同步升級!);
} else {
alert(升級成功!最細生成變為: ${newMinSpawnValue}!);
}
}
}         function upgradeSize() {
const cost = getSizeUpgradeCost();
if (gold >= cost && size < 6) {
gold -= cost;
size++;
maxGridSize = size;
updateUI();
alert(地圖成功擴大為 ${size}x${size}!自動為您開啟新一局!);
initGame();
}
}         function addRandomTile() {
let emptyCells = [];
for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) {
if (board[r][c] === 0) emptyCells.push({r, c});
}
}
if (emptyCells.length > 0) {
const {r, c} = emptyCells[Math.floor(Math.random() * emptyCells.length)];
const isHigher = Math.random() < 0.1; 
board[r][c] = Math.pow(2, minSpawnPower + (isHigher ? 1 : 0));
}
}         function drawTiles() {
tileContainer.innerHTML = '';
const containerSize = 400;
const padding = 15;
const cellSize = (containerSize - padding * (size + 1)) / size;             for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) {
const value = board[r][c];
if (value > 0) {
const tile = document.createElement('div');
tile.className = tile tile-${value <= 2048 ? value : 'super'};
tile.innerText = value;

const top = padding + r * (cellSize + padding);
const left = padding + c * (cellSize + padding);

tile.style.width = cellSize + 'px';
tile.style.height = cellSize + 'px';
tile.style.top = top + 'px';
tile.style.left = left + 'px';

tileContainer.appendChild(tile);
}
}
}
}         function slide(row) {
let arr = row.filter(val => val !== 0);
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] === arr[i+1]) {
arr[i] *= 2;
gold += arr[i]; // 合成時獲得金幣
score += arr[i]; // 增加分數
arr[i+1] = 0;
}
}
arr = arr.filter(val => val !== 0);
while (arr.length < size) {
arr.push(0);
}
return arr;
}         function rotateBoard() {
let newBoard = Array(size).fill().map(() => Array(size).fill(0));
for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) {
newBoard[c][size - 1 - r] = board[r][c];
}
}
board = newBoard;
}         function move(direction) {
let moved = false;
const rotations = { 'left': 0, 'bottom': 1, 'right': 2, 'top': 3 }[direction];

for (let i = 0; i < rotations; i++) rotateBoard();             for (let r = 0; r < size; r++) {
const origin = [...board[r]];
board[r] = slide(board[r]);
if (JSON.stringify(origin) !== JSON.stringify(board[r])) {
moved = true;
}
}             for (let i = 0; i < (4 - rotations) % 4; i++) rotateBoard();             if (moved) {
addRandomTile();
if (score > bestScore) {
bestScore = score;
}
updateUI();
drawTiles();
checkGameOver();
}
}         function checkGameOver() {
for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) {
if (board[r][c] === 0) return;
}
}
for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) {
if (c < size - 1 && board[r][c] === board[r][c+1]) return;
if (r < size - 1 && board[r][c] === board[r+1][c]) return;
}
}
document.getElementById('game-over').style.display = 'flex';
}         function restartGame() {
initGame();
}         function hardReset() {
if(confirm("你確定要刪除所有存檔嗎?金幣、地圖等級同埋最高紀錄會全部消失!")) {
localStorage.clear();
bestScore = 0;
gold = 0;
minSpawnPower = 1;
size = 4;
maxGridSize = 4;
initGame();
}
}         // 鍵盤控制
window.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp' || e.key === 'w') move('top');
if (e.key === 'ArrowDown' || e.key === 's') move('bottom');
if (e.key === 'ArrowLeft' || e.key === 'a') move('left');
if (e.key === 'ArrowRight' || e.key === 'd') move('right');
});         // 觸控控制
let touchStartX = 0;
let touchStartY = 0;
gameContainer.addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
}, {passive: true});         gameContainer.addEventListener('touchend', (e) => {
if (!touchStartX || !touchStartY) return;
let diffX = e.changedTouches[0].clientX - touchStartX;
let diffY = e.changedTouches[0].clientY - touchStartY;

if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > 30) {
move(diffX > 0 ? 'right' : 'left');
}
} else {
if (Math.abs(diffY) > 30) {
move(diffY > 0 ? 'bottom' : 'top');
}
}
touchStartX = 0;
touchStartY = 0;
}, {passive: true});         initGame();