当前位置: 首页 > news >正文

华容道问题求解_详细设计(五)之hash值和回放功能

(续上文)

布局的hash 值计算

笔者也参考了之前的一些文章,很多文章提到了怎么节省存贮空间来查找最优解,这不是笔者的目的。笔者的目的比较单一,就是找到最优解就行了。因此并没有在存贮上面进行过多的优化,曾经尝试使用移位指令进行棋子的移动,然而并没有发现运行速度比使用庞大的对象速度更快。(或许是算法的问题)因此索性就使用了高级语言的高级方法,放弃了对存贮空间的限制和约束。基于前面的设计,每个布局保存在一个4*5的整数数组里面(保存的棋子的ID),因此为了简单起见笔者将这个20个整数构成一个字符串,然后利用C#提供的现有的字符串hash函数,就得到了这个布局的hash值。这个方法简单粗暴,并不美丽,但是解决问题就行了。
一个细节:在实际棋子的移动过程当中,我们关心的是棋子的形状而非棋子的名字,因此在棋子布局的时候,尽管每个棋子都赋予了名字,在进行查找的时候,是忽略了这些名字的。因此在使用DFS结合Dijkstra 算法查找时会出现名字改变的情况,这里笔者没有对名字进行一致处理,因为这种处理对解法其实没有什么帮助,只是界面看着更连贯些。(具体原因也不解释了,很简单)。在BFS的查找过程当中,不会有这个问题。
hash 函数如下:

      public int GetMyHashCode(GameState gameState){string val = string.Empty;for (int i = 1; i <= 4; i++){for (int j = 1; j <=5; j++){var pcs  = gameState.layoutOfObj[i, j];val += pcs.GetHrdType().ToString();}}return val.GetHashCode();}

注意上面只取了Type值进行hash处理。

回放功能

找到了解法,还要能进行回放,否则也看不到效果,也不能证明是否成功了,因此增加回放功能很有必要。
回放功能,就是记录每个布局的hash值和布局,然后根据hash值从布局记录中查找对应的布局,在映射到UI上就可以了。

最佳结果保存在如下定义的数据字典里

       // the shorest path will be stored in the following variable.// key:the hash code of the current layout, value.item1, the hashcode of the source layout , item2 the number of the shortest steps.Dictionary<int, (int,int)> hCodeAndShotShortPathDict = new Dictionary<int, (int,int)>();

hash值和布局快照保存在如下定义的数据字典中

// store the hash code and the layout shot , selpcs index,dstPcs.idx Dictionary<int, (Piece[],int,int)> hCodeAndShotDict = new Dictionary<int, (Piece[], int, int)>();
回放时,将这两个数据字典结合起来,就可以找到快照,将快照映射到界面上就可以了。

具体的保存过程

DFS中的用了Dijkstra 算法,算法本身就包括了最短路径的记录,因此这里不再赘述。
BFS算法中,找到的方法都是对应不同布局的最佳步骤,因此在尝试移动棋子的过程当中,记录当前的布局就可以了。
代码如下:
在AddEdgeToGraph 中的函数AdjustShortPath 就是用于记录解法的。
       private void AddEdgeToGraph(StateShot source, StateShot dest){var toHashCode = GetMyHashCode(dest);var frmHashCode = GetMyHashCode(source);AdjustShortPath(dest, toHashCode, frmHashCode);AddEdge(frmHashCode, toHashCode, 1);}private void PushState(StateS

AdjustShortPath 代码如下:

      private void AdjustShortPath(StateShot dest, int toHashCode, int frmHashCode){(int, int) lastItem;if (hCodeAndShotShortPathDict.TryGetValue(toHashCode, out lastItem)){if (lastItem.Item2 > dest.bestSteps){//这是笔者增加的视图记录最短路径的判断,DFS时,找到了比原来步骤少的方法,但是不是最优的。BFS时,这里执行不到。hCodeAndShotShortPathDict[toHashCode] = (frmHashCode, dest.bestSteps);}//hCodeAndShotShortPathDict.Add(toHashCode, (dest.basePcs, selPcs.idx, dstPcs.idx, frmHashCode, pathLen));}else{// 查找过程记录,BFS时,记录的就是最佳结果hCodeAndShotShortPathDict.Add(toHashCode, (frmHashCode, dest.bestSteps));}}

回放过程

程序先找到要回放的结果布局,然后将这个结果布局的对应的保存的所有过程布局,放到一个数组中,每执行一步,数组的索引加一,指向下一个过程布局。代码如下:

     private void MoveNext(){//allShortPath = allShortPath2;if (allShortPath == null){MessageBox.Show(string.Format("The shortest path is now being searched ,wait for a moment."), "Notice", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);return;}//solutionPcsIdx 布局数组索引//cbbSolutionIdx 保存在CBB 中的结果布局索引(hash 值)if (solutionPcsIdx >= 0 && solutionPcsIdx < allShortPath[cbbSolutionIdx].Count){if (solutionPcsIdx < allShortPath[cbbSolutionIdx].Count - 1)solutionPcsIdx++;var lastKey = allShortPath[cbbSolutionIdx][solutionPcsIdx - 1];//txt_Notice.Text = string.Format("Layout code:{0} Graph node info {1}\r\n{2}",lastKey, _hrdGame.adjacencyList[lastKey],txt_Notice.Text);var lastLayoutArr = _hrdGame.GetLayoutIdxArrByHashCode(lastKey);var key = allShortPath[cbbSolutionIdx][solutionPcsIdx];var layoutArr = _hrdGame.GetLayoutIdxArrByHashCode(key);//get the moving pieces by comparing the two layoutsvar movedPcs = GetMovedPieces(lastLayoutArr.Item1, layoutArr.Item1);var frmIdx = layoutArr.Item2;var toIdx = frmIdx;// layoutArr.Item3;var srcPcs = movedPcs.Item1;var dstPcs = movedPcs.Item2;var passPcs = movedPcs.Item3;// animation moving var selPos = srcPcs.GetHrdPos();var emptySpPos = dstPcs.GetHrdPos();var passPos = passPcs.GetHrdPos();// the following used to add animation effectvar srcPos = new HrdPoint(selPos.X, selPos.Y);var dstPos = new HrdPoint(emptySpPos.X, emptySpPos.Y);_hrdGame.gameState.pieces = layoutArr.Item1;_hrdGame.MovePcsWithAnimation(srcPcs, srcPos, dstPos, passPos, dstPcs, null);btn_ShowSteps.Text = string.Format("{0}/{1}", solutionPcsIdx, allShortPath[cbbSolutionIdx].Count - 1);//MessageBox.Show(string.Format("Click for next step, current step: {0}/{1}", i, allPath[0].Count));}}

函数中增加了 动画部分,因此做了相应的处理,这一部分将在后文赘述。

回放时,需要用户点击回放按钮。也支持了Undo功能,以方便用户观察棋子的移动过程。目前,执行Undo功能时没有做动画处理。

说明如下:
首先在下拉列表中选择一个布局的Hash值,例如第一个(就应该是最少步骤那个,BFS决定的)
在这里插入图片描述

点击[>>],棋子移动一步

在这里插入图片描述
点击[<<],恢复到上一步

在这里插入图片描述
(待续)
MaraSun BJFWDQ
2024-03-10

相关文章:

  • qt使用QAxObject操作excel程序关闭之后excel进程未被关闭的解决方案
  • 【C++】三大特性之继承
  • Kafka|处理 Kafka 消息重复的有效措施
  • 线性代数 --- 特征值与特征向量(下)
  • CTP-API开发系列之五:SimNow环境介绍
  • 0102全排列和对换-行列式-线性代数
  • LeetCode每日一题之 快乐数
  • Rust常用特型之Drop特型
  • 【Python】科研代码学习:六 ModelOutput,SpecificModel
  • Rust有没有信号量机制,在缓存有数据的时候才允许等待的进程取数据?
  • 【Go】令牌桶限流算法
  • Unity Text文本实现滚动跑马灯效果
  • (MATLAB)第五章-矩阵运算
  • okHttp MediaType MIME格式详解
  • Java的堆如何分代的?
  • cookie和session
  • C语言笔记(第一章:C语言编程)
  • express + mock 让前后台并行开发
  • HTTP中的ETag在移动客户端的应用
  • Java教程_软件开发基础
  • NSTimer学习笔记
  • Shadow DOM 内部构造及如何构建独立组件
  • Spring声明式事务管理之一:五大属性分析
  • TCP拥塞控制
  • 从PHP迁移至Golang - 基础篇
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 分布式熔断降级平台aegis
  • 构造函数(constructor)与原型链(prototype)关系
  • 关于springcloud Gateway中的限流
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 我有几个粽子,和一个故事
  • 移动端唤起键盘时取消position:fixed定位
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 阿里云ACE认证之理解CDN技术
  • 函数计算新功能-----支持C#函数
  • # 学号 2017-2018-20172309 《程序设计与数据结构》实验三报告
  • #预处理和函数的对比以及条件编译
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (6)设计一个TimeMap
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (编译到47%失败)to be deleted
  • (多级缓存)缓存同步
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (附源码)计算机毕业设计大学生兼职系统
  • (三) diretfbrc详解
  • (四)Linux Shell编程——输入输出重定向
  • ***利用Ms05002溢出找“肉鸡
  • ***原理与防范
  • .NET CORE Aws S3 使用
  • .net 受管制代码