拼圖遊戲js
實現演算法:
1. JavaScript動態生成拼圖:通過生成16個div,且除最後一個div不使用背景圖片以外,其他div都設定拼圖圖片為背景。然後通過調整background-position來實現效果圖中的拼圖效果;
2. 打亂拼圖:定義一個大小為15的陣列,且其中的值依次為1-15,然後通過定義一個比較函式,利用sort(cmp)實現隨機打亂,也就生成了一個隨機陣列,然後根據這個陣列改變類名,定義拼圖分塊的位置;
function cmp() {
return 0.5-Math.random();
}
1
2
3
但是,生成的拼圖不一定能恢復到原來的樣子,必須進行驗證,如果沒有通過驗證,則需要重新生成隨機陣列,對於這個驗證,可以詳見這篇博文: 15迷問題的證明(15 puzzle)
3 .非常重要:id用來代表圖片的特定位置,如第一行第一個,第二行第三個等,然後通過類來定義left與top屬性,即可以通過改變類名來實現元素的移動。
4. 元素的移動:需要判斷點選的方格是否與空方格相鄰,可以通過offsetTop與offsetLeft屬性進行比較判斷,然後通過交換被點選方格與空白方格的類名來實現;
5. 檢查是否結束遊戲:生成拼圖時,id與position是一一對應的,因此只需要判斷所有元素是否都一一對應,如果都一一對應,遊戲結束,否則,繼續。
程式碼如下:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Fifteen_puzzle</title>
<link rel="stylesheet" type="text/css" href="CSS/puzzle.css">
<script type="text/javascript" src="JS/puzzle.js"></script>
</head>
<body>
<h1> 拼圖遊戲 </h1>
<div id="result"></div>
<div id="picture"></div>
<div id="restart">重新開始</div>
<div id="change_image">更換圖片</div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CSS:
/* 大布局定位 */
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
overflow: hidden;
}
/* 元素居中 */
body {
text-align: center;
}
/* 結果顯示 */
#result {
width: 200px;
height: 50px;
font-size: 40px;
color: red;
font-family: Courier, "Andale Mono", Arial, sans-serif;
}
#picture, #restart, #result, #change_image {
margin-top: 30px;
margin-left: auto;
margin-right: auto;
}
/* 拼圖容器 */
#picture {
position: relative;
width: 350px;
height: 355px;
margin-bottom: 30px;
}
/* 重新開始按鈕 */
#restart, #change_image {
cursor: pointer;
width: 120px;
height: 40px;
line-height: 40px;
background-color: #4286F5;
box-shadow: 3px 4px 15px black;
color: white;
font-size: 18px;
opacity: 0.7;
}
#restart:hover, #change_image:hover {
opacity: 1;
transform: scale(1.1);
}
/* 圖片分塊 */
.picture_part0 {
background: url("../Image/1.jpg") no-repeat;
}
.picture_part1 {
background: url("../Image/2.jpg") no-repeat;
}
.picture_part0, .picture_part1 {
transition-duration: 0.2s;
position: absolute;
border: solid 1px #969696;
display: inline-block;
width: 83px;
height: 83px;
margin-bottom: -4px;
opacity: 0.9;
}
.picture_part:hover {
opacity: 1;
transform: scale(1.05);
}
/* 類名 position_x 代表了位置 */
.position_1, .position_2, .position_3, .position_4 {
top: 0px;
}
.position_5, .position_6, .position_7, .position_8 {
top: 85px;
}
.position_9, .position_10, .position_11, .position_12 {
top: 170px;
}
.position_13, .position_14, .position_15, .position_16 {
top: 255px;
}
.position_1, .position_5, .position_9, .position_13 {
left: 0px;
}
.position_2, .position_6, .position_10, .position_14 {
left: 85px;
}
.position_3, .position_7, .position_11, .position_15 {
left: 170px;
}
.position_4, .position_8, .position_12, .position_16 {
left: 255px;
}
/* id _position_x 代表每一個分塊 */
#_position_1 {
background-position: 0px 0px;
}
#_position_2 {
background-position: -85px 0px;
}
#_position_3 {
background-position: -169px 0px;
}
#_position_4 {
background-position: -253px 0px;
}
#_position_5 {
background-position: 0px -84px;
}
#_position_6 {
background-position: -85px -84px;
}
#_position_7 {
background-position: -169px -84px;
}
#_position_8 {
background-position: -253px -84px;
}
#_position_9 {
background-position: 0px -168px;
}
#_position_10 {
background-position: -85px -168px;
}
#_position_11 {
background-position: -169px -168px;
}
#_position_12 {
background-position: -253px -168px;
}
#_position_13 {
background-position: 0px -252px;
}
#_position_14 {
background-position: -85px -252px;
}
#_position_15 {
background-position: -169px -252px;
}
#_position_16 {
opacity: 0;
background-position: -253px -252px;
background-image: none;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
JavaScript:
/*
專案:拼圖遊戲
*/
window.onload = function() {
create_pic();
document.getElementById("restart").addEventListener("click", random_pos);
document.getElementById("change_image").addEventListener("click", change_img);
};
/* 檢查產生的隨機數列是否是合理的,因為有可能出現恢復不到原圖的情況 */
function check_random_isValid() {
var count = 0;
for (var i = 0; i < 16; i++) {
for (var j = i+1; j < 16; j++) {
if (random_arr[j] < random_arr[i]) {
count++;
}
}
}
return count%2===0;
}
/* 產生拼圖 */
function create_pic() {
picture = document.getElementById("picture");
for (var i = 1; i <= 16; i++) {
var part = document.createElement("div");
part.addEventListener("click", pic_move);
part.className = "picture_part" + count + " position_"+i;
picture.appendChild(part);
part.id = "_position_"+i;
}
}
/* 改變圖片 */
var count = 0;
function change_img(event) {
if (count < 1) count++;
else return;
for (var i = 0; i < 16; i++) {
picture.childNodes[i].className += " picture_part" + count;
}
}
/* 產生隨機數列定義位置 */
function random_pos(event) {
document.getElementById("result").innerText = "";
/* 產生隨機數列前先將拼圖塊對應的位置復位 */
for (var k = 1; k <= 16; k++) {
document.getElementById("_position_"+k).className="picture_part"+count+" position_"+k;
}
var part = document.getElementById("picture").childNodes;
random_arr = [];
for (var j = 0; j < 15; j++) {
random_arr[j] = j+1;
}
/* 利用sort和cmp進行隨機打散 */
function cmp() { return 0.5-Math.random(); }
while(1) {
random_arr.sort(cmp);
if (check_random_isValid()) {
break;
}
}
/* 通過更改類名來改變位置 */
for (var i = 0; i < 15; i++) {
part[i].className = "picture_part" + count + " position_" + random_arr[i];
}
}
/* 點選圖片觸發的事件處理器 */
function pic_move(event) {
var blank_pic_offset = document.getElementById("_position_16");
var blank_pic_offset_top = blank_pic_offset.offsetTop;
var blank_pic_offset_left = blank_pic_offset.offsetLeft;
var _offset_top = this.offsetTop;
var _offset_left = this.offsetLeft;
/* 判斷點選的圖片塊是否與空格塊相鄰 */
if ((Math.abs(blank_pic_offset_top-_offset_top) == 85 && blank_pic_offset_left == _offset_left) ||
(Math.abs(blank_pic_offset_left-_offset_left) == 85 && blank_pic_offset_top == _offset_top)) {
var str = blank_pic_offset.className;
blank_pic_offset.className = this.className;
this.className = str;
check(); // 檢查是否還原原圖
}
}
/* 檢查是否還原原圖 */
function check() {
for (var i = 1; i <= 16; i++) {
var item = document.getElementById("_position_"+i);
if (item.className != "picture_part" + count +" position_"+i &&
item.className != "picture_part0" + " position_" + i + " picture_part1") {
document.getElementById("result").innerText = "Continue...";
return;
}
}
document.getElementById("result").innerText = "You Win!";
}