用 Vue 簡單實現一個八皇后小遊戲
八皇后小遊戲規則與效果預覽
效果預覽:
實現步驟
之前建立了專案,筆記:https://www.cnblogs.com/xiaoxuStudy/p/12663218.html
當前目錄結構:
(注意:元件命名用大駝峰命名法。)
在 EightQueen.vue 中寫模板。
EightQueen.vue:
<template> <div> <div class="title">八皇后問題</div> </div> </template>View Code
在 App.vue 中區域性註冊、引入、使用 EightQueen.vue 元件。
App.vue:
<template> <div id="app"> <eight-queen /> </div> </template> <script> import EightQueen from "./components/EightQueen"; export default { name: "app", components: { EightQueen } } </script>View Code
此時,頁面表現:
EightQueen.vue :
棋盤格是一個 8x8 的矩陣,先寫八橫
先寫第一行的八縱
上面這樣寫不好。通過 v-if 去遍歷資料結構來寫比較好。
下面是改進:
<template> <div> <div class="title">八皇后問題</div> <div class="gird"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <View Codediv class="cell" v-for="cell in row" :key="cell.key"> {{ `${cell.key}` }} </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } } } </script>
頁面表現:
(頁面太長,截圖頭跟尾,輸出了 64 個,key-0 到 key-63,對應八行八縱的 64 個格子)
檢視控制檯:
程式碼說明:
定義資料結構,通過函式 data 返回資料物件 grids
生成物件 girds,先生成一個八橫的資料結構,newArray(8) 是生成了一個 length 為 8 的空陣列。然後對八橫的資料結構進行填充,給該陣列用 fill(1) 方法是將該空陣列的 8 個元素替換為 1,也就是陣列現在為 [1, 1, 1, 1, 1, 1, 1, 1]。給八橫的資料結構填充八縱,給該陣列用 map 方法,map 方法的引數是一個函式,該函式的引數 _ 是當前值也就是 1 ,引數 r 是當前索引值,該函式作用於 [1, 1, 1, 1, 1, 1, 1, 1] 的每一個元素,因為要實現棋盤格八橫八縱,所以每一橫都對應八縱,所以給每一橫返回一個八縱的資料結構,每一縱的資料結構返回資料物件 key 跟 ok,key 代表單元格唯一的鍵值,ok 代表是否被點選或者說是否被皇后佔領。grids 是一個包含 8 個元素的陣列,每一個元素都是一個 8 個元素的陣列。
方便理解,花了一個圖,圖中每小格對應的 key 值是 r*8 + c
第 6 行:使用 v-for 去遍歷每一橫,grids 是陣列對應程式碼第 16 行,row 是當前值,每一個 row 都是一個包含 8 個元素的陣列,r_index 是當前索引值,需要繫結 key 值來幫助引擎更好地渲染 dom 節點。
第 7 行:使用 v-for 去遍歷一橫中的每一縱,row 是一個包含 8 個元素的陣列,cell 是當前值對應程式碼第 18-21 行,cell 包含資料物件 key 與 ok。
第 8 行:寫這條語句主要是為了看看頁面效果。輸出 cell.key ,整個遍歷完後輸出了 key-0 到 key-63 總共 64 個元素,對應棋盤格中八橫八縱的每一個單元格。
接下來寫樣式。
使用 scoped 防止元件樣式全域性汙染。(scoped相關筆記:https://www.cnblogs.com/xiaoxuStudy/p/13235418.html#three)
一個 class 為 cell 的 div 對應 8x8 棋盤格中的一個格子。設定一個小格子寬高為 50 px、水平垂直居中、背景顏色為 #999。
<template> <div> <div class="title">八皇后問題</div> <div class="gird"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <div class="cell" v-for="cell in row" :key="cell.key"> </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } } } </script> <style scoped> .cell{ width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: #999; } </style>View Code
頁面表現(總共有64個格子):
棋盤格相鄰格子顏色不同。:nth-child(2n) 表示對索引是 2 的倍數 class 為 cell 的 div 元素指定背景顏色。
<template> <div> <div class="title">八皇后問題</div> <div class="gird"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <div class="cell" v-for="cell in row" :key="cell.key"> </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } } } </script> <style scoped> .cell{ width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: #999; } .cell:nth-child(2n){ background: #efefef; } </style>View Code
頁面表現(總共有64個格子):
設定樣式讓格子按照八行顯示。一個 class 為 row 的 div 相當於棋盤格的一行,一共有八個 class 為 row 的 div,設定 class 為 row 的 div 高 50px 寬 400px(即8x50px),因為一行有八個格子。
class 為 cell 的 div 是 class 為 row 的 div 子盒子,子盒子設定了浮動可能會導致高度塌陷所以需要在父盒子設定 display:flow-root 觸發 BFC。
<template> <div> <div class="title">八皇后問題</div> <div class="gird"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <div class="cell" v-for="cell in row" :key="cell.key"> </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } } } </script> <style scoped> .cell{ width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: #999; float: left; } .cell:nth-child(2n){ background: #efefef; } .row{ height: 50px; width: 400px; display: flow-root; } </style>View Code
頁面表現:
之前設定了.cell:nth-child(2n){background:#efefef;},所以每一縱顏色都是相同的。
設定樣式實現每個格子上下左右相鄰顏色均不同。設定索引是 2 的倍數的行的樣式即可。
第50-52行:索引是 2 的倍數 class 為 row 的 div 元素且索引是 2 的倍數 class 為 cell 的 div 元素背景顏色為 #999
第53-55行:索引是 2 的倍數 class 為 row 的 div 元素且索引是 2n-1 的倍數 class 為 cell 的 div 元素背景顏色為 #efefef
<template> <div> <div class="title">八皇后問題</div> <div class="gird"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <div class="cell" v-for="cell in row" :key="cell.key"> </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } } } </script> <style scoped> .cell{ width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: #999; float: left; } .cell:nth-child(2n){ background: #efefef; } .row{ height: 50px; width: 400px; display: flow-root; } .row:nth-child(2n) .cell:nth-child(2n){ background: #999; } .row:nth-child(2n) .cell:nth-child(2n-1){ background: #efefef; } </style>View Code
頁面表現:
設定棋盤居中。
<template> <div> <div class="title">八皇后問題</div> <div class="grid"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <div class="cell" v-for="cell in row" :key="cell.key"> </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } } } </script> <style scoped> .grid{ width: 400px; margin: 0 auto; } .cell{ width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: #999; float: left; } .cell:nth-child(2n){ background: #efefef; } .row{ height: 50px; width: 400px; display: flow-root; } .row:nth-child(2n) .cell:nth-child(2n){ background: #999; } .row:nth-child(2n) .cell:nth-child(2n-1){ background: #efefef; } </style>View Code
頁面表現:
接著,實現滑鼠點選棋盤格放置皇后,這裡字母 “Q” 代表皇后。
實現游標移動到棋盤格內時,游標為“一隻手”。
<template> <div> <div class="title">八皇后問題</div> <div class="grid"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <div class="cell" v-for="cell in row" :key="cell.key"> </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } } } </script> <style scoped> .grid{ width: 400px; margin: 0 auto; } .cell{ width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: #999; float: left; cursor: pointer; } .cell:nth-child(2n){ background: #efefef; } .row{ height: 50px; width: 400px; display: flow-root; } .row:nth-child(2n) .cell:nth-child(2n){ background: #999; } .row:nth-child(2n) .cell:nth-child(2n-1){ background: #efefef; } </style>View Code
頁面表現(說明:下圖是用相機拍攝的,截圖顯示不出游標):
實現為每個格子新增點選事件。
第 8 行: 新增一個 v-if 判斷 cell.ok (對應第20行) 是否為 true,如果為 true 則顯示皇后“Q”
第 7 行:使用 @click 新增點選事件,使用 .stop 修飾符阻止預設冒泡,新增一個 select 函式。棋盤格八橫八縱,r_index 是遍歷橫時的當前索引,c_index 是遍歷縱時的當前索引。給 select 函式傳入 r_index 跟 c_index 作為引數。當點選棋盤格格子時觸發點選事件執行 select 函式。
第 32 行: 在 methods 裡新增 select 函式。grids[rindex][cindex]對應棋盤中的一個格子,select 函式的作用是設定 ok (對應20行)為true,ok 為 true 後 "Q" 就顯示出來(對應第 8 行)。
<template> <div> <div class="title">八皇后問題</div> <div class="grid"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <div class="cell" v-for="(cell, c_index) in row" :key="cell.key" @click.stop="select(r_index, c_index)"> <div v-if="cell.ok">Q</div> </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } }, methods: { select(rindex, cindex) { this.grids[rindex][cindex].ok = true; } } } </script> <style scoped> .grid{ width: 400px; margin: 0 auto; } .cell{ width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: #999; float: left; cursor: pointer; } .cell:nth-child(2n){ background: #efefef; } .row{ height: 50px; width: 400px; display: flow-root; } .row:nth-child(2n) .cell:nth-child(2n){ background: #999; } .row:nth-child(2n) .cell:nth-child(2n-1){ background: #efefef; } </style>View Code
頁面表現:
在棋盤格上隨意點選幾個格子,點選的格子裡出現了“Q”
八皇后小遊戲要滿足一個皇后的同一橫、豎、撇、捺都不能放置皇后。
可以先把每個小格子的座標都顯示出來,方便找出規律,進行邏輯運算。
根據座標,寫 validate 方法,實現一個皇后的同一橫、豎、撇、捺都不能放置皇后。
<template> <div> <div class="title">八皇后問題</div> <div class="grid"> <div class="row" v-for="(row, r_index) in grids" :key="r_index"> <div class="cell" v-for="(cell, c_index) in row" :key="cell.key" @click.stop="select(r_index, c_index)"> <div v-if="cell.ok">Q</div> </div> </div> </div> </div> </template> <script> const grids = new Array(8).fill(1).map((_, r) => { return new Array(8).fill(1).map((_, c) => { return { key: `key-${r*8 + c}`, ok: false } }) }) export default { data(){ return{ grids } }, methods: { select(rindex, cindex) { if(this.validate(rindex, cindex)){ this.grids[rindex][cindex].ok = !this.grids[rindex][cindex].ok; }else{ alert('該位置不能放置皇后!'); } }, validate(rindex, cindex){ //橫 for(let i=0; i<this.grids[rindex].length; i++){ if(this.grids[rindex][i].ok){ return false; } } //豎 for(let i=0; i<this.grids[cindex].length; i++){ if(this.grids[i][cindex]){ return false; } } //撇 for(let i=0; i<this.grids[0].length; i++){ let y = rindex + cindex - i; if( y>0 && y<this.grids.length && this.grids[y][i].ok){ return false; } } //捺 for(let i=0; i<this.grids[0].length; i++){ let y = rindex - cindex + i; if( y>0 && y<this.grids.length && this.grids[i][y].ok ){ return false; } } } } } </script> <style scoped> .grid{ width: 400px; margin: 0 auto; } .cell{ width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: #999; float: left; cursor: pointer; } .cell:nth-child(2n){ background: #efefef; } .row{ height: 50px; width: 400px; display: flow-root; } .row:nth-child(2n) .cell:nth-child(2n){ background: #999; } .row:nth-child(2n) .cell:nth-child(2n-1){ background: #efefef; } </style>App.vue
頁面表現:
如果在皇后的同一橫、豎、撇、捺上放置皇后,會彈出提示
至此,已經簡單完成了一個八皇后小遊戲。