Skip to content

Commit 21650c1

Browse files
author
Sabari
committed
15 puzzle
1 parent 0012689 commit 21650c1

File tree

5 files changed

+3874
-3530
lines changed

5 files changed

+3874
-3530
lines changed

assets/Images/15puzzle/15puzzle.png

49.7 KB
Loading

assets/css/15puzzle.css

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
html{
2+
height: 100%;
3+
}
4+
5+
body{
6+
height: 100%;
7+
background-image: linear-gradient(#7B9F35,#226666);
8+
display: flex;
9+
justify-content: center;
10+
align-items: center;
11+
flex-direction: column;
12+
}
13+
14+
.game {
15+
/* position: absolute;
16+
top: 50%;
17+
left: 50%;
18+
transform: translate(-50%, -50%); */
19+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
20+
background-color: tomato;
21+
padding: 15px;
22+
border-radius: 5px;
23+
}
24+
25+
.grid{
26+
display: grid;
27+
grid-template-columns: 80px 80px 80px 80px;
28+
grid-template-rows: 80px 80px 80px 80px;
29+
border: 1px solid #550000;
30+
}
31+
32+
.grid button{
33+
background-color: #cfcfcf;
34+
color: #56A7E8;
35+
font-size: 24px;
36+
font-weight: bold;
37+
border: 1px solid #354F00;
38+
outline: none;
39+
cursor: pointer;
40+
}
41+
42+
.footer{
43+
margin-top: 15px;
44+
display: flex;
45+
justify-content: space-between;
46+
}
47+
48+
.footer button{
49+
border: none;
50+
font-size: 20px;
51+
font-weight: bold;
52+
border-radius: 5px;
53+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
54+
padding: 5px;
55+
width: 80px;
56+
background-color: #7B9F35;
57+
color: #354F00;
58+
outline: none;
59+
cursor: pointer;
60+
}
61+
62+
.footer button:hover{
63+
color: #D4EE9F;
64+
background-color: #354F00;
65+
}
66+
67+
.footer span{
68+
flex: 1;
69+
text-align: center;
70+
font-size: 20px;
71+
color: yellow;
72+
font-weight: bold;
73+
margin: auto;
74+
}
75+
76+
.message{
77+
color: tomato;
78+
height: 80px;
79+
}

assets/js/15puzzle.js

+221
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
class Box {
2+
constructor(x, y) {
3+
this.x = x;
4+
this.y = y;
5+
}
6+
7+
getTopBox() {
8+
if (this.y === 0) return null;
9+
return new Box(this.x, this.y - 1);
10+
}
11+
12+
getRightBox() {
13+
if (this.x === 3) return null;
14+
return new Box(this.x + 1, this.y);
15+
}
16+
17+
getBottomBox() {
18+
if (this.y === 3) return null;
19+
return new Box(this.x, this.y + 1);
20+
}
21+
22+
getLeftBox() {
23+
if (this.x === 0) return null;
24+
return new Box(this.x - 1, this.y);
25+
}
26+
27+
getNextdoorBoxes() {
28+
return [
29+
this.getTopBox(),
30+
this.getRightBox(),
31+
this.getBottomBox(),
32+
this.getLeftBox(),
33+
].filter((box) => box !== null);
34+
}
35+
36+
getRandomNextdoorBox() {
37+
const nextdoorBoxes = this.getNextdoorBoxes();
38+
return nextdoorBoxes[Math.floor(Math.random() * nextdoorBoxes.length)];
39+
}
40+
}
41+
42+
const swapBoxes = (grid, box1, box2) => {
43+
const temp = grid[box1.y][box1.x];
44+
grid[box1.y][box1.x] = grid[box2.y][box2.x];
45+
grid[box2.y][box2.x] = temp;
46+
};
47+
48+
const isSolved = (grid) => {
49+
return (
50+
grid[0][0] === 1 &&
51+
grid[0][1] === 2 &&
52+
grid[0][2] === 3 &&
53+
grid[0][3] === 4 &&
54+
grid[1][0] === 5 &&
55+
grid[1][1] === 6 &&
56+
grid[1][2] === 7 &&
57+
grid[1][3] === 8 &&
58+
grid[2][0] === 9 &&
59+
grid[2][1] === 10 &&
60+
grid[2][2] === 11 &&
61+
grid[2][3] === 12 &&
62+
grid[3][0] === 13 &&
63+
grid[3][1] === 14 &&
64+
grid[3][2] === 15 &&
65+
grid[3][3] === 0
66+
);
67+
};
68+
69+
const getRandomGrid = () => {
70+
let grid = [
71+
[1, 2, 3, 4],
72+
[5, 6, 7, 8],
73+
[9, 10, 11, 12],
74+
[13, 14, 15, 0],
75+
];
76+
77+
//Shuffle
78+
let blankBox = new Box(3, 3);
79+
for (let i = 0; i < 1000; i++) {
80+
const randomNextdoorBox = blankBox.getRandomNextdoorBox();
81+
swapBoxes(grid, blankBox, randomNextdoorBox);
82+
blankBox = randomNextdoorBox;
83+
}
84+
85+
if (isSolved(grid)) return getRandomGrid();
86+
return grid;
87+
};
88+
89+
class State {
90+
constructor(grid, move, time, status) {
91+
this.grid = grid;
92+
this.move = move;
93+
this.time = time;
94+
this.status = status;
95+
}
96+
97+
static ready() {
98+
return new State(
99+
[
100+
[0, 0, 0, 0],
101+
[0, 0, 0, 0],
102+
[0, 0, 0, 0],
103+
[0, 0, 0, 0],
104+
],
105+
0,
106+
0,
107+
'ready'
108+
);
109+
}
110+
111+
static start() {
112+
return new State(getRandomGrid(), 0, 0, 'playing');
113+
}
114+
}
115+
116+
class Game {
117+
constructor(state) {
118+
this.state = state;
119+
this.tickId = null;
120+
this.tick = this.tick.bind(this);
121+
this.render();
122+
this.handleClickBox = this.handleClickBox.bind(this);
123+
}
124+
125+
static ready() {
126+
return new Game(State.ready());
127+
}
128+
129+
tick() {
130+
this.setState({
131+
time: this.state.time + 1,
132+
});
133+
}
134+
135+
setState(newState) {
136+
this.state = {
137+
...this.state,
138+
...newState,
139+
};
140+
this.render();
141+
}
142+
143+
handleClickBox(box) {
144+
return function () {
145+
const nextdoorBoxes = box.getNextdoorBoxes();
146+
const blankBox = nextdoorBoxes.find(
147+
(nextdoorBox) => this.state.grid[nextdoorBox.y][nextdoorBox.x] === 0
148+
);
149+
if (blankBox) {
150+
const newGrid = [...this.state.grid];
151+
swapBoxes(newGrid, box, blankBox);
152+
153+
if (isSolved(newGrid)) {
154+
clearInterval(this.tickId);
155+
this.setState({
156+
status: 'won',
157+
grid: newGrid,
158+
move: this.state.move + 1,
159+
});
160+
} else {
161+
this.setState({
162+
grid: newGrid,
163+
move: this.state.move + 1,
164+
});
165+
}
166+
}
167+
}.bind(this);
168+
}
169+
170+
render() {
171+
const { grid, move, time, status } = this.state;
172+
173+
//Render Grid
174+
const newGrid = document.createElement('div');
175+
newGrid.className = 'grid';
176+
177+
for (let i = 0; i < 4; i++) {
178+
for (let j = 0; j < 4; j++) {
179+
const button = document.createElement('button');
180+
181+
if (status === 'playing') {
182+
button.addEventListener('click', this.handleClickBox(new Box(j, i)));
183+
}
184+
185+
button.textContent = grid[i][j] === 0 ? '' : grid[i][j].toString();
186+
newGrid.appendChild(button);
187+
}
188+
}
189+
190+
document.querySelector('.grid').replaceWith(newGrid);
191+
192+
//render button
193+
const newButton = document.createElement('button');
194+
if (status === 'ready') newButton.textContent = 'play';
195+
if (status === 'playing') newButton.textContent = 'reset';
196+
if (status === 'won') newButton.textContent = 'play';
197+
198+
newButton.addEventListener('click', () => {
199+
clearInterval(this.tickId);
200+
this.tickId = setInterval(this.tick, 1000);
201+
this.setState(State.start());
202+
});
203+
204+
document.querySelector('.footer button').replaceWith(newButton);
205+
206+
// Render move
207+
document.getElementById('move').textContent = `Move: ${move}`;
208+
209+
// Render Time
210+
document.getElementById('time').textContent = `Time: ${time}`;
211+
212+
// Render Message
213+
if (status === 'won') {
214+
document.querySelector('.message').textContent = 'You win!';
215+
} else {
216+
document.querySelector('.message').textContent = '';
217+
}
218+
}
219+
}
220+
221+
const GAME = Game.ready();

public/15puzzle.html

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>15 Puzzle game</title>
5+
<link rel="stylesheet" type="text/css" href="../assets/css/15puzzle.css">
6+
</head>
7+
8+
<body>
9+
<div class="game">
10+
<div class="grid">
11+
<button>1</button>
12+
<button>2</button>
13+
<button>3</button>
14+
<button>4</button>
15+
<button>5</button>
16+
<button>6</button>
17+
<button>7</button>
18+
<button>8</button>
19+
<button>9</button>
20+
<button>10</button>
21+
<button>11</button>
22+
<button>12</button>
23+
<button>13</button>
24+
<button>14</button>
25+
<button>15</button>
26+
<button></button>
27+
</div>
28+
29+
<div class="footer">
30+
<button>Play</button>
31+
<span id="move">Move: 100</span>
32+
<span id="time">Time: 100</span>
33+
34+
</div>
35+
36+
</div>
37+
38+
<h1 class="message">You win!</h1>
39+
40+
<script src="../assets/js/15puzzle.js"></script>
41+
</body>
42+
</html>

0 commit comments

Comments
 (0)