From 6e5106e43f2f85e36e7e0be12271daf9770e07e6 Mon Sep 17 00:00:00 2001
From: Abhay Mall <136182755+AbhayMall@users.noreply.github.com>
Date: Mon, 18 Aug 2025 10:43:55 +0530
Subject: [PATCH 1/2] Added 2048 Game
---
public/2048Game/index.css | 325 +++++++++++++++++++++++++++++++++++++
public/2048Game/index.html | 76 +++++++++
public/2048Game/script.js | 320 ++++++++++++++++++++++++++++++++++++
scripts/app.js | 12 ++
4 files changed, 733 insertions(+)
create mode 100644 public/2048Game/index.css
create mode 100644 public/2048Game/index.html
create mode 100644 public/2048Game/script.js
diff --git a/public/2048Game/index.css b/public/2048Game/index.css
new file mode 100644
index 00000000..caaca1f0
--- /dev/null
+++ b/public/2048Game/index.css
@@ -0,0 +1,325 @@
+
+ *, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ }
+
+ body {
+ font-family: 'Arial', sans-serif;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: #333;
+ overflow-x: hidden;
+ }
+
+ .game-container {
+ display: flex;
+ gap: 2rem;
+ max-width: 1200px;
+ width: 100%;
+ padding: 1rem;
+ align-items: flex-start;
+ }
+
+ .game-section {
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 20px;
+ padding: 2rem;
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ }
+
+ .main-game {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1.5rem;
+ }
+
+ .instructions {
+ width: 700px;
+ min-height: 150px;
+ }
+
+ .game-header {
+ text-align: center;
+ margin-bottom: 1rem;
+ }
+
+ .game-title {
+ font-size: 3.5rem;
+ font-weight: bold;
+ color: #ed6a5a;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
+ margin-bottom: 0.5rem;
+ }
+
+ .game-subtitle {
+ font-size: 1rem;
+ color: #666;
+ margin-bottom: 1rem;
+ }
+
+ .score-container {
+ display: flex;
+ gap: 1rem;
+ justify-content: center;
+ margin-bottom: 1rem;
+ }
+
+ .score-box {
+ background: #ed6a5a;
+ color: white;
+ padding: 0.8rem 1.5rem;
+ border-radius: 12px;
+ text-align: center;
+ min-width: 100px;
+ box-shadow: 0 4px 8px rgba(237, 106, 90, 0.3);
+ }
+
+ .score-label {
+ font-size: 0.8rem;
+ opacity: 0.9;
+ margin-bottom: 0.2rem;
+ }
+
+ .score-value {
+ font-size: 1.5rem;
+ font-weight: bold;
+ }
+
+ .controls {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 1rem;
+ }
+
+ .btn {
+ padding: 0.8rem 1.5rem;
+ border: none;
+ border-radius: 12px;
+ font-size: 1rem;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ }
+
+ .btn-primary {
+ background: #4ecdc4;
+ color: white;
+ }
+
+ .btn-primary:hover {
+ background: #45b7aa;
+ transform: translateY(-2px);
+ box-shadow: 0 6px 12px rgba(78, 205, 196, 0.4);
+ }
+
+ .btn-secondary {
+ background: #f7f7f7;
+ color: #333;
+ border: 2px solid #ddd;
+ }
+
+ .btn-secondary:hover {
+ background: #e9e9e9;
+ transform: translateY(-2px);
+ }
+
+ #board {
+ display: grid;
+ grid-template-columns: repeat(4, 80px);
+ grid-template-rows: repeat(4, 80px);
+ gap: 8px;
+ background: #bbada0;
+ border-radius: 16px;
+ padding: 12px;
+ position: relative;
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
+ }
+
+ .tile {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 8px;
+ font-size: 2rem;
+ font-weight: bold;
+ background: rgba(238, 228, 218, 0.35);
+ transition: all 0.15s ease-in-out;
+ user-select: none;
+ }
+
+ .tile.new-tile {
+ animation: appear 0.2s ease-in-out;
+ }
+
+ @keyframes appear {
+ 0% { opacity: 0; transform: scale(0); }
+ 100% { opacity: 1; transform: scale(1); }
+ }
+
+ .tile.merged {
+ animation: merge 0.15s ease-in-out;
+ }
+
+ @keyframes merge {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.1); }
+ 100% { transform: scale(1); }
+ }
+
+ /* Tile colors */
+ .x2 { background: #eee4da; color: #727371; }
+ .x4 { background: #ece0ca; color: #727371; }
+ .x8 { background: #f4b17a; color: white; }
+ .x16 { background: #f59575; color: white; }
+ .x32 { background: #f57c5f; color: white; }
+ .x64 { background: #f65d3b; color: white; }
+ .x128 { background: #edce71; color: white; font-size: 1.8rem; }
+ .x256 { background: #edcc63; color: white; font-size: 1.8rem; }
+ .x512 { background: #edc651; color: white; font-size: 1.8rem; }
+ .x1024 { background: #eec744; color: white; font-size: 1.5rem; }
+ .x2048 { background: #ecc230; color: white; font-size: 1.5rem; box-shadow: 0 0 20px rgba(236, 194, 48, 0.5); }
+ .x4096 { background: #fe3d3d; color: white; font-size: 1.5rem; }
+ .x8192 { background: #ff2020; color: white; font-size: 1.5rem; }
+
+ /* Instructions panel */
+ .instructions h2 {
+ color: #ed6a5a;
+ font-size: 1.8rem;
+ margin-bottom: 1rem;
+ text-align: center;
+ }
+
+ .instructions h3 {
+ color: #4ecdc4;
+ font-size: 1.2rem;
+ margin: 1.5rem 0 0.5rem 0;
+ }
+
+ .instructions p, .instructions li {
+ line-height: 1.6;
+ margin-bottom: 0.8rem;
+ color: #555;
+ }
+
+ .instructions ul {
+ margin-left: 1.2rem;
+ }
+
+ .key-hint {
+ display: inline-flex;
+ align-items: center;
+ background: #f0f0f0;
+ border: 2px solid #ddd;
+ border-radius: 6px;
+ padding: 0.2rem 0.5rem;
+ font-family: monospace;
+ font-weight: bold;
+ margin: 0 0.2rem;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ }
+
+ .goal-highlight {
+ background: linear-gradient(90deg, #ecc230, #edce71);
+ color: white;
+ padding: 0.8rem;
+ border-radius: 12px;
+ text-align: center;
+ font-weight: bold;
+ margin: 1rem 0;
+ box-shadow: 0 4px 8px rgba(236, 194, 48, 0.3);
+ }
+
+ .game-over {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.8);
+ display: none;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+ }
+
+ .game-over-content {
+ background: white;
+ padding: 2rem;
+ border-radius: 20px;
+ text-align: center;
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
+ }
+
+ .game-over h2 {
+ color: #ed6a5a;
+ font-size: 2rem;
+ margin-bottom: 1rem;
+ }
+
+ .game-over p {
+ font-size: 1.2rem;
+ margin-bottom: 1.5rem;
+ color: #666;
+ }
+
+ /* Mobile responsive */
+ @media (max-width: 768px) {
+ .game-container {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .instructions {
+ width: 100%;
+ max-width: 400px;
+ order: 2;
+ }
+
+ .main-game {
+ order: 1;
+ }
+
+ .game-title {
+ font-size: 2.5rem;
+ }
+
+ #board {
+ grid-template-columns: repeat(4, 70px);
+ grid-template-rows: repeat(4, 70px);
+ }
+
+ .tile {
+ font-size: 1.5rem;
+ }
+
+ .x128, .x256, .x512 {
+ font-size: 1.3rem;
+ }
+
+ .x1024, .x2048, .x4096, .x8192 {
+ font-size: 1.1rem;
+ }
+ }
+
+ @media (max-width: 480px) {
+ #board {
+ grid-template-columns: repeat(4, 60px);
+ grid-template-rows: repeat(4, 60px);
+ gap: 6px;
+ }
+
+ .tile {
+ font-size: 1.3rem;
+ }
+ }
+
\ No newline at end of file
diff --git a/public/2048Game/index.html b/public/2048Game/index.html
new file mode 100644
index 00000000..a39ae994
--- /dev/null
+++ b/public/2048Game/index.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+ 2048 Game
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🎮 How to Play
+
+
+ Goal: Reach the 2048 tile!
+
+
+
🎯 Game Rules
+
+ - Use arrow keys to move tiles
+ - When two tiles with the same number touch, they merge into one
+ - After every move, a new tile will randomly appear
+ - The game is over when you can't make any more moves
+
+
+
⌨️ Controls
+
Desktop:
+
+ - ↑ Move Up
+ - ↓ Move Down
+ - ← Move Left
+ - → Move Right
+
+
+
Mobile: Swipe in any direction
+
+
🏆 Scoring
+
Every time you merge tiles, you get points equal to the value of the new tile created. Try to beat your best score!
+
+
+
+
+
+
Game Over!
+
Final Score: 0
+
+
+
+
+
\ No newline at end of file
diff --git a/public/2048Game/script.js b/public/2048Game/script.js
new file mode 100644
index 00000000..0923fe53
--- /dev/null
+++ b/public/2048Game/script.js
@@ -0,0 +1,320 @@
+var board;
+ var score = 0;
+ var best = localStorage.getItem('best2048') || 0;
+ var rows = 4;
+ var columns = 4;
+ var gameStarted = false;
+
+ // Touch handling variables
+ let pageWidth = window.innerWidth || document.body.clientWidth;
+ let threshold = Math.max(1, Math.floor(0.01 * pageWidth));
+ let touchstartX = 0;
+ let touchstartY = 0;
+ let touchendX = 0;
+ let touchendY = 0;
+
+ const limit = Math.tan(45 * 1.5 / 180 * Math.PI);
+
+ window.onload = function() {
+ document.getElementById('best').innerText = best;
+ setGame();
+ }
+
+ function setGame() {
+ board = [
+ [0, 0, 0, 0],
+ [0, 0, 0, 0],
+ [0, 0, 0, 0],
+ [0, 0, 0, 0]
+ ];
+
+ // Clear the board
+ document.getElementById("board").innerHTML = '';
+
+ for (let row = 0; row < rows; row++) {
+ for (let column = 0; column < columns; column++) {
+ let tile = document.createElement("div");
+ tile.id = row.toString() + "-" + column.toString();
+ let num = board[row][column];
+ updateTile(tile, num);
+ document.getElementById("board").append(tile);
+ }
+ }
+
+ generateRandomTile();
+ generateRandomTile();
+ gameStarted = true;
+ }
+
+ function newGame() {
+ score = 0;
+ document.getElementById("score").innerText = score;
+ document.getElementById("gameOver").style.display = 'none';
+ setGame();
+ }
+
+ function toggleInstructions() {
+ const instructions = document.getElementById('instructions');
+ if (window.innerWidth <= 768) {
+ instructions.style.display = instructions.style.display === 'none' ? 'block' : 'none';
+ }
+ }
+
+ function hasEmptyTile() {
+ for (let row = 0; row < rows; row++) {
+ for (let column = 0; column < columns; column++) {
+ if (board[row][column] == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ function canMove() {
+ if (hasEmptyTile()) return true;
+
+ // Check for possible merges
+ for (let row = 0; row < rows; row++) {
+ for (let column = 0; column < columns; column++) {
+ let current = board[row][column];
+ if ((row < rows - 1 && current == board[row + 1][column]) ||
+ (column < columns - 1 && current == board[row][column + 1])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ function generateRandomTile() {
+ if (!hasEmptyTile()) {
+ if (!canMove()) {
+ gameOver();
+ }
+ return;
+ }
+
+ let found = false;
+ while (!found) {
+ let row = Math.floor(Math.random() * rows);
+ let column = Math.floor(Math.random() * columns);
+
+ if (board[row][column] == 0) {
+ board[row][column] = Math.random() < 0.9 ? 2 : 4;
+ let tile = document.getElementById(row.toString() + "-" + column.toString());
+ updateTile(tile, board[row][column]);
+ tile.classList.add('new-tile');
+ setTimeout(() => tile.classList.remove('new-tile'), 200);
+ found = true;
+ }
+ }
+ }
+
+ function gameOver() {
+ document.getElementById('finalScore').innerText = score;
+ document.getElementById('gameOver').style.display = 'flex';
+
+ if (score > best) {
+ best = score;
+ localStorage.setItem('best2048', best);
+ document.getElementById('best').innerText = best;
+ }
+ }
+
+ function updateTile(tile, num) {
+ tile.innerText = "";
+ tile.classList.value = "";
+ tile.classList.add("tile");
+
+ if (num > 0) {
+ tile.innerText = num;
+ if (num <= 8192) {
+ tile.classList.add("x" + num.toString());
+ } else {
+ tile.classList.add("x8192");
+ }
+ }
+ }
+
+ function makeMove(direction) {
+ if (!gameStarted) return;
+
+ let moved = false;
+ let oldBoard = board.map(row => row.slice());
+
+ switch(direction) {
+ case 'left': moved = slideLeft(); break;
+ case 'right': moved = slideRight(); break;
+ case 'up': moved = slideUp(); break;
+ case 'down': moved = slideDown(); break;
+ }
+
+ if (moved) {
+ generateRandomTile();
+ document.getElementById("score").innerText = score;
+
+ // Check for 2048 tile
+ for (let row = 0; row < rows; row++) {
+ for (let column = 0; column < columns; column++) {
+ if (board[row][column] == 2048) {
+ setTimeout(() => {
+ alert('Congratulations! You reached 2048! 🎉\nYou can continue playing to reach higher scores.');
+ }, 300);
+ }
+ }
+ }
+ }
+ }
+
+ // Keyboard controls
+ document.addEventListener("keyup", (event) => {
+ switch(event.code) {
+ case "ArrowLeft": makeMove('left'); break;
+ case "ArrowRight": makeMove('right'); break;
+ case "ArrowUp": makeMove('up'); break;
+ case "ArrowDown": makeMove('down'); break;
+ }
+ });
+
+ // Touch controls
+ document.addEventListener('touchstart', function(event) {
+ touchstartX = event.changedTouches[0].screenX;
+ touchstartY = event.changedTouches[0].screenY;
+ }, false);
+
+ document.addEventListener('touchend', function(event) {
+ touchendX = event.changedTouches[0].screenX;
+ touchendY = event.changedTouches[0].screenY;
+ handleGesture();
+ }, false);
+
+ function handleGesture() {
+ let x = touchendX - touchstartX;
+ let y = touchendY - touchstartY;
+ let xy = Math.abs(x / y);
+ let yx = Math.abs(y / x);
+
+ if (Math.abs(x) > threshold || Math.abs(y) > threshold) {
+ if (yx <= limit) {
+ if (x < 0) {
+ makeMove('left');
+ } else {
+ makeMove('right');
+ }
+ }
+ if (xy <= limit) {
+ if (y < 0) {
+ makeMove('up');
+ } else {
+ makeMove('down');
+ }
+ }
+ }
+ }
+
+ function filterZeros(row) {
+ return row.filter(num => num != 0);
+ }
+
+ function slide(row) {
+ row = filterZeros(row);
+ let merged = new Array(row.length).fill(false);
+
+ for (let i = 0; i < row.length - 1; i++) {
+ if (row[i] == row[i + 1] && !merged[i] && !merged[i + 1]) {
+ row[i] *= 2;
+ row[i + 1] = 0;
+ score += row[i];
+ merged[i] = true;
+ }
+ }
+
+ row = filterZeros(row);
+
+ while (row.length < columns) {
+ row.push(0);
+ }
+
+ return row;
+ }
+
+ function slideLeft() {
+ let moved = false;
+ for (let i = 0; i < rows; i++) {
+ let originalRow = board[i].slice();
+ let row = slide(board[i]);
+ board[i] = row;
+
+ if (JSON.stringify(originalRow) !== JSON.stringify(row)) {
+ moved = true;
+ }
+
+ for (let j = 0; j < columns; j++) {
+ let tile = document.getElementById(i.toString() + "-" + j.toString());
+ updateTile(tile, board[i][j]);
+ }
+ }
+ return moved;
+ }
+
+ function slideRight() {
+ let moved = false;
+ for (let i = 0; i < rows; i++) {
+ let originalRow = board[i].slice();
+ let row = board[i].slice().reverse();
+ row = slide(row);
+ row.reverse();
+ board[i] = row;
+
+ if (JSON.stringify(originalRow) !== JSON.stringify(row)) {
+ moved = true;
+ }
+
+ for (let j = 0; j < columns; j++) {
+ let tile = document.getElementById(i.toString() + "-" + j.toString());
+ updateTile(tile, board[i][j]);
+ }
+ }
+ return moved;
+ }
+
+ function slideUp() {
+ let moved = false;
+ for (let j = 0; j < columns; j++) {
+ let originalColumn = [board[0][j], board[1][j], board[2][j], board[3][j]];
+ let row = slide(originalColumn);
+
+ if (JSON.stringify(originalColumn) !== JSON.stringify(row)) {
+ moved = true;
+ }
+
+ for (let i = 0; i < rows; i++) {
+ board[i][j] = row[i];
+ let tile = document.getElementById(i.toString() + "-" + j.toString());
+ updateTile(tile, board[i][j]);
+ }
+ }
+ return moved;
+ }
+
+ function slideDown() {
+ let moved = false;
+ for (let j = 0; j < columns; j++) {
+ let originalColumn = [board[0][j], board[1][j], board[2][j], board[3][j]];
+ let row = originalColumn.slice().reverse();
+ row = slide(row);
+ row.reverse();
+
+ if (JSON.stringify(originalColumn) !== JSON.stringify(row)) {
+ moved = true;
+ }
+
+ for (let i = 0; i < rows; i++) {
+ board[i][j] = row[i];
+ let tile = document.getElementById(i.toString() + "-" + j.toString());
+ updateTile(tile, board[i][j]);
+ }
+ }
+ return moved;
+ }
\ No newline at end of file
diff --git a/scripts/app.js b/scripts/app.js
index 99d61b3e..7e31f634 100644
--- a/scripts/app.js
+++ b/scripts/app.js
@@ -1134,9 +1134,21 @@ class WebDev100Days {
features: [
"Dynamic grid scaling, pattern generation & replay, dark/light mode, score & level tracking, keyboard accessibility, animated feedback, and replay option."
]
+},
+{
+ originalDay: 166,
+ name:"2048 Game",
+ description:"2048 is a sliding puzzle game where you combine numbered tiles to reach the 2048 tile.",
+ demoLink:"./public/2048Game/index.html",
+ category: "games",
+ technologies: ["HTML", "CSS", "JavaScript"],
+ features: [
+ "Sliding puzzle with high score , winner badge and score tracker."
+ ]
}
+
];
this.projects = projectsData.map((project, index) => ({
From 2d892e93a87410ed0eac4a41cf6c88f78cef869b Mon Sep 17 00:00:00 2001
From: Abhay Mall <136182755+AbhayMall@users.noreply.github.com>
Date: Mon, 18 Aug 2025 11:04:07 +0530
Subject: [PATCH 2/2] ADDED 2048 Game
---
public/2048Game/index.css => CSS/2048-index.css | 0
public/2048Game/index.html => HTML Pages/2048-index.html | 4 ++--
public/2048Game/script.js => Logic/2048-script.js | 0
scripts/app.js | 2 +-
4 files changed, 3 insertions(+), 3 deletions(-)
rename public/2048Game/index.css => CSS/2048-index.css (100%)
rename public/2048Game/index.html => HTML Pages/2048-index.html (95%)
rename public/2048Game/script.js => Logic/2048-script.js (100%)
diff --git a/public/2048Game/index.css b/CSS/2048-index.css
similarity index 100%
rename from public/2048Game/index.css
rename to CSS/2048-index.css
diff --git a/public/2048Game/index.html b/HTML Pages/2048-index.html
similarity index 95%
rename from public/2048Game/index.html
rename to HTML Pages/2048-index.html
index a39ae994..4f5f1ab8 100644
--- a/public/2048Game/index.html
+++ b/HTML Pages/2048-index.html
@@ -4,8 +4,8 @@
2048 Game
-
-
+
+
diff --git a/public/2048Game/script.js b/Logic/2048-script.js
similarity index 100%
rename from public/2048Game/script.js
rename to Logic/2048-script.js
diff --git a/scripts/app.js b/scripts/app.js
index 7e31f634..7970d589 100644
--- a/scripts/app.js
+++ b/scripts/app.js
@@ -1139,7 +1139,7 @@ class WebDev100Days {
originalDay: 166,
name:"2048 Game",
description:"2048 is a sliding puzzle game where you combine numbered tiles to reach the 2048 tile.",
- demoLink:"./public/2048Game/index.html",
+ demoLink:"./HTML Pages/2048-index.html",
category: "games",
technologies: ["HTML", "CSS", "JavaScript"],
features: [