本帖最后由 a38758720 于 2021-3-20 23:48 编辑
主要是昨天的帖子https://www.52pojie.cn/thread-1395407-1-1.html有朋友有点兴趣我就再详细的过一遍整个流程,而且还有朋友想逗猫 @蓦留 @yagiza (重新试试怎么艾特)
就改一下源码,让猫彻底没路可走的时候胜利
老样子,随便输入一个url让52走404页面 https://www.52pojie.cn/hxxx
说了,今天不作弊,就不作弊,今天逗逗猫。
主要看注释啊朋友们
进入今天的正题。
今天不走控制台了,为了方便直接override js文件,也方便加注释啥的,参考https://www.52pojie.cn/thread-1394509-1-1.html这个老哥的做法,我不再写一遍了。
搜索“你赢了”发现有两个地方出现了这个字眼,一个是switch case,只是为了统一页面显示的字样而已,这个不重要,重要的以下这个
[JavaScript] 纯文本查看 复制代码 var n = this.getBlock(t, e);
return n ? n.isWall ? (this.setStatusText(f.default("点击位置已经是墙了,禁止点击")),
!1) : this.cat.i === t && this.cat.j === e ? (this.setStatusText(f.default("点击位置是猫当前位置,禁止点击")),
!1) : (n.isWall = !0,
this.cat.isCaught() ? (this.setStatusText(f.default("猫已经无路可走,你赢了")),
this.state = i.WIN,
!1) : (this.setStatusText(f.default("您点击了 ") + "(" + t + ", " + e + ")"),
this.cat.step() || (this.setStatusText(f.default("猫认输,你赢了!")),
this.state = i.WIN),
!0)) : (this.setStatusText(f.default("代码错误,当前位置不存在")),
!1)
昨天是通过直接重写isCaught方法实现作弊直接胜利。我们今天分析一下这个方法干了啥。
[JavaScript] 纯文本查看 复制代码 isCaught = function() {
var t = this;
//获取当前点所有的邻居,并判断所有邻居是否都是墙,如果都是墙返回true,有一个不是墙则返回false
//昨天相当于直接绕开了这层判断,不管邻居是不是墙,都是true
return !this.getCurrentNeighbours().some(function(e, n) {
var o = t.scene.getBlock(e.i, e.j); //
return null !== o && !o.isWall
})
}
getBlock = function(t, e) {
//这就没啥好说的,就是返回当前点的对象而已,如果坐标不合理直接返回null
//正常情况下不可能返回null的,除非强行改代码了
return t >= 0 && t < this.w && e >= 0 && e < this.h ? this.blocks[t][e] : null
}
发现这边的逻辑,其实就是老抓猫的逻辑,也就是得等猫周边全是墙,才胜利。这种逻辑可以逗猫。
但是实际上,我们只要把猫圈起来,就获得胜利了(我们今天就是要干掉这个逻辑,为了逗猫)
所以我们要看,这里的另一个方法cat.step
[JavaScript] 纯文本查看 复制代码 step = function() {
//调了某个方法,这个方法在猫没地方走的情况下返回-1,其他情况下返回方向,这里是今天最关键的地方
var t = this.solver.call(this, this.scene.blocksData, this.i, this.j);
//这里的t指的是方向,上面这个方法返回的就是猫接下来要往哪个方向走,猫能走的方向有6个
//0-5分边代表了6个方向,至于分别代表什么方向,有兴趣可以自己跟一下代码
//return了一个三元表达式,和逗号表达式,学JavaScript可以去了解一下,
//逗号表达式是指执行完逗号左边的,再执行右边,然后返回右边的值
//也就是说这个方法,正常情况下的永远是!1,只有猫无路可走时,返回三元运算法冒号后面的表达式 !!this.stepDirection(t) || (this.caught(),!1)
//这里涉及到js的强转技巧,!!指的是两层否,表示不改变逻辑,但是把返回值转成了bool类型了
// ||是或运算法, A || B ,A为true返回A ,A不满足的情况,执行B,返回B
return t < 0 || t > 6 ? (this.caught(),
!1) : !!this.stepDirection(t) || (this.caught(),
!1)
}
我们先分析stepDirection和caught方法吧.
[JavaScript] 纯文本查看 复制代码 e.prototype.stepDirection = function(t) {
//设置方向
return this.direction = t,
//行动
this.stepForward()
}
,
e.prototype.stepForward = function() {
var t = this
, e = this.getCurrentNeighbours()[this.direction] //其实就是猫下一步要走的位置了
, n = this.scene.getBlock(e.i, e.j);
//行动
return null !== n && (!n.isWall && (this.play(s.default.directions[this.direction].name + "_step"),
this.once("animationcomplete", function() {
t.moveForward(),
t.resetTextureToStop()
}),
!0))
}
e.prototype.caught = function() {
//就是set了一串位移的字符串进去而已,上面某个地方会做解析
this.setTexture(s.default.cannotEscapeTextures[s.default.directions[this.direction].name])
}
接下来就是今天重点的方法了,我们用断点来看吧,比较直接
首先三个入参,分别是地图数据和猫的坐标,然后new一个i对象,其实就是地图对象
执行地图对象的calcAllDistances方法
跟进来发现,这里遍历blocks,就是所有的地图数据,isWall是是否被点击过。isEdge是否边缘,猫到了边缘我们就输了
[JavaScript] 纯文本查看 复制代码 t.prototype.calcAllDistances = function() {
var t = [];
this.blocks.forEach(function(e) {
e.forEach(function(e) {
//这里是多重判断,&&操作是前一个表达式必须是true才执行下一个表达式
//如果e是边缘,不执行后面操作,如果e不是边缘,判断是否是墙,不是墙的情况下,执行最后一句话 (e.distance = 0,t.push(e)) 进行赋值和塞e到t
//也就是说t里面的元素,是所有的边缘不是墙的点
e.isEdge && !e.isWall && (e.distance = 0,t.push(e))
})
});
//这里是一个for循环,结束条件是t里面的所有数据都清空
for (var e = function() {
//从t数组取一个点出来,从上面我们知道,t里面的元素都是边缘数据,且不是墙
var e = t.shift();
e.neighbours.forEach(function(n) {
//又是多重判断的写法,||操作是前一个表达式必须是false才执行下一个表达式
//首先null不为空,n不是边缘,n不是墙,n.distance > e.distance + 1为true的情况下,才执行括号里的内容
//括号内,把邻居的距离+1,当这个邻居不存在于t时,把这个邻居加到t里,后面遍历会遍历到这个邻居
//这样把所有邻居都遍历一遍,直到所有的邻居都不在t里面为止,最终结果是计算出所有点到边缘最近的距离
//猫当然是要往最近的方向走
null === n || n.isEdge || n.isWall || n.distance > e.distance + 1 && (n.distance = e.distance + 1, t.indexOf(n) < 0 && t.push(n))
})
}; t.length > 0; )
e()
}
执行完这一段。节点的dirctions,dirction等值突然发生了变化,但是上述代码没有对这几个属性做操作,所以必定有监听器,下个代码块是监听器代码
[JavaScript] 纯文本查看 复制代码 return Object.defineProperty(t.prototype, "routesCount", {
get: function() {
var t = this;
if (void 0 === this._routesCount)
if (this.isEdge)
this._routesCount = 1;
else {
var e = 0;
this.neighbours.forEach(function(n) {
//n不为空,n不为墙,n的距离比当前的距离小,_routesCount+1能走的路径数量+1
null === n || n.isWall || n.distance < t.distance && (e += n.routesCount)
}),
this._routesCount = e
}
return this._routesCount
},
enumerable: !0,
configurable: !0
}),
Object.defineProperty(t.prototype, "neighbours", {
get: function() {
var t = this;
if (void 0 === this._neighbours) {
var e = o.default.getNeighbours(this.i, this.j);
this._neighbours = e.map(function(e) {
return t.parent.getBlock(e.i, e.j)
})
}
return this._neighbours
},
enumerable: !0,
configurable: !0
}),
Object.defineProperty(t.prototype, "directions", {
//监控所有的方向
get: function() {
var t = this
, e = [];
return this.neighbours.forEach(function(n, o) {
//n不为空,且n不是墙,且n.distance < t.distance,取出
null === n || n.isWall || n.distance < t.distance && e.push(o)
}),
//所有比 当前位置到边缘距离小的节点 的方向(比当前位置还远,我干嘛要往那个方向跑)
e
},
enumerable: !0,
configurable: !0
}),
Object.defineProperty(t.prototype, "direction", {
get: function() {
var t = this
, e = 0
, n = -1;
//遍历所有能走的方向,找到下一步可能性最大的方向
return this.directions.forEach(function(o) {
var r = t.neighbours[o];
r.routesCount > e && (e = r.routesCount, n = o)
}),
n
},
enumerable: !0,
configurable: !0
}),
t
}()
监听器其实就是做了路径数的计算,以及规划猫猫接下来要走的路,这一段应该是猫猫逃脱所用到的算法,具体看看注释吧
好了回归正题
看了上面的代码,我们发现,如果想让猫猫,完全没路走才获胜的话,我们就需要猫猫只通过cat.isCaught()方法来判断胜负
也就是说要让cat.step的返回值永远为true,否则就判断胜利了,
所以,将step最后返回值做一下修改
[JavaScript] 纯文本查看 复制代码 e.prototype.step = function() {
var t = this.solver.call(this, this.scene.blocksData, this.i, this.j);
t < 0 || t > 6 ? (this.caught(),!1) : !!this.stepDirection(t) || (this.caught(),!1)
return 1;
}
这个时候发现猫被围住之后完全不动了,因为猫的方向永远是-1,所以他不走,所以,我们得在监听器里,修改方向的逻辑
[JavaScript] 纯文本查看 复制代码 Object.defineProperty(t.prototype, "direction", {
get: function() {
var t = this
, e = 0
, n = -1;
this.directions.forEach(function(o) {
var r = t.neighbours[o];
r.routesCount > e && (e = r.routesCount, n = o)
})
//遍历所有能走的方向,找到下一步可能性最大的方向
//加一条,当n==-1的时候随机往一个能走的方向走
if(n==-1){
this.neighbours.forEach(function(nn, o) {
if(!nn.isWall){
n = o;
}
});
}
return n;
},
enumerable: !0,
configurable: !0
}),
t
搞定啦。我们试试结果
修改后的js
catch-the-cat.rar
(16.59 KB, 下载次数: 111)
txt和js都发不上来。无奈了
今天研究了一个晚上,本来想找出一个算法来自动玩的
但是这游戏本身不是稳赢的游戏
现在没啥思路
在控制台输入this.window.game.mainScene.playerClick(1,2)可以实现点击1,2这个格子
this.window.game.mainScene.blocksData这个属性是地图数据
有思路朋友的可以去试一波,发帖记得艾特我呀
坐等算法大佬来玩
|