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

2024-03-11 03:04

本文主要是介绍华容道问题求解_详细设计(五)之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

这篇关于华容道问题求解_详细设计(五)之hash值和回放功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/796394

相关文章

Flutter实现文字镂空效果的详细步骤

《Flutter实现文字镂空效果的详细步骤》:本文主要介绍如何使用Flutter实现文字镂空效果,包括创建基础应用结构、实现自定义绘制器、构建UI界面以及实现颜色选择按钮等步骤,并详细解析了混合模... 目录引言实现原理开始实现步骤1:创建基础应用结构步骤2:创建主屏幕步骤3:实现自定义绘制器步骤4:构建U

使用Python创建一个功能完整的Windows风格计算器程序

《使用Python创建一个功能完整的Windows风格计算器程序》:本文主要介绍如何使用Python和Tkinter创建一个功能完整的Windows风格计算器程序,包括基本运算、高级科学计算(如三... 目录python实现Windows系统计算器程序(含高级功能)1. 使用Tkinter实现基础计算器2.

MySQL主从同步延迟问题的全面解决方案

《MySQL主从同步延迟问题的全面解决方案》MySQL主从同步延迟是分布式数据库系统中的常见问题,会导致从库读取到过期数据,影响业务一致性,下面我将深入分析延迟原因并提供多层次的解决方案,需要的朋友可... 目录一、同步延迟原因深度分析1.1 主从复制原理回顾1.2 延迟产生的关键环节二、实时监控与诊断方案

SQLyog中DELIMITER执行存储过程时出现前置缩进问题的解决方法

《SQLyog中DELIMITER执行存储过程时出现前置缩进问题的解决方法》在SQLyog中执行存储过程时出现的前置缩进问题,实际上反映了SQLyog对SQL语句解析的一个特殊行为,本文给大家介绍了详... 目录问题根源正确写法示例永久解决方案为什么命令行不受影响?最佳实践建议问题根源SQLyog的语句分

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

MyBatis模糊查询报错:ParserException: not supported.pos 问题解决

《MyBatis模糊查询报错:ParserException:notsupported.pos问题解决》本文主要介绍了MyBatis模糊查询报错:ParserException:notsuppo... 目录问题描述问题根源错误SQL解析逻辑深层原因分析三种解决方案方案一:使用CONCAT函数(推荐)方案二:

Redis 热 key 和大 key 问题小结

《Redis热key和大key问题小结》:本文主要介绍Redis热key和大key问题小结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、什么是 Redis 热 key?热 key(Hot Key)定义: 热 key 常见表现:热 key 的风险:二、

IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤及问题解决

《IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决》:本文主要介绍IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决,本文分步骤结合实例给大... 目录步骤 1:创建 Maven Web 项目步骤 2:添加 Spring MVC 依赖1、保存后执行2、将新的依赖

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊