<!-- This Source Code Form is subject to the terms of the GNU General Public License: Copyright (C) 2021-2024 PokeTube (https://codeberg.org/Ashley/poketube) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/. --> <% if (!game) { %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="/css/yt-ukraine.svg?v=3" rel=icon> <link rel="manifest" href="/manifest.json"> <meta content="▶▶ Poke Games" property=og:title> <meta content="Do gaming on poke!" property=twitter:description> <meta content="https://cdn.glitch.global/d68d17bb-f2c0-4bc3-993f-50902734f652/aa70111e-5bcd-4379-8b23-332a33012b78.image.png?v=1701898829884" property="og:image" /> <meta content="summary_large_image" name="twitter:card" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { margin: 0; font-family: 'PokeTube flex', sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; background: linear-gradient(135deg, #2c3e50, #34495e); color: #fff; } .container { text-align: center; padding: 20px; } h1 { margin-top: 0; font-weight: 1000; font-stretch: ultra-expanded; font-family: "Poketube flex"; font-style: italic; color: #ffabcc; } .game-container { display: flex; gap: 20px; justify-content: center; flex-wrap: wrap; } @font-face { font-family: "PokeTube Flex"; src: url("https://p.poketube.fun/https://cdn.glitch.global/43b6691a-c8db-41d4-921c-8cf6aa0d9108/robotoflex.ttf?v=1668343428681"); font-style: normal; font-stretch: 1% 800%; font-display: swap; } .game { flex: 0 0 300px; padding: 20px; border: 2px solid #fff; border-radius: 10px; background: rgba(255, 255, 255, 0.1); transition: background 0.3s ease; text-decoration: none; color: #fff; display: flex; flex-direction: column; align-items: center; } .game:hover { background: rgba(255, 255, 255, 0.2); } canvas { border: 1px solid #fff; visibility: collapse; } .board { display: grid; grid-template-columns: repeat(3, 100px); grid-gap: 5px; margin-top: 20px; } .cell { width: 100px; height: 100px; border: 1px solid #fff; font-size: 2em; cursor: pointer; } </style> <title>Gaming Hub</title> </head> <body> <div class="container"> <h1>Poke! Games Hub</h1> <div class="game-container"> <a href="?game=snake" class="game"> <h2>Snake</h2> <canvas id="snakeCanvas"></canvas> </a> <a href="?game=tic-tac-toe" class="game"> <h2>Tic-Tac-Toe</h2> <div id="message"></div> <div class="board" id="board"></div> </a> <a href="?game=sudoku" class="game"> <h2>Sudoku</h2> <div id="sudokuMessage"></div> <div class="board" id="sudokuBoard"></div> </a> <a href="?game=pong" class="game"> <h2>Ping-Pong</h2> <div id="pingPongMessage"></div> <div class="board" id="pingPongBoard"></div> </a> </div> </div> <script src="/static/data-mobile.js?v=5459"></script> </body> </html> <% } %> <% if (game === "snake") { %> <!DOCTYPE html> <html lang="en"> <head> <link href="/css/yt-ukraine.svg?v=3" rel=icon> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> html, body { height: 100%; margin: 0; background: linear-gradient(135deg, #2c3e50, #34495e); overflow: hidden; } canvas { border: 1px solid #000; display: block; position: absolute; } </style> <title>Snak</title> </head> <body> <canvas id="snakeCanvas" width="400" height="400"></canvas> <script type="text/javascript"> <!--//--><![CDATA[//><!-- /** * @licstart The following is the entire license notice for the JavaScript * code in this page. * * Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube) * * The JavaScript code in this page is free software: you can redistribute * it and/or modify it under the terms of the GNU General Public License * (GNU GPL) as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. The code is * distributed WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL * for more details. * * As additional permission under GNU GPL version 3 section 7, you may * distribute non-source (e.g., minimized or compacted) forms of that code * without the copy of the GNU GPL normally required by section 4, provided * you include this license notice and a URL through which recipients can * access the Corresponding Source. * * @licend The above is the entire license notice for the JavaScript code * in this page. */ //--><!]]> </script> <script> const canvas = document.getElementById("snakeCanvas"); const ctx = canvas.getContext("2d"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // Initial snake position and size let snake = [ { x: canvas.width / 2, y: canvas.height / 2 }, { x: canvas.width / 2, y: canvas.height / 2 + 10 }, { x: canvas.width / 2, y: canvas.height / 2 + 20 } ]; let direction = "down"; // Initial direction // Initial food position let food = { x: 200, y: 200 }; function draw() { // Clear the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw the snake ctx.fillStyle = "#00F"; snake.forEach(segment => { ctx.fillRect(segment.x, segment.y, 10, 10); }); // Draw the food ctx.fillStyle = "#F00"; ctx.fillRect(food.x, food.y, 10, 10); } function move() { const head = { ...snake[0] }; // Create a new object for the head // Move the head based on the direction if (direction === "up") head.y -= 10; else if (direction === "down") head.y += 10; else if (direction === "left") head.x -= 10; else if (direction === "right") head.x += 10; // Add the new head to the front of the snake snake.unshift(head); // Check if the snake has eaten the food if (head.x === food.x && head.y === food.y) { // Generate new food food.x = Math.floor(Math.random() * (canvas.width / 10)) * 10; food.y = Math.floor(Math.random() * (canvas.height / 10)) * 10; } else { // If not eaten, remove the tail snake.pop(); } } function checkCollision() { const head = snake[0]; // Check if the snake hits the walls if (head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height) { alert("Game Over!"); resetGame(); } // Check if the snake collides with itself for (let i = 1; i < snake.length; i++) { if (head.x === snake[i].x && head.y === snake[i].y) { resetGame(); } } } function resetGame() { const middleX = Math.floor(canvas.width / 2 / 10) * 10; const middleY = Math.floor(canvas.height / 2 / 10) * 10; snake = [ { x: middleX, y: middleY }, { x: middleX, y: middleY + 10 }, { x: middleX, y: middleY + 20 } ]; direction = "down"; food = { x: 200, y: 200 }; } function gameLoop() { move(); checkCollision(); draw(); } // Set up keyboard event listeners window.addEventListener("keydown", e => { if (e.key === "ArrowUp" && direction !== "down") { direction = "up"; } else if (e.key === "ArrowDown" && direction !== "up") { direction = "down"; } else if (e.key === "ArrowLeft" && direction !== "right") { direction = "left"; } else if (e.key === "ArrowRight" && direction !== "left") { direction = "right"; } }); // Set up touch controls let touchStartX, touchStartY; canvas.addEventListener("touchstart", function(event) { touchStartX = event.touches[0].clientX; touchStartY = event.touches[0].clientY; }); canvas.addEventListener("touchmove", function(event) { const touchEndX = event.touches[0].clientX; const touchEndY = event.touches[0].clientY; const dx = touchEndX - touchStartX; const dy = touchEndY - touchStartY; if (Math.abs(dx) > Math.abs(dy)) { // Horizontal swipe if (dx > 0 && direction !== "left") { direction = "right"; } else if (dx < 0 && direction !== "right") { direction = "left"; } } else { // Vertical swipe if (dy > 0 && direction !== "up") { direction = "down"; } else if (dy < 0 && direction !== "down") { direction = "up"; } } }); // Set up the game loop setInterval(gameLoop, 100); </script> </body> </html> <% } %> <% if (game === "tic-tac-toe") { %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="/css/yt-ukraine.svg?v=3" rel=icon> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { font-family: "Poketube flex", sans-serif; text-align: center; display: flex; align-items: center; justify-content: center; height: 100vh; background: linear-gradient(135deg, #2c3e50, #34495e); margin: 0; } .container { text-align: left; margin-top: 20px; margin-left: 20px; color:#fff; } .board { display: grid; grid-template-columns: repeat(3, 100px); grid-gap: 5px; margin: 20px auto; } .cell { width: 100px; height: 100px; border: 1px solid #ccc; font-size: 2em; cursor: pointer; } @font-face { font-family: "PokeTube Flex"; src: url("https://p.poketube.fun/https://cdn.glitch.global/43b6691a-c8db-41d4-921c-8cf6aa0d9108/robotoflex.ttf?v=1668343428681"); font-style: normal; font-stretch: 1% 800%; font-display: swap; } .container > h1 { font-weight: 1000; font-stretch: ultra-expanded; font-style: italic; } </style> <title>Tic-Tac-Toe</title> </head> <body> <div class="container"> <h1>Tic-Tac-Toe</h1> <div id="message"></div> <div class="board" id="board"></div> </div> <script type="text/javascript"> <!--//--><![CDATA[//><!-- /** * @licstart The following is the entire license notice for the JavaScript * code in this page. * * Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube) * * The JavaScript code in this page is free software: you can redistribute * it and/or modify it under the terms of the GNU General Public License * (GNU GPL) as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. The code is * distributed WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL * for more details. * * As additional permission under GNU GPL version 3 section 7, you may * distribute non-source (e.g., minimized or compacted) forms of that code * without the copy of the GNU GPL normally required by section 4, provided * you include this license notice and a URL through which recipients can * access the Corresponding Source. * * @licend The above is the entire license notice for the JavaScript code * in this page. */ //--><!]]> </script> <script> const boardElement = document.getElementById("board"); const messageElement = document.getElementById("message"); let currentPlayer = "X"; let board = ["", "", "", "", "", "", "", "", ""]; function checkWinner() { const winningCombinations = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], // Rows [0, 3, 6], [1, 4, 7], [2, 5, 8], // Columns [0, 4, 8], [2, 4, 6] // Diagonals ]; for (const combination of winningCombinations) { const [a, b, c] = combination; if (board[a] && board[a] === board[b] && board[a] === board[c]) { return board[a]; } } return null; } function checkDraw() { return !board.includes(""); } function handleClick(index) { if (board[index] === "" && !checkWinner() && !checkDraw()) { board[index] = currentPlayer; renderBoard(); const winner = checkWinner(); if (winner) { messageElement.textContent = `Player ${winner} won!!!!!! woaah`; } else if (checkDraw()) { messageElement.textContent = "It's a draw! oh welp >~<"; } else { currentPlayer = currentPlayer === "X" ? "O" : "X"; messageElement.textContent = `Player ${currentPlayer}'s turn :3`; if (currentPlayer === "O") { setTimeout(makeComputerMove, 500); // AI waits for 0.5 second } } } } function makeComputerMove() { // Look for a winning move, then look to block player, otherwise, choose a random move const winningMove = findWinningMove(); const blockingMove = findBlockingMove(); if (winningMove !== null) { board[winningMove] = currentPlayer; } else if (blockingMove !== null) { board[blockingMove] = currentPlayer; } else { // Randomly choose an empty cell for the computer's move const emptyCells = board.reduce((acc, value, index) => { if (value === "") { acc.push(index); } return acc; }, []); if (emptyCells.length > 0) { const randomIndex = Math.floor(Math.random() * emptyCells.length); const computerMove = emptyCells[randomIndex]; board[computerMove] = currentPlayer; } } renderBoard(); const winner = checkWinner(); if (winner) { messageElement.textContent = `Player ${winner} won!!!!!! woaah`; } else if (checkDraw()) { messageElement.textContent = "It's a draw! oh welp >~<"; } else { currentPlayer = currentPlayer === "X" ? "O" : "X"; messageElement.textContent = `Player ${currentPlayer}'s turn :3`; } } function findWinningMove() { for (let i = 0; i < board.length; i++) { if (board[i] === "") { board[i] = currentPlayer; if (checkWinner() === currentPlayer) { board[i] = ""; // Reset the move return i; } board[i] = ""; // Reset the move } } return null; } function findBlockingMove() { const opponent = currentPlayer === "X" ? "O" : "X"; for (let i = 0; i < board.length; i++) { if (board[i] === "") { board[i] = opponent; if (checkWinner() === opponent) { board[i] = ""; // Reset the move return i; } board[i] = ""; // Reset the move } } return null; } function renderBoard() { boardElement.innerHTML = ""; board.forEach((value, index) => { const cell = document.createElement("div"); cell.classList.add("cell"); cell.textContent = value; cell.addEventListener("click", () => handleClick(index)); boardElement.appendChild(cell); }); } function resetGame() { currentPlayer = "X"; board = ["", "", "", "", "", "", "", "", ""]; messageElement.textContent = `Player ${currentPlayer}'s turn :3`; renderBoard(); // If AI is the starting player, make the first move if (currentPlayer === "O") { setTimeout(makeComputerMove, 500); // AI waits for 0.5 second } } // Initial setup resetGame(); </script> </body> </html> <% } %> <% if (game === "pong") { %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="/css/yt-ukraine.svg?v=3" rel=icon> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { margin: 0; display: flex; flex-direction: column; align-items: center; justify-content: space-between; height: 100vh; background: linear-gradient(135deg, #2c3e50, #34495e); color: #fff; font-family: 'Arial', sans-serif; } canvas { border: 1px solid #fff; } #controls { margin-bottom: 20px; } #score { position: absolute; top: 10px; left: 10px; } </style> <title>Pong</title> </head> <body> <canvas id="pongCanvas" width="800" height="400"></canvas> <div id="controls"> <p> U can press space to start owo </p> <p>Left Paddle Controls : W (Up) and S (Down)</p> <p>Right Paddle Controls: Arrow Up (Up) and Arrow Down (Down)</p> </div> <script type="text/javascript"> <!--//--><![CDATA[//><!-- /** * @licstart The following is the entire license notice for the JavaScript * code in this page. * * Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube) * * The JavaScript code in this page is free software: you can redistribute * it and/or modify it under the terms of the GNU General Public License * (GNU GPL) as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. The code is * distributed WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL * for more details. * * As additional permission under GNU GPL version 3 section 7, you may * distribute non-source (e.g., minimized or compacted) forms of that code * without the copy of the GNU GPL normally required by section 4, provided * you include this license notice and a URL through which recipients can * access the Corresponding Source. * * @licend The above is the entire license notice for the JavaScript code * in this page. */ //--><!]]> </script> <script> const canvas = document.getElementById("pongCanvas"); const ctx = canvas.getContext("2d"); const paddleWidth = 10; const paddleHeight = 60; const ballSize = 10; const paddleSpeed = 5; const maxScore = 5; // Adjust this to set the score threshold for game restart let leftPaddleY = canvas.height / 2 - paddleHeight / 2; let rightPaddleY = canvas.height / 2 - paddleHeight / 2; let ballX = canvas.width / 2; let ballY = canvas.height / 2; let ballSpeedX = 0; let ballSpeedY = 0; let leftScore = 0; let rightScore = 0; let leftPaddleUp = false; let leftPaddleDown = false; let rightPaddleUp = false; let rightPaddleDown = false; let gameActive = false; function drawPaddle(x, y) { ctx.fillStyle = "#fff"; ctx.fillRect(x, y, paddleWidth, paddleHeight); } function drawBall() { ctx.fillStyle = "#fff"; ctx.beginPath(); ctx.arc(ballX, ballY, ballSize, 0, Math.PI * 2); ctx.fill(); } function movePaddle() { if (gameActive) { document.addEventListener("keydown", function(event) { switch (event.key) { case "ArrowUp": rightPaddleUp = true; break; case "ArrowDown": rightPaddleDown = true; break; case "w": leftPaddleUp = true; break; case "s": leftPaddleDown = true; break; } }); document.addEventListener("keyup", function(event) { switch (event.key) { case "ArrowUp": rightPaddleUp = false; break; case "ArrowDown": rightPaddleDown = false; break; case "w": leftPaddleUp = false; break; case "s": leftPaddleDown = false; break; } }); if (leftPaddleUp && leftPaddleY > 0) leftPaddleY -= paddleSpeed; if (leftPaddleDown && leftPaddleY < canvas.height - paddleHeight) leftPaddleY += paddleSpeed; if (rightPaddleUp && rightPaddleY > 0) rightPaddleY -= paddleSpeed; if (rightPaddleDown && rightPaddleY < canvas.height - paddleHeight) rightPaddleY += paddleSpeed; } } function moveBall() { if (gameActive) { ballX += ballSpeedX; ballY += ballSpeedY; // Ball hits top or bottom if (ballY < 0 || ballY > canvas.height) { ballSpeedY = -ballSpeedY; } // Ball hits left paddle if (ballX - ballSize < paddleWidth && ballY > leftPaddleY && ballY < leftPaddleY + paddleHeight) { ballSpeedX = -ballSpeedX; } // Ball hits right paddle if (ballX + ballSize > canvas.width - paddleWidth && ballY > rightPaddleY && ballY < rightPaddleY + paddleHeight) { ballSpeedX = -ballSpeedX; } // Ball goes out of bounds if (ballX < 0) { // Right player scores rightScore++; checkGameOver(); } if (ballX > canvas.width) { // Left player scores leftScore++; checkGameOver(); } } } function checkGameOver() { if (leftScore >= maxScore || rightScore >= maxScore) { resetGame(); } else { resetBall(); } } function resetGame() { leftScore = 0; rightScore = 0; resetBall(); } function resetBall() { ballX = canvas.width / 2; ballY = canvas.height / 2; ballSpeedX = 0; ballSpeedY = 0; gameActive = false; setTimeout(() => { ballSpeedX = Math.random() > 0.5 ? 5 : -5; ballSpeedY = Math.random() > 0.5 ? 5 : -5; gameActive = true; }, 100); } function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); drawPaddle(0, leftPaddleY); drawPaddle(canvas.width - paddleWidth, rightPaddleY); drawBall(); movePaddle(); moveBall(); drawScores(); requestAnimationFrame(update); } function drawScores() { ctx.fillStyle = "#fff"; ctx.font = "20px Arial"; ctx.fillText("Left Player: " + leftScore, 20, 30); ctx.fillText("Right Player: " + rightScore, canvas.width - 160, 30); } document.addEventListener("keydown", function(event) { if (!gameActive && event.key === " ") { resetGame(); ballSpeedX = Math.random() > 0.5 ? 5 : -5; ballSpeedY = Math.random() > 0.5 ? 5 : -5; gameActive = true; } }); // Touch controls canvas.addEventListener("touchstart", function(event) { if (!gameActive) { resetGame(); ballSpeedX = Math.random() > 0.5 ? 5 : -5; ballSpeedY = Math.random() > 0.5 ? 5 : -5; gameActive = true; } }); canvas.addEventListener("touchmove", function(event) { const touchY = event.touches[0].clientY - canvas.offsetTop; if (touchY > leftPaddleY && touchY < leftPaddleY + paddleHeight) { leftPaddleY = touchY - paddleHeight / 2; } }); update(); </script> </body> </html> <% } %> <% if (game === "sudoku") { %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="/css/yt-ukraine.svg?v=3" rel=icon> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: linear-gradient(135deg, #2c3e50, #34495e); font-family: 'Arial', sans-serif; } #sudoku-board { display: grid; grid-template-columns: repeat(9, 40px); grid-template-rows: repeat(9, 40px); grid-gap: 1px; } .cell { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; font-size: 16px; font-weight: bold; cursor: pointer; background-color: #ddd; user-select: none; } .given { background-color: #ccc; } .error { background-color: #ffbaba; } #popup { display: none; border-radius: 1em; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px; background-color: #fff; border: 1px solid #ccc; box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); z-index: 999; } #popup > button { border: none; background: gray; border-radius: 3px; color: #fff; height: 4em; width: 9em; } #overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 998; } </style> <title>Sudoku</title> </head> <body> <div id="sudoku-board"></div> <div id="overlay"></div> <div id="popup"> <h2>U did a incorrect move :sob:</h2> <p>Please enter a valid number (1-9)</p> <button onclick="closePopup()">Oki</button> </div> <script type="text/javascript"> <!--//--><![CDATA[//><!-- /** * @licstart The following is the entire license notice for the JavaScript * code in this page. * * Copyright (C) 2021-2024 POKETUBE (https://codeberg.org/Ashley/poketube) * * The JavaScript code in this page is free software: you can redistribute * it and/or modify it under the terms of the GNU General Public License * (GNU GPL) as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. The code is * distributed WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL * for more details. * * As additional permission under GNU GPL version 3 section 7, you may * distribute non-source (e.g., minimized or compacted) forms of that code * without the copy of the GNU GPL normally required by section 4, provided * you include this license notice and a URL through which recipients can * access the Corresponding Source. * * @licend The above is the entire license notice for the JavaScript code * in this page. */ //--><!]]> </script> <script type="text/javascript"> function closePopup() { document.getElementById('popup').style.display = 'none'; document.getElementById('overlay').style.display = 'none'; } function showAlertPopup() { document.getElementById('popup').style.display = 'block'; document.getElementById('overlay').style.display = 'block'; } const initialBoard = [ [5, 3, 0, 0, 7, 0, 0, 0, 0], [6, 0, 0, 1, 9, 5, 0, 0, 0], [0, 9, 8, 0, 0, 0, 0, 6, 0], [8, 0, 0, 0, 6, 0, 0, 0, 3], [4, 0, 0, 8, 0, 3, 0, 0, 1], [7, 0, 0, 0, 2, 0, 0, 0, 6], [0, 6, 0, 0, 0, 0, 2, 8, 0], [0, 0, 0, 4, 1, 9, 0, 0, 5], [0, 0, 0, 0, 8, 0, 0, 7, 9] ]; let board = JSON.parse(JSON.stringify(initialBoard)); // Deep copy to prevent mutation of the original board function initializeBoard() { const sudokuBoard = document.getElementById('sudoku-board'); board = generateRandomBoard(initialBoard); for (let i = 0; i < 9; i++) { for (let j = 0; j < 9; j++) { const cell = document.createElement('div'); cell.className = 'cell'; cell.textContent = board[i][j] !== 0 ? board[i][j] : ''; cell.dataset.row = i; cell.dataset.col = j; if (board[i][j] !== 0) { cell.classList.add('given'); } cell.addEventListener('click', () => handleClick(i, j)); sudokuBoard.appendChild(cell); } } } function generateRandomBoard(baseBoard) { // Randomly shuffle rows and columns const shuffledRows = shuffleArray(baseBoard); const shuffledCols = transposeMatrix(shuffleArray(transposeMatrix(baseBoard))); // Randomly remove some numbers const modifiedBoard = removeNumbers(shuffledRows); return modifiedBoard; } function shuffleArray(array) { const newArray = array.slice(); for (let i = newArray.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [newArray[i], newArray[j]] = [newArray[j], newArray[i]]; } return newArray; } function transposeMatrix(matrix) { return matrix[0].map((_, colIndex) => matrix.map(row => row[colIndex])); } function removeNumbers(board) { const modifiedBoard = board.map(row => row.slice()); const emptyCount = Math.floor(Math.random() * 10) + 20; // Randomly choose 20 to 30 cells to be empty for (let i = 0; i < emptyCount; i++) { const row = Math.floor(Math.random() * 9); const col = Math.floor(Math.random() * 9); modifiedBoard[row][col] = 0; } return modifiedBoard; } function handleClick(row, col) { const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`); if (!cell.classList.contains('given')) { const value = prompt('Enter a number (1-9):'); const parsedValue = parseInt(value, 10); if (!isNaN(parsedValue) && parsedValue >= 1 && parsedValue <= 9) { board[row][col] = parsedValue; cell.textContent = parsedValue; if (isBoardValid()) { cell.classList.remove('error'); } else { cell.classList.add('error'); showAlertPopup(); } } else { alert('Invalid input. Please enter a number between 1 and 9.'); } } } function isBoardValid() { // Check rows and columns for (let i = 0; i < 9; i++) { const rowSet = new Set(); const colSet = new Set(); for (let j = 0; j < 9; j++) { if (board[i][j] !== 0 && rowSet.has(board[i][j])) { return false; } rowSet.add(board[i][j]); if (board[j][i] !== 0 && colSet.has(board[j][i])) { return false; } colSet.add(board[j][i]); } } // Check 3x3 subgrids for (let i = 0; i < 9; i += 3) { for (let j = 0; j < 9; j += 3) { const subgridSet = new Set(); for (let row = i; row < i + 3; row++) { for (let col = j; col < j + 3; col++) { if (board[row][col] !== 0 && subgridSet.has(board[row][col])) { return false; } subgridSet.add(board[row][col]); } } } } return true; } initializeBoard(); </script> </body> </html> <% } %>