diff --git a/web-app/js/projects.js b/web-app/js/projects.js
index b341de5..501b820 100644
--- a/web-app/js/projects.js
+++ b/web-app/js/projects.js
@@ -69,7 +69,8 @@ function initializeProject(projectName) {
'coordinate-polar-transform': initCoordinatePolarTransform,
'derivative-calculator': initDerivativeCalculator,
'morse-code': initMorseCode,
- 'tower-of-hanoi': initTowerOfHanoi
+ 'tower-of-hanoi': initTowerOfHanoi,
+ '2048-game': init2048Game // Added explicit mapped hook definition binding reference
};
if (initializers[projectName]) {
diff --git a/web-app/js/projects/2048-game.js b/web-app/js/projects/2048-game.js
index 75f59fd..b7dd6c1 100644
--- a/web-app/js/projects/2048-game.js
+++ b/web-app/js/projects/2048-game.js
@@ -3,408 +3,416 @@
// ============================================
function get2048GameHTML() {
-
return `
-
🎮 2048 Game
-
-
- Score:
- 0
+ Score: 0
-
- Best:
- 0
+ Best: 0
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
-
`;
}
-function init2048Game() {
-
- const gridContainer =
- document.getElementById("grid-container");
-
-const scoreDisplay =
- document.getElementById("score");
-
-const bestDisplay =
- document.getElementById("best-score");
-
-if (!gridContainer || !scoreDisplay || !bestDisplay) {
- console.log("2048 elements not loaded yet");
- return;
-}
+// ============================================
+// INIT WRAPPER
+// ============================================
-let board = [];
+function init2048Game() {
+ const gridContainer = document.getElementById("grid-container");
+ const scoreDisplay = document.getElementById("score");
+ const bestDisplay = document.getElementById("best-score");
-let score = 0;
+ if (!gridContainer || !scoreDisplay || !bestDisplay) {
+ setTimeout(init2048Game, 50);
+ return;
+ }
-let bestScore =
- localStorage.getItem("best2048") || 0;
+ if (gridContainer.dataset.initialized === "true") return;
+ gridContainer.dataset.initialized = "true";
-bestDisplay.textContent = bestScore;
+ let board = [];
+ let score = 0;
+ let bestScore = localStorage.getItem("best2048") || 0;
+ bestDisplay.textContent = bestScore;
function createBoard() {
-
board = [
[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
[0,0,0,0]
];
-
score = 0;
-
addNewTile();
addNewTile();
-
drawBoard();
}
function addNewTile() {
-
- let emptyCells = [];
-
- for(let r = 0; r < 4; r++) {
-
- for(let c = 0; c < 4; c++) {
-
- if(board[r][c] === 0) {
-
- emptyCells.push({r,c});
- }
+ let empty = [];
+ for (let r = 0; r < 4; r++) {
+ for (let c = 0; c < 4; c++) {
+ if (board[r][c] === 0) empty.push({ r, c });
}
}
+ if (!empty.length) return;
- if(emptyCells.length === 0) return;
-
- const randomCell =
- emptyCells[
- Math.floor(
- Math.random() * emptyCells.length
- )
- ];
-
- board[randomCell.r][randomCell.c] =
- Math.random() < 0.9 ? 2 : 4;
+ const cell = empty[Math.floor(Math.random() * empty.length)];
+ board[cell.r][cell.c] = Math.random() < 0.9 ? 2 : 4;
}
function drawBoard() {
-
gridContainer.innerHTML = "";
-
board.forEach(row => {
-
row.forEach(cell => {
-
- const tile =
- document.createElement("div");
-
- tile.classList.add("tile");
-
- tile.textContent =
- cell !== 0 ? cell : "";
-
- tile.style.background = getTileColor(cell);
-
- if(cell <= 4) {
- tile.style.color = "#776e65";
- } else {
- tile.style.color = "var(--on-accent)";
- }
-
+ const tile = document.createElement("div");
+ tile.className = "tile";
+ tile.textContent = cell || "";
+ tile.style.backgroundColor = getColor(cell);
+ tile.style.color = cell <= 4 ? "#776e65" : "#fff";
gridContainer.appendChild(tile);
});
});
scoreDisplay.textContent = score;
- if(score > bestScore) {
-
+ if (score > bestScore) {
bestScore = score;
-
- localStorage.setItem(
- "best2048",
- bestScore
- );
-
+ localStorage.setItem("best2048", bestScore);
bestDisplay.textContent = bestScore;
}
}
- function getTileColor(value) {
-
- const colors = {
- 0: "var(--control-color)",
- 2: "#eee4da",
- 4: "#ede0c8",
- 8: "#f2b179",
- 16: "#f59563",
- 32: "#f67c5f",
- 64: "#f65e3b",
- 128: "#edcf72",
- 256: "#edcc61",
- 512: "#edc850",
- 1024: "#edc53f",
- 2048: "#ffcc00",
- 4096: "#ff5733"
- };
-
- return colors[value] || "var(--accent-color)";
-}
-
- function slide(row) {
-
- row = row.filter(val => val);
-
- for(let i = 0; i < row.length - 1; i++) {
+ function getColor(v) {
+ return {
+ 0: "#cdc1b4",
+ 2: "#eee4da",
+ 4: "#ede0c8",
+ 8: "#f2b179",
+ 16: "#f59563",
+ 32: "#f67c5f",
+ 64: "#f65e3b",
+ 128: "#edcf72",
+ 256: "#edcc61",
+ 512: "#edc850",
+ 1024: "#edc53f",
+ 2048: "#edc22e"
+ }[v] || "#3c3a32";
+ }
- if(row[i] === row[i+1]) {
+ function compress(row) {
+ let arr = row.filter(v => v);
+ while (arr.length < 4) arr.push(0);
+ return arr;
+ }
+ function merge(row) {
+ for (let i = 0; i < 3; i++) {
+ if (row[i] && row[i] === row[i + 1]) {
row[i] *= 2;
- if(row[i] === 2048) {
-
- setTimeout(() => {
-
- alert("🎉 You reached 2048!");
-
- }, 100);
-}
-
score += row[i];
-
- row[i+1] = 0;
+ row[i + 1] = 0;
}
}
-
- row = row.filter(val => val);
-
- while(row.length < 4) {
-
- row.push(0);
- }
-
return row;
}
function moveLeft() {
-
- let changed = false;
-
- for(let r = 0; r < 4; r++) {
-
- let original = [...board[r]];
-
- board[r] = slide(board[r]);
-
- if(original.toString() !== board[r].toString()) {
-
- changed = true;
- }
- }
-
- return changed;
+ let moved = false;
+ board = board.map(row => {
+ const old = [...row];
+ let newRow = compress(merge(compress(row)));
+ if (old.toString() !== newRow.toString()) moved = true;
+ return newRow;
+ });
+ return moved;
}
function moveRight() {
-
- let changed = false;
-
- for(let r = 0; r < 4; r++) {
-
- let original = [...board[r]];
-
- board[r].reverse();
-
- board[r] = slide(board[r]);
-
- board[r].reverse();
-
- if(original.toString() !== board[r].toString()) {
-
- changed = true;
- }
- }
-
- return changed;
+ board = board.map(row => row.reverse());
+ let moved = moveLeft();
+ board = board.map(row => row.reverse());
+ return moved;
}
function transpose() {
-
- for(let r = 0; r < 4; r++) {
-
- for(let c = r; c < 4; c++) {
-
- let temp = board[r][c];
-
- board[r][c] = board[c][r];
-
- board[c][r] = temp;
- }
- }
+ board = board[0].map((_, i) => board.map(row => row[i]));
}
function moveUp() {
-
transpose();
-
- let changed = moveLeft();
-
+ let moved = moveLeft();
transpose();
-
- return changed;
+ return moved;
}
function moveDown() {
-
transpose();
-
- let changed = moveRight();
-
+ let moved = moveRight();
transpose();
-
- return changed;
+ return moved;
}
- window.addEventListener("keydown", (e) => {
-
+ function makeMove(dir) {
let moved = false;
+ if (dir === "left") moved = moveLeft();
+ if (dir === "right") moved = moveRight();
+ if (dir === "up") moved = moveUp();
+ if (dir === "down") moved = moveDown();
- if(e.key === "ArrowLeft") {
- moved = moveLeft();
+ if (moved) {
+ addNewTile();
+ drawBoard();
}
+ }
- else if(e.key === "ArrowRight") {
- moved = moveRight();
- }
+ // On-Screen Controls Configuration
+ document.getElementById("ctrl-2048-up").onclick = () => makeMove("up");
+ document.getElementById("ctrl-2048-down").onclick = () => makeMove("down");
+ document.getElementById("ctrl-2048-left").onclick = () => makeMove("left");
+ document.getElementById("ctrl-2048-right").onclick = () => makeMove("right");
- else if(e.key === "ArrowUp") {
- moved = moveUp();
- }
+ // Restart Handling
+ document.getElementById("restart-btn").onclick = createBoard;
- else if(e.key === "ArrowDown") {
- moved = moveDown();
+ // Keyboard Fallback Setup
+ const handleKeyDown = (e) => {
+ if (!document.getElementById("grid-container")) {
+ window.removeEventListener("keydown", handleKeyDown);
+ return;
}
+ if (e.key === "ArrowLeft") { e.preventDefault(); makeMove("left"); }
+ if (e.key === "ArrowRight") { e.preventDefault(); makeMove("right"); }
+ if (e.key === "ArrowUp") { e.preventDefault(); makeMove("up"); }
+ if (e.key === "ArrowDown") { e.preventDefault(); makeMove("down"); }
+ };
+ window.addEventListener("keydown", handleKeyDown);
- if(moved) {
+ // Pointer Swiping Mechanics
+ let touchStartX = 0;
+ let touchStartY = 0;
+ const minSwipeDistance = 40;
- addNewTile();
+ function handleSwipeEnd(endX, endY) {
+ const diffX = endX - touchStartX;
+ const diffY = endY - touchStartY;
- drawBoard();
+ if (Math.max(Math.abs(diffX), Math.abs(diffY)) < minSwipeDistance) return;
+
+ if (Math.abs(diffX) > Math.abs(diffY)) {
+ if (diffX > 0) makeMove("right");
+ else makeMove("left");
+ } else {
+ if (diffY > 0) makeMove("down");
+ else makeMove("up");
}
- });
+ }
- document
- .getElementById("restart-btn")
- .addEventListener("click", () => {
+ gridContainer.addEventListener("touchstart", (e) => {
+ touchStartX = e.touches[0].clientX;
+ touchStartY = e.touches[0].clientY;
+ }, { passive: true });
+
+ gridContainer.addEventListener("touchend", (e) => {
+ if (!e.changedTouches.length) return;
+ handleSwipeEnd(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
+ }, { passive: true });
+
+ let isDragging = false;
+ gridContainer.addEventListener("mousedown", (e) => {
+ isDragging = true;
+ touchStartX = e.clientX;
+ touchStartY = e.clientY;
+ });
- createBoard();
- });
+ window.addEventListener("mouseup", (e) => {
+ if (!isDragging) return;
+ isDragging = false;
+ handleSwipeEnd(e.clientX, e.clientY);
+ });
createBoard();
}
+
+// Global polling initializer fallback hook
+const waitFor2048 = setInterval(() => {
+ if (document.getElementById("grid-container")) {
+ clearInterval(waitFor2048);
+ init2048Game();
+ }
+}, 100);
\ No newline at end of file