1.简介
八皇后估计大家都知道是什么问题,就不细述了(不知道的百度吧).
说是显示版,主要还是依赖于vue,所以写代码的时候显示和内在逻辑都是区分开的,所以到也没比非显示版的难到哪儿去,唯一的好处是可以自己先试试,更加直观.
此前接触八皇后问题,但真正开始思考这个还是今天下午开始的,写代码的时候着实遇到了不少坑,不管是不是自己挖的.
写完之后,又看了看之前写的,发现确实可以再优化优化,不过今天就算了,太累了.
2.思路示意图
见下图
3.代码
见下(很多没用的代码,但还是那个原因,太累了,懒得再整理了),有一点比较好的是,这个代码直接复制粘贴就是能用.
换句话说,直接把这个代码拷到一个txt文档里面,然后改名为html,再用浏览器打开就行了.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<title>首页</title>
<style>
#main_zone .table{margin:10px;width: 300px;}
#main_zone .table td{width: 50px;}
#main_zone .table td input{width:30px;border:none;background-color:transparent;text-align:center;}
.btn-cells{margin: 10px;}
.print-log{margin: 10px;height: 150px;width:400px;overflow: auto;}
</style>
</head>
<body>
<main id="main_zone">
<table class="table table-striped table-bordered table-hover">
<tbody>
<tr v-for="(arrFirst,indexFirst) in mainData" >
<td v-for="(valSecond,indexSecond) in arrFirst"
:style="tdStyle(indexFirst,indexSecond)"
@click="toggleQueen(indexFirst,indexSecond)">
{{arrFirst[indexSecond]}}
</td>
</tr>
</tbody>
</table>
<div class="btn-cells">
<input type="button" value="重置" class="btn btn-primary" @click="resetChoice()" />
<input type="button" value="计算" class="btn btn-success" @click="calcChoice()" />
<input type="button" value="其他" class="btn btn-secondary" @click="otherTest()" />
</div>
<div class="print-log">
<p v-for="(log,index) in logs">{{log}}</p>
</div>
</main>
<script>
//为了提升速度可以考虑下个bootstrap插件放到本地:
//<link href="../../css/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
var vueMain=new Vue({
el:'#main_zone',
data:{
mainData:[
[11,12,13,14,15,16,17,18],
[21,22,23,24,25,26,27,28],
[31,32,33,34,35,36,37,38],
[41,42,43,44,45,46,47,48],
[51,52,53,54,55,56,57,58],
[61,62,63,64,65,66,67,68],
[71,72,73,74,75,76,77,78],
[81,82,83,84,85,86,87,88]
],
choiceData:[],//记录点击的节点记录
choiceLog:[],//接下来可选择节点集合的日志,应始终比choiceData的length大1(最开始的一次是全部节点)
logs:[]//打印日志信息,暂时不用管
},
created:function(){
//1.初始化环节,不会改变
var arr0=[];
for(var i=0;i<8;i++){
for(var j=0;j<8;j++){
arr0.push({x:i,y:j});
}
}
//console.log(JSON.parse(JSON.stringify(arr0)));
this.choiceLog.push(arr0);
},
computed:{
},
methods:{
//当前级别
curLevel:function(){
return this.choiceData.length;
},
//td样式
tdStyle:function(x,y){
var okQueenBc='#5cb85c';//绿色
var fbQueenBc='#d9534f';//红色
var fbOtherBc='#f0ad4e';//橙色
var okOtherBc='white';//白色
var bc=okOtherBc;
if(this.hasQueen(x,y)&&!this.forbidArea(x,y)){
bc=okQueenBc;
}else if(this.hasQueen(x,y)&&this.forbidArea(x,y)){
bc=fbQueenBc;
}else if(!this.hasQueen(x,y)&&this.forbidArea(x,y)){
bc=fbOtherBc;
}
return {
'background-color':bc,
cursor:'pointer'
};
},
//是否有queen
hasQueen:function(x,y){
return this.choiceData.some(function(eachChoice){
return eachChoice.x==x&&eachChoice.y==y;
});
},
//禁止区域
forbidArea:function(x,y){
return this.choiceData.some(function(eachChoice){
if(eachChoice.x==x&&eachChoice.y==y){
return false;
}else if(eachChoice.x==x||eachChoice.y==y){
return true;
}else if(eachChoice.x-x==eachChoice.y-y||eachChoice.x-x==y-eachChoice.y){
return true;
}
});
},
//有被禁用的queen
hasFbQueen:function(){
var that=this;
return this.choiceData.some(function(choice){
return that.hasQueen(choice.x,choice.y)&&that.forbidArea(choice.x,choice.y)
});
},
//有其他ok的(暂不考虑非0情况了,考虑的话可以一定程度提升计算效率,但逻辑就更复杂了)
hasOkOther:function(logIndex){
logIndex=logIndex||0;
var that=this;
return this.choiceLog[logIndex].some(function(eachNode){
return !(that.hasQueen(eachNode.x,eachNode.y)||that.forbidArea(eachNode.x,eachNode.y));
});
},
//查找某个级别节点的下一个节点,如果没有则返回null
calcNextNode:function(nodeLv){
var thisNodeLog=this.choiceLog[nodeLv];
if(nodeLv==this.curLevel()){//寻找下一节点
if(thisNodeLog.length>0){
return thisNodeLog[0];
}else{
return null;
}
}
var thisNode=this.choiceData[nodeLv];
var thisNodeIndex=parseInt(this.calcNodeIndex(thisNode,thisNodeLog));
if(thisNodeIndex==-1||thisNodeIndex>=thisNodeLog.length-1){
//当查找不到该节点或该节点已经是该级别节点集合的最后一个节点时,返回null
return null;
}else{
// console.log(JSON.stringify(thisNodeLog));
// console.log(thisNodeIndex+1);
return thisNodeLog[thisNodeIndex+1];
}
},
//获取节点的index
calcNodeIndex:function(thisNode,nodeLog){
for(var i in nodeLog){
var eachNode=nodeLog[i];
if(eachNode.x==thisNode.x&&eachNode.y==thisNode.y){
return i;
}
}
return -1;
},
//根据choiceData计算其他ok节点
calcOtherOkNode:function(){
var that=this;
return this.choiceLog[0].filter(function(eachNode){
return !that.hasQueen(eachNode.x,eachNode.y)&&!that.forbidArea(eachNode.x,eachNode.y);
});
},
//翻转queen(点击事件)
toggleQueen:function(x,y){
if(this.hasQueen(x,y)){
this.delQueen(x,y);
}else{
this.setQueen(x,y);
}
var that=this;
//this.logs.push('当前皇后数:'+this.choiceData.length+(that.hasFbQueen()?',已有冲突!':',没有冲突!'));
},
//设置queen
setQueen:function(x,y){
this.choiceData.push({
x:x,
y:y
});
},
//移除queen
delQueen:function(x,y){
this.choiceData=this.choiceData.filter(function(eachNode){
return !(eachNode.x==x&&eachNode.y==y);
})
},
//试着增加queen,并更新后续日志
tryAddQueen:function(newNode,newLevel){
var curLevel=this.choiceData.length;//当前已经达到的级别
newLevel=newLevel||curLevel;//默认增加下一级别
//1.不考虑检查节点,同样是为了减少逻辑复杂度考虑(否则需要考虑历史情况而非实时情况)
//2.如果级别存在差异,则削data和log
for(var i=0;i<curLevel-newLevel;i++){
this.choiceData.pop();
this.choiceLog.pop();
}
//3.增加数据与日志
this.choiceData.push(newNode);
this.choiceLog.push(this.calcOtherOkNode());
// console.log('try log length:'+this.choiceData.length+','+this.choiceLog.length);
},
//重置
resetChoice:function(){
this.choiceData=[];
this.logs=[];
},
//回滚节点设置(只能回滚以前的级别,否则会有问题)
rollbackNode:function(nodeLevel){
if(nodeLevel<0){
console.error('node level <0 !');
return false;
}
// console.log('rollback level:'+nodeLevel);
var nextNode=this.calcNextNode(nodeLevel);
// console.log('next node:'+JSON.stringify(nextNode));
if(nextNode==null||typeof(nextNode)==='undefined'){
this.rollbackNode(nodeLevel-1);
}else{
this.tryAddQueen(nextNode,nodeLevel);
this.autoAddNode(8);
}
},
//自动根据级别增加节点(可以则返回true,否则返回false)
autoAddNode:function(nodeLevel){
//此处需区分是开始就没有,还是到底了
// if(nodeLv=this.curLevel()+1){//寻找下一节点
// if(thisNodeLog.length>0){
// return thisNodeLog[0];
// }else{
// return null;
// }
// }
var nextNode=this.calcNextNode(this.curLevel());
// console.log('next node:'+JSON.stringify(nextNode));
if(nextNode==null||typeof(nextNode)==='undefined'){
if(nodeLevel<0){
console.warning('node level =0 ?');
return;
}
if(!this.rollbackNode(this.curLevel()-1)){
return ;
}
}else{
this.tryAddQueen(nextNode);
}
var that=this;
if(this.curLevel()<nodeLevel){
// console.log('curLevel:'+this.curLevel());
setTimeout(function(){
that.autoAddNode(nodeLevel);
},10);
}
},
//查找
calcChoice:function(){
//1.增加1级queen
this.autoAddNode(8);
/*
* function dosth(){
* if(ok){
* add
* }else{
* rollback
* }
* }
*/
// this.tryAddQueen(this.choiceLog[0][0]);
// if(this.choiceLog[1].length>0){
// this.tryAddQueen(this.choiceLog[1][0]);
// if(this.choiceLog[2].length>0){
// this.tryAddQueen(this.choiceLog[2][0]);
// }else{
// this.tryAddQueen(this.choiceLog[1][1],1);
// }
// }else{
// this.tryAddQueen(this.choiceLog[0][1],0);
// }
},
//其他测试
otherTest:function(){
//this.toggleQueen()
console.log(this.hasOkOther());
}
},
});
</script>
</body>
</html>
4.结果
不知道八皇后问题一共有几个非对称解,我这里试出来的是这个: