diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000..f2c9e97 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/HTML+CSS+JS Apps/Flappy Block/index.html b/HTML+CSS+JS Apps/Flappy Block/index.html new file mode 100644 index 0000000..07e38f1 --- /dev/null +++ b/HTML+CSS+JS Apps/Flappy Block/index.html @@ -0,0 +1,21 @@ + + + + Flappy Block + + + +
Score: 0
+
Click or tap to float up gently!
+
+
+
+ Game Over!
+ Click Start to play again +
+
+ + + + + diff --git a/HTML+CSS+JS Apps/Flappy Block/script.js b/HTML+CSS+JS Apps/Flappy Block/script.js new file mode 100644 index 0000000..c9bc9ae --- /dev/null +++ b/HTML+CSS+JS Apps/Flappy Block/script.js @@ -0,0 +1,140 @@ +const gameContainer = document.getElementById('game-container'); +const block = document.getElementById('block'); +const scoreDisplay = document.getElementById('score'); +const startButton = document.getElementById('start-button'); +const gameOverDisplay = document.getElementById('game-over'); + +let blockY = 300; +let velocity = 0; +let gravity = 0.15; // Much lower gravity +let jump = -4; // Much gentler jump +let score = 0; +let gameLoop; +let obstacles = []; +let gameActive = false; + +function startGame() { + blockY = 300; + velocity = 0; + score = 0; + scoreDisplay.textContent = `Score: ${score}`; + obstacles.forEach(obstacle => obstacle.remove()); + obstacles = []; + gameOverDisplay.style.display = 'none'; + gameActive = true; + + gameLoop = setInterval(updateGame, 20); + setInterval(createObstacle, 3000); // Much longer delay between obstacles +} + +function updateGame() { + if (!gameActive) return; + + velocity += gravity; + velocity = Math.min(velocity, 5); // Limit falling speed + blockY += velocity; + block.style.top = blockY + 'px'; + + const blockRect = block.getBoundingClientRect(); + + // More forgiving boundary checking + if (blockY < -30 || blockY > gameContainer.clientHeight - 30) { + gameOver(); + } + + obstacles.forEach(obstacle => { + const obstacleRect = obstacle.getBoundingClientRect(); + // More forgiving collision detection + if ( + blockRect.right - 10 > obstacleRect.left && + blockRect.left + 10 < obstacleRect.right && + (blockRect.top + 10 < obstacleRect.top + obstacleRect.height && + blockRect.bottom - 10 > obstacleRect.top) + ) { + gameOver(); + } + }); +} + +function createObstacle() { + if (!gameActive) return; + + const gapHeight = 300; // Much larger gap + const gapPosition = Math.random() * (gameContainer.clientHeight - gapHeight - 100) + 50; + + const topObstacle = document.createElement('div'); + topObstacle.className = 'obstacle'; + topObstacle.style.height = gapPosition + 'px'; + topObstacle.style.top = '0'; + gameContainer.appendChild(topObstacle); + + const bottomObstacle = document.createElement('div'); + bottomObstacle.className = 'obstacle'; + bottomObstacle.style.height = (gameContainer.clientHeight - gapPosition - gapHeight) + 'px'; + bottomObstacle.style.bottom = '0'; + gameContainer.appendChild(bottomObstacle); + + obstacles.push(topObstacle, bottomObstacle); + + let position = gameContainer.clientWidth; + const moveObstacles = setInterval(() => { + if (!gameActive) { + clearInterval(moveObstacles); + return; + } + + position -= 1; // Much slower obstacle movement + topObstacle.style.right = gameContainer.clientWidth - position + 'px'; + bottomObstacle.style.right = gameContainer.clientWidth - position + 'px'; + + if (position < -80) { + topObstacle.remove(); + bottomObstacle.remove(); + obstacles = obstacles.filter(obs => obs !== topObstacle && obs !== bottomObstacle); + clearInterval(moveObstacles); + score++; + scoreDisplay.textContent = `Score: ${score}`; + } + }, 20); +} + +function gameOver() { + gameActive = false; + clearInterval(gameLoop); + gameOverDisplay.style.display = 'block'; +} + +gameContainer.addEventListener('click', () => { + if (gameActive) { + velocity = jump; + } +}); + +// Add continuous floating while mouse/finger is held down +let floatInterval; +gameContainer.addEventListener('mousedown', () => { + if (gameActive) { + floatInterval = setInterval(() => { + velocity = Math.max(velocity, -2); // Gentle floating + }, 50); + } +}); + +gameContainer.addEventListener('mouseup', () => { + clearInterval(floatInterval); +}); + +gameContainer.addEventListener('touchstart', (e) => { + e.preventDefault(); + if (gameActive) { + floatInterval = setInterval(() => { + velocity = Math.max(velocity, -2); + }, 50); + } +}); + +gameContainer.addEventListener('touchend', () => { + clearInterval(floatInterval); +}); + +startButton.addEventListener('click', startGame); \ No newline at end of file diff --git a/HTML+CSS+JS Apps/Flappy Block/style.css b/HTML+CSS+JS Apps/Flappy Block/style.css new file mode 100644 index 0000000..91303b1 --- /dev/null +++ b/HTML+CSS+JS Apps/Flappy Block/style.css @@ -0,0 +1,79 @@ +body { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; + margin: 0; + background: linear-gradient(45deg, #FF61D8, #6B5DFF); + font-family: Arial, sans-serif; +} +#game-container { + width: 500px; + height: 600px; + position: relative; + overflow: hidden; + background: linear-gradient(180deg, #00C6FF, #0072FF); + border: 3px solid #ffffff; + border-radius: 10px; + box-shadow: 0 0 20px rgba(0,0,0,0.3); +} +#block { + width: 60px; + height: 60px; + background: linear-gradient(45deg, #FF5E5E, #FFB459); + position: absolute; + left: 100px; + border-radius: 10px; + box-shadow: 0 0 10px rgba(0,0,0,0.2); +} +.obstacle { + width: 80px; + background: linear-gradient(180deg, #00FF87, #60EFFF); + position: absolute; + right: -80px; + border: 2px solid rgba(255,255,255,0.5); + opacity: 0.8; +} +#score { + font-size: 32px; + color: white; + margin: 20px; + text-shadow: 2px 2px 4px rgba(0,0,0,0.3); +} +#start-button { + padding: 15px 30px; + font-size: 24px; + background: linear-gradient(45deg, #FF61D8, #6B5DFF); + color: white; + border: none; + border-radius: 25px; + cursor: pointer; + margin: 15px; + box-shadow: 0 4px 15px rgba(0,0,0,0.2); + transition: transform 0.2s; +} +#start-button:hover { + transform: scale(1.05); +} +#game-over { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: linear-gradient(45deg, rgba(0,0,0,0.8), rgba(0,0,0,0.9)); + color: white; + padding: 30px; + border-radius: 15px; + text-align: center; + display: none; + font-size: 24px; + box-shadow: 0 0 20px rgba(0,0,0,0.5); +} +#instructions { + color: white; + text-align: center; + margin-bottom: 10px; + font-size: 18px; + text-shadow: 1px 1px 2px rgba(0,0,0,0.3); +} \ No newline at end of file diff --git a/HTML+CSS+JS Apps/Minesweeper Game App/index.html b/HTML+CSS+JS Apps/Minesweeper Game App/index.html new file mode 100644 index 0000000..cf4f5b8 --- /dev/null +++ b/HTML+CSS+JS Apps/Minesweeper Game App/index.html @@ -0,0 +1,26 @@ + + + + Minesweeper + + + +
+

Minesweeper

+
Find the safe path, avoid the mines!
+ +
+
+
Score
+
0
+
+ +
+ +
+
+
+ + + + diff --git a/HTML+CSS+JS Apps/Minesweeper Game App/script.js b/HTML+CSS+JS Apps/Minesweeper Game App/script.js new file mode 100644 index 0000000..594588c --- /dev/null +++ b/HTML+CSS+JS Apps/Minesweeper Game App/script.js @@ -0,0 +1,145 @@ +class MineMaster { + constructor() { + this.GRID_SIZE = 8; + this.MINE_COUNT = 10; + this.grid = []; + this.revealed = []; + this.gameOver = false; + this.points = 0; + this.win = false; + + this.gridElement = document.querySelector('.grid'); + this.scoreElement = document.querySelector('.score'); + this.messageElement = document.querySelector('.message'); + this.newGameButton = document.querySelector('.new-game-btn'); + + this.newGameButton.addEventListener('click', () => this.initializeGame()); + this.initializeGame(); + } + + initializeGame() { + this.grid = Array(this.GRID_SIZE).fill().map(() => Array(this.GRID_SIZE).fill(0)); + this.revealed = Array(this.GRID_SIZE).fill().map(() => Array(this.GRID_SIZE).fill(false)); + this.gameOver = false; + this.points = 0; + this.win = false; + this.scoreElement.textContent = '0'; + this.messageElement.style.display = 'none'; + + // Place mines + let minesPlaced = 0; + while (minesPlaced < this.MINE_COUNT) { + const x = Math.floor(Math.random() * this.GRID_SIZE); + const y = Math.floor(Math.random() * this.GRID_SIZE); + if (this.grid[y][x] !== -1) { + this.grid[y][x] = -1; + minesPlaced++; + } + } + + // Calculate numbers + for (let y = 0; y < this.GRID_SIZE; y++) { + for (let x = 0; x < this.GRID_SIZE; x++) { + if (this.grid[y][x] !== -1) { + let count = 0; + for (let dy = -1; dy <= 1; dy++) { + for (let dx = -1; dx <= 1; dx++) { + const ny = y + dy; + const nx = x + dx; + if (ny >= 0 && ny < this.GRID_SIZE && nx >= 0 && nx < this.GRID_SIZE) { + if (this.grid[ny][nx] === -1) count++; + } + } + } + this.grid[y][x] = count; + } + } + } + + this.renderGrid(); + } + + renderGrid() { + this.gridElement.innerHTML = ''; + for (let y = 0; y < this.GRID_SIZE; y++) { + for (let x = 0; x < this.GRID_SIZE; x++) { + const cell = document.createElement('button'); + cell.className = 'cell'; + cell.dataset.x = x; + cell.dataset.y = y; + cell.addEventListener('click', () => this.revealCell(y, x)); + this.gridElement.appendChild(cell); + } + } + } + + revealCell(y, x) { + if (this.gameOver || this.revealed[y][x] || this.win) return; + + if (this.grid[y][x] === -1) { + this.gameOver = true; + this.showMessage('Game Over! 💥', false); + this.revealAllMines(); + return; + } + + this.floodFill(y, x); + this.checkWin(); + } + + floodFill(y, x) { + if (y < 0 || y >= this.GRID_SIZE || x < 0 || x >= this.GRID_SIZE) return; + if (this.revealed[y][x]) return; + + this.revealed[y][x] = true; + this.points += 10; + this.scoreElement.textContent = this.points; + + const cell = this.gridElement.children[y * this.GRID_SIZE + x]; + cell.classList.add('revealed'); + + if (this.grid[y][x] > 0) { + cell.textContent = this.grid[y][x]; + cell.dataset.value = this.grid[y][x]; + } + + if (this.grid[y][x] === 0) { + for (let dy = -1; dy <= 1; dy++) { + for (let dx = -1; dx <= 1; dx++) { + this.floodFill(y + dy, x + dx); + } + } + } + } + + revealAllMines() { + for (let y = 0; y < this.GRID_SIZE; y++) { + for (let x = 0; x < this.GRID_SIZE; x++) { + if (this.grid[y][x] === -1) { + const cell = this.gridElement.children[y * this.GRID_SIZE + x]; + cell.classList.add('revealed', 'mine'); + cell.textContent = '💣'; + } + } + } + } + + checkWin() { + const revealedCount = this.revealed.flat().filter(cell => cell).length; + if (revealedCount === (this.GRID_SIZE * this.GRID_SIZE) - this.MINE_COUNT) { + this.win = true; + this.points += 100; + this.scoreElement.textContent = this.points; + this.showMessage(`Victory! 🎉 Final Score: ${this.points}`, true); + } + } + + showMessage(text, isWin) { + this.messageElement.textContent = text; + this.messageElement.className = `message ${isWin ? 'win' : 'lose'}`; + this.messageElement.style.display = 'block'; + } +} + +// Start the game +new MineMaster(); \ No newline at end of file diff --git a/HTML+CSS+JS Apps/Minesweeper Game App/style.css b/HTML+CSS+JS Apps/Minesweeper Game App/style.css new file mode 100644 index 0000000..cdea0f1 --- /dev/null +++ b/HTML+CSS+JS Apps/Minesweeper Game App/style.css @@ -0,0 +1,144 @@ +:root { + --cell-size: 40px; + --grid-size: 8; +} + +body { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + margin: 0; + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #f0f4ff 0%, #f4f0ff 100%); +} + +.game-container { + background: white; + padding: 2rem; + border-radius: 1rem; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + text-align: center; +} + +.title { + font-size: 2rem; + font-weight: bold; + margin-bottom: 0.5rem; + background: linear-gradient(45deg, #4466ee, #9944ee); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.subtitle { + color: #666; + margin-bottom: 1.5rem; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; +} + +.score-container { + background: white; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} + +.score-label { + font-size: 0.875rem; + color: #666; +} + +.score { + font-size: 1.5rem; + font-weight: bold; + color: #4466ee; +} + +.new-game-btn { + padding: 0.75rem 1.5rem; + font-size: 1rem; + font-weight: bold; + color: white; + background: linear-gradient(45deg, #4466ee, #9944ee); + border: none; + border-radius: 0.5rem; + cursor: pointer; + transition: transform 0.2s; +} + +.new-game-btn:hover { + transform: scale(1.05); +} + +.message { + margin: 1rem 0; + padding: 1rem; + border-radius: 0.5rem; + font-weight: bold; + display: none; +} + +.message.win { + background: #e6ffe6; + color: #00aa00; + border: 2px solid #00aa00; +} + +.message.lose { + background: #ffe6e6; + color: #aa0000; + border: 2px solid #aa0000; +} + +.grid { + display: grid; + grid-template-columns: repeat(var(--grid-size), var(--cell-size)); + gap: 4px; + padding: 1rem; + background: #f0f0f0; + border-radius: 0.5rem; +} + +.cell { + width: var(--cell-size); + height: var(--cell-size); + display: flex; + justify-content: center; + align-items: center; + font-weight: bold; + font-size: 1.25rem; + border: none; + border-radius: 0.25rem; + cursor: pointer; + transition: transform 0.2s; + background: linear-gradient(45deg, #4466ee, #9944ee); + color: white; +} + +.cell:hover:not(.revealed) { + transform: scale(1.05); +} + +.cell.revealed { + background: #ffffff; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.cell.mine { + background: #ff4444; +} + +.cell[data-value="1"] { color: #4466ee; } +.cell[data-value="2"] { color: #44aa44; } +.cell[data-value="3"] { color: #ee4444; } +.cell[data-value="4"] { color: #4444aa; } +.cell[data-value="5"] { color: #aa4444; } +.cell[data-value="6"] { color: #44aaaa; } +.cell[data-value="7"] { color: #444444; } +.cell[data-value="8"] { color: #888888; } \ No newline at end of file