[文章][ArkUI]HarmonyOS新ArkUI纯ets版本抓住神经猫--会算法的神经猫你能抓住吗?

阅读量0
2
4
1.前言
<<抓住神经猫>>游戏相信不少朋友都玩过,最近不是ARKUI挺火热的么,一直搞嵌入式的也想尝尝鲜,想找个小玩意入门,想起来5,6年前看过的一篇文章,分析神经猫的寻路算法,号称是最难抓住的神经猫,回去一看,这项目不正好练手吗,说干就干.

2.效果
目前已完成整个游戏的UI(当然图是网上找的),游戏逻辑(启动,移动,胜负判定),可以学到的知识:ets方式的UI布局,ets组件化开发思想,自定义弹出框,ets语言构造多叉树,广度优先算法,最小路径算法,设置图片动画等等,整体开发下来UI地方还算顺畅,难到不难,只是这种声明式UI和以前的命令式UI的编程方式需要一个思路转变,最费时间的地方是那个猫跑路的算法,算法本来是薄弱项,这下正中软肋了,不过好在翻了好几本书,找了些资料,再看了点视频,就搞定了,废话少说,来个动图感受一下

先来个失败的:
失败\n
失败

再来个成功的:
胜利\n
胜利

实际体验下来也是输多赢少,因为这是一只会算法的猫.
3.关键部分解读3.1 玩法
游戏的地图是一个9*9交错排列的一些灰色小圆点,初始情况下游戏生成地图的时候会随机选一些点变成橙色做为墙,然后玩家每次点击一个灰色圆点,圆点变成橙色,同时猫移动一步,关于胜负的判定是猫被圈进一个圈子里面就为赢,当猫跑到地图边边上就为输.玩法还是挺简单的
3.3 部分代码
代码结构长这样,因为时间关系,代码的结构还没有优化,有些地方看起来很是很混乱,包括文件名,变量名那些,后面有时间再调整吧,目前就将就看一下吧

再贴个"猫"类吧:  

  1. @Observed
  2. export class catData{
  3.   pos_x:number
  4.   pos_y:number

  5.   location:number
  6.   step:number = 0

  7.   inWall:boolean=false

  8.   constructor(loc:number = defaultLocation){
  9.     this.setLocation(loc)
  10.   }

  11.   reset(){
  12.     this.inWall = false
  13.     this.step = 0
  14.     this.setLocation(defaultLocation)
  15.   }

  16.   setLocation(loc:number){
  17.     console.log("set Loction:"+loc)
  18.     this.location=loc
  19.     this.setPosition()
  20.   }

  21.   setPosition(){
  22.     let col:number = Math.floor(this.location/9)
  23.     let row:number = this.location%9

  24.     this.pos_x = (col%2 ? row*38+17:row*38)
  25.     this.pos_y = col*38

  26. //    console.log("x:"+this.pos_x+" y:"+this.pos_y)
  27.   }

  28.   moveTo(location:number) {

  29.     this.setLocation(location)
  30.     this.step++

  31.   }

  32.   catInWall(yes:boolean=true){
  33.     this.inWall = yes
  34.   }
  35. }

复制代码
猫类的主要功能就是记录移动步数,然后移动位置,当游戏重新开始的时候某些属性复位
3.3 主要逻辑
游戏的主要逻辑就是靠玩家点击圆点发生,当点击圆点的时候,先将该点置为墙,然后先要找出猫能逃跑的路线,再让猫选择一条路线进行逃生,主要逻辑的代码如下:

  1. export function onItemClick(index : number){
  2.   console.log("onItemClick "+index)
  3.   if(myCircleDataArray[index].clicked ){
  4.     console.log("不能点击")
  5.     return;

  6.   }
  7.   myCircleDataArray[index].setClick()

  8.   let catLocation = myCat.location

  9.   myCircleDataArray.forEach(item=>{
  10.     item.path = -100
  11.     item.depth = 100
  12.   })

  13.   myCircleDataArray[catLocation].depth=0

  14.   let neighbors = getNeighbors(catLocation,myCircleDataArray[catLocation].depth)

  15.   neighbors.forEach(item=> {

  16.     myCircleDataArray[item.location].depth=item.depth

  17.   })

  18.   let neighborList:Array<Neighbor> =neighbors

  19.   while(neighborList.length > 0){
  20.     let neighborsArrayList:Array<Neighbor> = []

  21.     neighborList.forEach(item=>{
  22.       let neighborsTemp = buildNeighborChild(item)
  23.       neighborsTemp.forEach(item=>{
  24.         neighborsArrayList.push(item)
  25.       })
  26.     })


  27.     neighborList = neighborsArrayList

  28.   }
  29.   neighbors.forEach(item=>{

  30.     calcCirclePath(item)

  31.   })

  32.   let best = getBestNeighbor(neighbors)

  33.   console.log("the best neighbor is:"+best.location+" path="+best.path);
  34.   if(best.path == 100){
  35.     console.log("cat is in wall");
  36.     myCat.catInWall()
  37.     myGame.setState(gameState.gameVictory)
  38.   }

  39.   myCat.moveTo(best.location)

  40.   if(catCheckLocation() == true){
  41.     console.log("cat escaped")
  42.     myGame.setState(gameState.gameFailed)
  43.   }

  44.   neighbors.length = 0



  45. }
复制代码
关于寻路算法这里说一下,目前采用的是广度优先遍历,在找到几条最短路径,当有两条以上的最短路径的时候,就看一下哪条路径下的子路径比较多(防止走入玩家设定的陷阱)
  1. function getBestNeighbor(neighbors:Neighbor[]):Neighbor{

  2.   let neighbor:Neighbor = neighbors[0]

  3.   //already get the shortest path
  4.   for(var i=1;i < neighbors.length;i++){
  5.     if(neighbor.path > neighbors[i].path){
  6.       neighbor = neighbors[i]
  7.     }
  8.   }
  9.   console.log("the shortest nei:"+neighbor.location)

  10.   //select the child more than other
  11.   for(var j=0;j < neighbors.length;j++){
  12.     if(neighbor.path == neighbors[j].path){
  13.       console.log("find same path:"+neighbors[j].location)
  14.      if(neighbor.child.length < neighbors[j].child.length){

  15.        neighbor = neighbors[j]
  16.        console.log("changed neighbor:"+neighbor.location)
  17.      }
  18.     }
  19.   }

  20.   return neighbor
  21. }
复制代码
开发的时候设置一个调试小技巧效率高很多(然而一开始的时候我也是个铁憨憨,在草纸上记,弱爆了):
当把这个置true的时候可以清晰的看到点的位置和到达边界需要的步数,神经猫正是通过这个来跑路的,看一下打开的效果:



4.补充4.1 后续
后续需要做的事情主要是以下几件:
  • 当前可以看到3个难度图标,目前还没做功能,后续会加上,难度的调节目前认为有几个地方:
    初始墙的数量,逃跑算法的强度,或者优化初始墙的位置(简单的话就相对集中,困难的话就相对分散),以我非数学专业的功力感觉第一种靠谱一些,如果有数学大神也可以探讨一下后面两种怎么做
  • 可以看到猫逃跑的时候,从当前位置移动到下一位置,会出现短暂的消失,这有可能是模拟器在渲染动图的时候有什么延时导致的,后期准备研究一下动画效果,让猫的走动更顺滑
  • 由于目前还没有OpenHarmony能玩起来的开发板,所以只能是模拟器演示,后续如果有真正的开发板的话,相信很快看就能移植上去
4.2 关于代码
目前代码托管在Gitee上,感兴趣的小伙伴可以下载过来试一下,如果有什么建议或者改动也可以提提PR,提一下,这个游戏别看简单,拿来入门练手还是非常合适的,如果一开始就搞什么复杂的界面或者交互很可能会打击信心的哈
代码地址:https://gitee.com/yegren/CrazyCat
声明:此代码仅供学习使用,任何人不得用于商业用途,否则造成的后果与本人无关
欢迎算法大神一起研究探讨
4.3 鸣谢
最后感谢一下@九弓子 和@坚果前端 两位大佬前期帮忙解答关于声明式UI的一些疑惑


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友