vue3开发扫雷游戏,支持调整难度,支持计时
闲来练习练习js,写了个扫雷游戏,直接拿去复制粘到自己本地某个html文件里就能运行,记得把vue加载地址改成线上的~~
有空了可以再加上计分板
运行起来长下面这样
直接上代码
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>扫雷v1.0</title> 7 <!-- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> --> 8 <script src="vue.global-v3.5.12.js"></script> 9 <style> 10 *{ margin:0; padding:0; font-size:14px; color:#333;} 11 body{ padding: 20px; } 12 .container{ 13 background-color: #efefef; 14 padding: 10px; 15 display: flex; 16 } 17 .warper{ 18 padding:1px; 19 background-color: #cfcfcf; 20 } 21 .row{ 22 display: flex; 23 flex-direction: row; 24 } 25 .cell{ 26 width: 30px; 27 text-align: center; 28 height: 30px; 29 line-height: 30px; 30 background-color: #dfdfdf; 31 border: 2px solid #cfcfcf; 32 } 33 .cell:hover{ 34 border-color:rgb(250, 179, 112); 35 background-color: rgb(231, 202, 164); 36 cursor: pointer; 37 } 38 .board{ 39 width: 170px; 40 margin-right: 20px; 41 } 42 .config-item{ 43 padding:10px; 44 } 45 .config-item .btn{ 46 margin-right: 20px; 47 } 48 .cell.clicked-0{ background-color: #f7f7f7; } 49 .cell.clicked-1{ background-color: #fff0f0; } 50 .cell.clicked-2{ background-color: #ffdbdb; } 51 .cell.clicked-3{ background-color: #ffc3c3; } 52 .cell.clicked-4{ background-color: #ffa3a3; } 53 .cell.clicked-5{ background-color: #ff9898; color: white; } 54 .cell.clicked-6{ background-color: #ff6f6f; color: white; } 55 .cell.clicked-7{ background-color: #ff5a5a; color: white; } 56 .cell.clicked-8{ background-color: #ff2424; color: white; } 57 .cell.boom{ background-color: #ffffff; color: rgb(255, 0, 0); font-size:22px;} 58 .cell.flag{ border-color: #fdafaf; } 59 </style> 60 </head> 61 <body> 62 <div class="container" id="app"> 63 <div class="board"> 64 <div class="config-item"> 65 <h3>请点击START开始游戏</h3> 66 <h3>点击RESET重置游戏</h3> 67 </div> 68 <div class="config-item"> 69 <form> 70 <label for="rowsindex">难度</label> 71 <select v-model="currentLevel" @change="handleLevelChange"> 72 <option v-for="(level, index) in levels" :key="index" :value="level">{{ level.name }}</option> 73 </select> 74 </form> 75 </div> 76 <div class="config-item"> 77 用时 <strong>{{ costTime }}</strong> 78 </div> 79 <div class="config-item"> 80 <button class="btn" @click="resetGame">RESET</button> 81 <button class="btn" :disabled="gameStatus == 'start'" @click="startGame">START</button> 82 </div> 83 </div> 84 <div class="warper"> 85 <div class="row" v-for="(row, indexRow) in cells" :key="indexRow"> 86 <div 87 v-for="(column, indexColumn) in cells[indexRow]" 88 :key="indexColumn" 89 :class="'cell '+column.style" 90 @click="handleCellClick(indexRow, indexColumn)" 91 @click.right.prevent="handleCellFlag(indexRow, indexColumn)" 92 > 93 {{ column.text }} 94 </div> 95 </div> 96 </div> 97 </div> 98 99 <script> 100 const { createApp, ref, computed, nextTick } = Vue 101 createApp({ 102 setup() { 103 const boomValue = 332962963; 104 const levels = ref([ 105 { name: '黑铁', rowsindex: 8, columnsindex: 8, boomCount: 6 }, 106 { name: '青铜', rowsindex: 10, columnsindex: 10, boomCount: 11 }, 107 { name: '白银', rowsindex: 14, columnsindex: 14, boomCount: 20 }, 108 { name: '黄金', rowsindex: 16, columnsindex: 16, boomCount: 28 }, 109 { name: '白金', rowsindex: 20, columnsindex: 20, boomCount: 44 }, 110 { name: '钻石', rowsindex: 24, columnsindex: 24, boomCount: 60 }, 111 { name: '大师', rowsindex: 30, columnsindex: 30, boomCount: 95 }, 112 { name: '王者', rowsindex: 36, columnsindex: 36, boomCount: 120 } 113 ]); 114 const currentLevel = ref(levels.value[0]); 115 116 let rowsindex = currentLevel.value.rowsindex; 117 let columnsindex = currentLevel.value.columnsindex; 118 let boomCount = currentLevel.value.boomCount; 119 120 const start = 0; 121 let end = computed(()=>{ 122 return rowsindex*columnsindex-1; 123 }) 124 const gameStatus = ref("stop"); 125 const costTime = ref("00:00:00"); 126 let timer; 127 let startTime = 0; 128 129 const cells = ref([]); 130 let randoms = []; 131 let rightsZero = []; 132 133 const startGame = () => { 134 gameStatus.value = "start"; 135 startTimer(); 136 initGame(); 137 } 138 139 const resetGame = () => { 140 gameStatus.value = "stop"; 141 stopTimer(); 142 resetTimer(); 143 initGame(); 144 } 145 146 const handleLevelChange = () => { 147 console.log(currentLevel.value) 148 resetGame(); 149 } 150 151 const handleCellClick = (i, j) => { 152 if(gameStatus.value!=='start'){ 153 return; 154 } 155 156 if(cells.value[i][j].value != boomValue){ 157 rightsZero = []; 158 clickRights(i, j); 159 for(let i=0; i<rightsZero.length; i++){ 160 let [rowIndex, columnIndex] = getRowIndexColumnIndex(rightsZero[i]); 161 cells.value[rowIndex][columnIndex] = getCellClickedInfo(rowIndex, columnIndex); 162 } 163 ifWin(); 164 }else{ 165 cells.value[i][j] = {style:'boom', clicked: true, text: '⭕️', value: boomValue}; 166 finishGame(); 167 } 168 } 169 170 const handleCellFlag = (i, j) => { 171 if(gameStatus.value!=='start'){ 172 return; 173 } 174 175 let cell = cells.value[i][j]; 176 if(cell.clicked){ 177 return; 178 } 179 if(cell.style === 'flag'){ 180 cells.value[i][j] = {style:'unClicked', clicked: false, text: '', value: cell.value}; 181 }else{ 182 cells.value[i][j] = {style:'flag', clicked: false, text: '🚩', value: cell.value}; 183 ifWin(); 184 } 185 } 186 187 const ifWin = () => { 188 let flagCount = 0; 189 for(let i=0; i<rowsindex; i++){ 190 for(let j=0; j<columnsindex; j++){ 191 if(cells.value[i][j].style === 'flag'){ 192 flagCount++; 193 } 194 } 195 } 196 let rightCount = 0; 197 for(let i=0; i<rowsindex; i++){ 198 for(let j=0; j<columnsindex; j++){ 199 if(cells.value[i][j].value === boomValue && cells.value[i][j].style == 'flag'){ 200 rightCount++; 201 } 202 } 203 } 204 if(flagCount === boomCount && flagCount === rightCount){ 205 winGame(); 206 } 207 } 208 209 const clickRights = (i, j) => { 210 let index = i*rowsindex+j; 211 if(rightsZero.indexOf(index) !== -1){ 212 return; 213 } 214 rightsZero.push(index); 215 if(cells.value[i][j].value === 0){ 216 //上 217 if(i - 1 >= 0 && cells.value[i-1][j].value === 0 && cells.value[i-1][j].clicked === false && cells.value[i-1][j].style !== 'flag'){ 218 clickRights(i-1, j); 219 } 220 //下 221 if(i + 1 < rowsindex && cells.value[i+1][j].value === 0 && cells.value[i+1][j].clicked === false && cells.value[i+1][j].style !== 'flag'){ 222 clickRights(i+1, j); 223 } 224 //左 225 if(j - 1 >= 0 && cells.value[i][j-1].value === 0 && cells.value[i][j-1].clicked === false && cells.value[i][j-1].style !== 'flag'){ 226 clickRights(i, j-1); 227 } 228 //右 229 if(j + 1 < columnsindex && cells.value[i][j+1].value === 0 && cells.value[i][j+1].clicked === false && cells.value[i][j+1].style !== 'flag'){ 230 clickRights(i, j+1); 231 } 232 } 233 } 234 235 const getCellClickedInfo = (row, index) => { 236 let cell = cells.value[row][index]; 237 return {style:'clicked-'+cell.value, clicked: true, text: cell.value === 0 ? '' : cell.value, value: cell.value}; 238 } 239 240 const finishGame = () => { 241 gameStatus.value = 'stop'; 242 setTimeout(()=>{ 243 stopTimer(); 244 alert('Game Over'); 245 showAllBoom(); 246 // initGame(); 247 }, 50) 248 } 249 250 const winGame = () => { 251 gameStatus.value = 'stop'; 252 setTimeout(()=>{ 253 stopTimer(); 254 alert('You Win'); 255 // initGame(); 256 }, 50) 257 } 258 259 const showAllBoom = () => { 260 for(let i=0; i<rowsindex; i++){ 261 for(let j=0; j<columnsindex; j++){ 262 const cell = cells.value[i][j]; 263 if(cell.value === boomValue){ 264 cells.value[i][j] = {style:'boom', clicked: true, text: '⭕️', value: boomValue}; 265 }else{ 266 cells.value[i][j] = {style:'clicked-'+cell.value, clicked: true, text: cell.value === 0 ? '' : cell.value, value: cell.value}; 267 268 } 269 } 270 } 271 } 272 273 const initGame = () => { 274 console.log('initGame') 275 rowsindex = currentLevel.value.rowsindex; 276 columnsindex = currentLevel.value.columnsindex; 277 boomCount = currentLevel.value.boomCount; 278 279 cells.value = []; 280 randoms = []; 281 for(let i=0; i<rowsindex; i++){ 282 cells.value[i] = []; 283 for(let j=0; j<columnsindex; j++){ 284 cells.value[i][j] = {style:'unClicked', clicked: false, text: '', value: 0}; 285 } 286 } 287 for(let i=0; i<boomCount; i++){ 288 getUniqueRandoms(randoms); 289 } 290 for(let i=0; i<randoms.length; i++){ 291 let [rowIndex, columnIndex] = getRowIndexColumnIndex(randoms[i]); 292 cells.value[rowIndex][columnIndex].value = boomValue; 293 } 294 for(let i=0; i<rowsindex; i++){ 295 for(let j=0; j<columnsindex; j++){ 296 if(cells.value[i][j].value !== boomValue){ 297 let count = 0; 298 for(let x=i-1; x<=i+1; x++){ 299 for(let y=j-1; y<=j+1; y++){ 300 if(x>=0 && x<rowsindex && y>=0 && y<columnsindex && cells.value[x][y].value === boomValue){ 301 count++; 302 } 303 } 304 } 305 cells.value[i][j].value = count; 306 } 307 } 308 } 309 } 310 311 const getRandoms = () => { 312 return parseInt(Math.random()*(start+1-end.value) + end.value); 313 } 314 315 const getUniqueRandoms = (randoms) => { 316 let random = getRandoms(); 317 if(randoms.indexOf(random) === -1){ 318 randoms.push(random); 319 }else{ 320 getUniqueRandoms(randoms); 321 } 322 } 323 324 const getRowIndexColumnIndex = (index) => { 325 let rowIndex = parseInt(index / columnsindex); 326 let columnIndex = index % columnsindex; 327 return [rowIndex, columnIndex]; 328 } 329 330 const startTimer = () => { 331 startTime = new Date().getTime(); 332 timer = setInterval(()=>{ 333 costTime.value = getCostTime(); 334 }, 1000) 335 } 336 337 const stopTimer = () => { 338 clearInterval(timer); 339 } 340 341 const resetTimer = () => { 342 startTime = 0; 343 costTime.value = '00:00:00'; 344 } 345 346 const getCostTime = () => { 347 let now = new Date().getTime(); 348 let diff = parseInt((now - startTime) / 1000); 349 let h = parseInt(diff / 3600); 350 let hStr = h < 10? '0'+h : h; 351 let m = parseInt((diff % 3600) / 60); 352 let mStr = m < 10? '0'+m : m; 353 let s = diff % 60; 354 let sStr = s < 10? '0'+s : s; 355 return hStr+':'+mStr+':'+sStr; 356 } 357 358 initGame(); 359 360 return { 361 cells, 362 costTime, 363 gameStatus, 364 rowsindex, 365 columnsindex, 366 levels, 367 currentLevel, 368 handleCellClick, 369 handleCellFlag, 370 resetGame, 371 startGame, 372 handleLevelChange 373 } 374 } 375 }).mount('#app') 376 </script> 377 </body> 378 </html>