1. 程式人生 > 實用技巧 >用 Vue 簡單實現一個八皇后小遊戲

用 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">
                <
div 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>
View Code

頁面表現:

(頁面太長,截圖頭跟尾,輸出了 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

頁面表現:

如果在皇后的同一橫、豎、撇、捺上放置皇后,會彈出提示

至此,已經簡單完成了一個八皇后小遊戲。