用 Vue 簡單實現一個八皇后小游戲


八皇后小游戲規則與效果預覽

規則:

效果預覽:

  用字母 Q 代表皇后,橫/豎/撇/捺處均無皇后可放置皇后,在橫/豎/撇/捺有皇后處放置皇后會彈出提示“該位置不能放置皇后!”。

實現步驟

之前創建了項目,筆記: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="introduction">
            <div class="title">八皇后小游戲</div>
            <div class="rule">規則:在8x8的棋盤上放置8個皇后,即任兩個皇后都不能處於同一條橫線、縱線或者斜線上。</div>
        </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.length; i++){
                if(this.grids[i][cindex].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[y][i].ok){
                    return false;
                }y
            }
            //
            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;
                }
            }

            return 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;
    }
    .title{
        font-size: 30px;
        padding-bottom: 20px;
    }
    .introduction{
        text-align: center;
        padding: 30px 0;
    }
</style>
App.vue

 

頁面表現:

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

 

 放下八個皇后的成功例子

 

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM