八皇后小游戏规则与效果预览
规则:
效果预览:
用字母 Q 代表皇后,横/竖/撇/捺处均无皇后可放置皇后,在横/竖/撇/捺有皇后处放置皇后会弹出提示“该位置不能放置皇后!”。
实现步骤
之前创建了项目,笔记:https://www.cnblogs.com/xiaoxuStudy/p/12663218.html
当前目录结构:
(注意:组件命名用大驼峰命名法。)
在 EightQueen.vue 中写模板。
EightQueen.vue:
<template> <div> <div class="title">八皇后问题</div> </div> </template>
在 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>
此时,页面表现:
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>
页面表现:
(页面太长,截图头跟尾,输出了 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>
页面表现(总共有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>
页面表现(总共有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>
页面表现:
之前设置了.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>
页面表现:
设置棋盘居中。
<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>
页面表现:
接着,实现鼠标点击棋盘格放置皇后,这里字母 “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>
页面表现(说明:下图是用相机拍摄的,截图显示不出光标):
实现为每个格子添加点击事件。
第 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>
页面表现:
在棋盘格上随意点击几个格子,点击的格子里出现了“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>
页面表现:
如果在皇后的同一横、竖、撇、捺上放置皇后,会弹出提示
放下八个皇后的成功例子
至此,已经简单完成了一个八皇后小游戏。