用 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

页面表现:

如果在皇后的同一横、竖、撇、捺上放置皇后,会弹出提示

 放下八个皇后的成功例子

至此,已经简单完成了一个八皇后小游戏。

原文地址:https://www.cnblogs.com/xiaoxuStudy/p/13216592.html