diff --git a/Games/16_Puzzle/README.md b/Games/16_Puzzle/README.md new file mode 100644 index 0000000000..62fff635a2 --- /dev/null +++ b/Games/16_Puzzle/README.md @@ -0,0 +1,35 @@ +# Number Puzzle Game + +This is a simple and attractive number puzzle game implemented using HTML, CSS, JavaScript, and Bootstrap. The game is inspired by the classic 8-puzzle game, where the objective is to arrange numbered tiles in sequential order by sliding them into an empty space. + +## Features + +- Interactive 4x4 grid puzzle (16-puzzle). +- Shuffle button to randomize the tiles. +- Move counter to track the number of moves. +- Timer to track the time taken to solve the puzzle. +- Score calculation based on moves and time taken. + +## How to Play + +1. Open the game in your web browser. +2. Click the "Shuffle" button to start a new game. +3. Slide the tiles by clicking on them to move them into the empty space. +4. Arrange the tiles in numerical order (1 to 16) with the empty space in the bottom-right corner. +5. The game ends when you have arranged all the tiles correctly. Your score will be displayed based on the number of moves and time taken. + + +## Installation + +No installation is required. Simply open the `index.html` file in your web browser to start playing the game. + +## Files + +- `index.html`: The main HTML file that contains the structure of the game. +- `style.css`: The CSS file that styles the game using Bootstrap and custom styles. +- `script.js`: The JavaScript file that contains the game logic and interactivity. + +## Usage + +1. Clone the repository or download the files. +2. Open `index.html` in your preferred web browser. \ No newline at end of file diff --git a/Games/16_Puzzle/index.html b/Games/16_Puzzle/index.html new file mode 100644 index 0000000000..5bd168b742 --- /dev/null +++ b/Games/16_Puzzle/index.html @@ -0,0 +1,40 @@ + + + + Puzzle game + + + + +
+
+ + + + + + + + + + + + + + + + +
+ + +
+

You win!

+ + + + + \ No newline at end of file diff --git a/Games/16_Puzzle/script.js b/Games/16_Puzzle/script.js new file mode 100644 index 0000000000..716b66ec0a --- /dev/null +++ b/Games/16_Puzzle/script.js @@ -0,0 +1,201 @@ +class Box { + constructor(x, y) { + this.x = x; + this.y = y; + } + + getTopBox() { + if (this.y === 0) return null; + return new Box(this.x, this.y - 1); + } + + getRightBox() { + if (this.x === 3) return null; + return new Box(this.x + 1, this.y); + } + + getBottomBox() { + if (this.y === 3) return null; + return new Box(this.x, this.y + 1); + } + + getLeftBox() { + if (this.x === 0) return null; + return new Box(this.x - 1, this.y); + } + + getNextdoorBoxes() { + return [ + this.getTopBox(), + this.getRightBox(), + this.getBottomBox(), + this.getLeftBox() + ].filter(box => box !== null); + } + + getRandomNextdoorBox() { + const nextdoorBoxes = this.getNextdoorBoxes(); + return nextdoorBoxes[Math.floor(Math.random() * nextdoorBoxes.length)]; + } +} + +const swapBoxes = (grid, box1, box2) => { + const temp = grid[box1.y][box1.x]; + grid[box1.y][box1.x] = grid[box2.y][box2.x]; + grid[box2.y][box2.x] = temp; +}; + +const isSolved = grid => { + return ( + grid[0][0] === 1 && + grid[0][1] === 2 && + grid[0][2] === 3 && + grid[0][3] === 4 && + grid[1][0] === 5 && + grid[1][1] === 6 && + grid[1][2] === 7 && + grid[1][3] === 8 && + grid[2][0] === 9 && + grid[2][1] === 10 && + grid[2][2] === 11 && + grid[2][3] === 12 && + grid[3][0] === 13 && + grid[3][1] === 14 && + grid[3][2] === 15 && + grid[3][3] === 0 + ); +}; + +const getRandomGrid = () => { + let grid = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 0]]; + + // Shuffle + let blankBox = new Box(3, 3); + for (let i = 0; i < 1000; i++) { + const randomNextdoorBox = blankBox.getRandomNextdoorBox(); + swapBoxes(grid, blankBox, randomNextdoorBox); + blankBox = randomNextdoorBox; + } + + if (isSolved(grid)) return getRandomGrid(); + return grid; +}; + +class State { + constructor(grid, move, time, status) { + this.grid = grid; + this.move = move; + this.time = time; + this.status = status; + } + + static ready() { + return new State( + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + 0, + 0, + "ready" + ); + } + + static start() { + return new State(getRandomGrid(), 0, 0, "playing"); + } +} + +class Game { + constructor(state) { + this.state = state; + this.tickId = null; + this.tick = this.tick.bind(this); + this.render(); + this.handleClickBox = this.handleClickBox.bind(this); + } + + static ready() { + return new Game(State.ready()); + } + + tick() { + this.setState({ time: this.state.time + 1 }); + } + + setState(newState) { + this.state = { ...this.state, ...newState }; + this.render(); + } + + handleClickBox(box) { + return function() { + const nextdoorBoxes = box.getNextdoorBoxes(); + const blankBox = nextdoorBoxes.find( + nextdoorBox => this.state.grid[nextdoorBox.y][nextdoorBox.x] === 0 + ); + if (blankBox) { + const newGrid = [...this.state.grid]; + swapBoxes(newGrid, box, blankBox); + if (isSolved(newGrid)) { + clearInterval(this.tickId); + this.setState({ + status: "won", + grid: newGrid, + move: this.state.move + 1 + }); + } else { + this.setState({ + grid: newGrid, + move: this.state.move + 1 + }); + } + } + }.bind(this); + } + + render() { + const { grid, move, time, status } = this.state; + + // Render grid + const newGrid = document.createElement("div"); + newGrid.className = "grid"; + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + const button = document.createElement("button"); + + if (status === "playing") { + button.addEventListener("click", this.handleClickBox(new Box(j, i))); + } + + button.textContent = grid[i][j] === 0 ? "" : grid[i][j].toString(); + newGrid.appendChild(button); + } + } + document.querySelector(".grid").replaceWith(newGrid); + + // Render button + const newButton = document.createElement("button"); + if (status === "ready") newButton.textContent = "Play"; + if (status === "playing") newButton.textContent = "Reset"; + if (status === "won") newButton.textContent = "Play"; + newButton.addEventListener("click", () => { + clearInterval(this.tickId); + this.tickId = setInterval(this.tick, 1000); + this.setState(State.start()); + }); + document.querySelector(".footer button").replaceWith(newButton); + + // Render move + document.getElementById("move").textContent = `Move: ${move}`; + + // Render time + document.getElementById("time").textContent = `Time: ${time}`; + + // Render message + if (status === "won") { + document.querySelector(".message").textContent = "You win!"; + } else { + document.querySelector(".message").textContent = ""; + } + } +} + +const GAME = Game.ready(); diff --git a/Games/16_Puzzle/style.css b/Games/16_Puzzle/style.css new file mode 100644 index 0000000000..9cc40cc8fd --- /dev/null +++ b/Games/16_Puzzle/style.css @@ -0,0 +1,79 @@ +html { + height: 100%; +} + +body { + height: 100%; + background-image: linear-gradient(#7B9F35, #226666); + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.game { + /* position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); */ + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5); + padding: 15px; + background-color: #AA3939; + border-radius: 5px; +} + +.grid { + display: grid; + grid-template-columns: 80px 80px 80px 80px; + grid-template-rows: 80px 80px 80px 80px; + border: 1px solid #550000; +} + +.grid button { + background-color: #cfcfcf; + color: #003333; + font-size: 24px; + font-weight: bold; + border: 1px solid #550000; + outline: none; + cursor: pointer; +} + +.footer { + margin-top: 15px; + display: flex; + justify-content: space-between; +} + +.footer button { + border: none; + font-size: 20px; + font-weight: bold; + border-radius: 5px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5); + padding: 5px; + width: 80px; + background-color: #D4EE9F; + color: #003333; + outline: none; + cursor: pointer; +} + +.footer button:hover { + color: #D4EE9F; + background-color: #003333; +} + +.footer span { + flex: 1; + text-align: center; + font-size: 20px; + color: #D4EE9F; + font-weight: bold; + margin: auto 0; +} + +.message { + color:#AA3939; + height: 80px; +} \ No newline at end of file diff --git a/README.md b/README.md index 6ad612495b..e5208785bc 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ This repository also provides one such platforms where contributers come over an | [Automated_rock_paper_scissor](https://github.com/kunjgit/GameZone/tree/main/Games/automated_rock_paper_scissor) | | [Maze_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Maze_Game) | [Astronaut_runner](https://github.com/tanishkaa08/GameZone/tree/main/Games/Astronaunt_runner) | - +[16_Puzzle](https://github.com/kunjgit/GameZone/tree/main/Games/16_Puzzle) | | [Dragon_Tower](https://github.com/kunjgit/GameZone/tree/main/Games/Dragon_Tower) | diff --git a/assets/images/16_Puzzle.png b/assets/images/16_Puzzle.png new file mode 100644 index 0000000000..56f2d4d077 Binary files /dev/null and b/assets/images/16_Puzzle.png differ diff --git a/assets/js/gamesData.json b/assets/js/gamesData.json index 10dac16ba9..b6d220766b 100644 --- a/assets/js/gamesData.json +++ b/assets/js/gamesData.json @@ -2062,6 +2062,12 @@ "gameTitle": "Candy_Crush_Saga", "gameUrl": "Candy_Crush_Saga", "thumbnailUrl": "Candy_Crush_Saga.png" +},"419":{ + + "gameTitle": "16_Puzzle", + "gameUrl": "16_Puzzle", + "thumbnailUrl": "16_Puzzle.png" + }, "406":{ "gameTitle": "Knife_hit",