华容道游戏(golang+govcl+自动求解算法)

jjmgx · · 1793 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

自从发现名为govcl的golang gui库以后,发现写个带gui的游戏也不是什么困难的事了,当然要写好也不容易。今天我来发布一个用golang+govcl实现的华容道游戏,没什么技术含量,只是苦力而已,电脑自动计算花了点时间优化速度,我这个只不过实现了功能,还有很多可以优化改进的地方。 最后面有源码下载地址。 先上截图如下: ![image.png](https://static.studygolang.com/200601/06dd1819a0c723935fc0f2861857388b.png) 主要源码如下: ```` package main import ( "time" "github.com/ying32/govcl/vcl" "github.com/ying32/govcl/vcl/types" ) //::private:: type TForm1Fields struct { } var kHW = int32(107) //每小块的宽高 var xPy = int32(66) //角色图片在背景的偏移x var yPy = int32(66) //角色图片在背景的偏移y var selX = int32(-1) //被选择的角色位置x var selY = int32(-1) //被选择的角色位置y var seled = false //是否有选择的角色 var zhenFa TZHF //当前刷新的角色数据 func (f *TForm1) OnFormCreate(vcl.IObject) { //默认为第一个阵法 zhenFa = zhenFaList[0].ZhFa //窗口双缓冲刷新,这样不抖动 f.SetDoubleBuffered(true) //窗口标题 f.SetCaption("华容道") //初始化阵法列表 font := f.ListBox1.Font() font.SetSize(18) f.ListBox1.SetFont(font) for _, z := range zhenFaList { f.ListBox1.Items().Add(z.Name) } } func (f *TForm1) OnListBox1DblClick(sender vcl.IObject) { //阵法列表双击时选择新的阵法 n := f.ListBox1.ItemIndex() if n == -1 { return } zhenFa = zhenFaList[int(n)].ZhFa seled = false selX = int32(-1) selY = int32(-1) f.OnPaintBox1Paint(f.PaintBox1) } func (f *TForm1) OnPaintBox1Paint(sender vcl.IObject) { //画背景及角色 p := f.PaintBox1.Canvas() p.Draw(0, 0, pngBg) for i := 0; i < 5; i++ { for j := 0; j < 4; j++ { if zhenFa[i][j] > 0 { //0空格及-1补位,不用画 x := xPy + int32(j)*kHW y := yPy + int32(i)*kHW p.Draw(x, y, pngHash[zhenFa[i][j]]) if seled && i == int(selX) && j == int(selY) { //如果是被选择的,画框 switch zhenFa[i][j] { case 1: p.FrameRect(types.TRect{ x, y, x + kHW*2, y + kHW*2, }) case 2, 7, 8, 9, 10: p.FrameRect(types.TRect{ x, y, x + kHW*2, y + kHW, }) case 3, 4, 5, 6: p.FrameRect(types.TRect{ x, y, x + kHW, y + kHW*2, }) case 11: p.FrameRect(types.TRect{ x, y, x + kHW, y + kHW, }) } } } } } } func (f *TForm1) OnPaintBox1MouseDown(sender vcl.IObject, button types.TMouseButton, shift types.TShiftState, x, y int32) { if button == types.MbLeft { //取出点击位置最左边索引及角色名 indexX, indexY, lx := getDjInfo(x, y, zhenFa) if lx == -1 { //点击了非正常位置,放弃 return } if lx > 0 { //点击了正常位置时,标边框 selX = indexX selY = indexY seled = true f.PaintBox1.Repaint() return } if lx == 0 && seled { //点击了空位,且已经选择了角色时,开始移动 if tz, ok := yd(selX, selY, indexX, indexY, zhenFa); ok { zhenFa = tz f.PaintBox1.Repaint() if zhenFa[3][1] == 1 { //成功出逃 vcl.ShowMessage("曹瞒兵败走华容,正与关公狭路逢,\r\n只为当初恩义重,放开金锁走蛟龙。") } } } } } //OnButton1Click 电脑算法 func (f *TForm1) OnButton1Click(sender vcl.IObject) { hashjg := make(map[int]TZHF) //存储走过的步骤,用来防止重复走及回溯解法 sz := []TZHF{} //存储每一次要尝试走法的数据列表 sz = append(sz, zhenFa) //把当前数据加入到尝试列表 jg := TZHF{} //记录最终成功时的数据 for { //循环至无解或者得到解法 newsz := []TZHF{} //缓存下一次要尝试的列表 for _, sj := range sz { //遍历本次要尝试的列表 ydhsz := getYdsz(sj) //得到可以移动到的结果 for _, ydh := range ydhsz { //遍历可移动的结果 key := getKey(ydh) //转换成key if ydh[3][1] == 1 { //判断是否已是成功解,如果是,直接跳出 hashjg[key] = sj jg = ydh goto END } if _, ok := hashjg[key]; !ok { //判断这个结果以前没有出现过时,加入到下次要尝试的列表 hashjg[key] = sj newsz = append(newsz, ydh) } } } if len(newsz) == 0 { //如果下次要尝试的列表为空,证明没有解 vcl.ShowMessage("无语2,电脑也没解出来!") return } //将下次要尝试的列表保存,为下次遍历作准备 sz = newsz } END: //到此处时,证明求到了解 jgsz := []TZHF{jg} //先把最后一次的结构保存进结果数组,为后面演示做准备 key := getKey(jg) //取出最后一次数据的key,往回追溯父节点 ykey := getKey(zhenFa) //保存求解前的key,为是否追溯到根判断用 for { //循环直到追溯到根 if jf, ok := hashjg[key]; ok { jgsz = append(jgsz, jf) key = getKey(jf) if key == ykey { break } } } go func() { //开始演示 //倒着从最后一个数据开始 for i := len(jgsz) - 1; i >= 0; i-- { vcl.ThreadSync(func() { //赋值后刷新画板再延时即可 zhenFa = jgsz[i] f.PaintBox1.Repaint() time.Sleep(500 * time.Millisecond) }) } }() } func getKey(sj TZHF) int { //key是一个int类型,一是节约空间,二是可以更好的判断重复的不用再尝试 jg := 1 for i := 0; i < 5; i++ { for j := 0; j < 4; j++ { sj := int(sj[i][j]) if sj == 4 || sj == 5 || sj == 6 { //为竖着的块时,统一成3 sj = 3 } else if sj == 7 || sj == 8 || sj == 9 || sj == 10 { //为横块时,统一成2 sj = 2 } else if sj == 11 { //为小兵时,转换成4 sj = 4 } else if sj == 0 { //为空格时,转换成5 sj = 5 } if sj > -1 { //-1点位块时,不做处理 jg = jg*10 + sj //合并数据得到一个int型的key } } } return jg } func getYdsz(sj TZHF) []TZHF { //保存可以走动的数据 ls := []TZHF{} for i := 0; i < 5; i++ { for j := 0; j < 4; j++ { if sj[i][j] == 0 { //当块为空时,判断块的上下左右能否移动到此块 sja := [3]int32{int32(i - 1), int32(i), int32(i + 1)} sjb := [3]int32{int32(j - 1), int32(j), int32(j + 1)} for _, a := range sja { for _, b := range sjb { if a >= 0 && b >= 0 && a < 5 && b < 4 && //位置合法 (a+b-int32(i+j) == 1 || int32(i+j)-a-b == 1) { //不是斜角 x, y := a, b if sj[a][b] == -1 { //如果为点位块,找到主块 x, y = getZhukuai(a, b, sj) } //尝试移动,得到结果时保存 if jg, ok := yd(x, y, int32(i), int32(j), sj); ok { ls = append(ls, jg) if jg[3][1] == 1 { //成功时直接返回,节约性能 return ls } } } } } } } } return ls //没成功时返回所有可能性,为下次尝试做准备 } func yd(yx, yy, nx, ny int32, TempZF TZHF) (TZHF, bool) { if yx == nx && yy == ny { //如果点击的是自己,直接返回 return TempZF, false } if TempZF[nx][ny] != 0 { //如果目的位不是空,直接返回 return TempZF, false } switch TempZF[yx][yy] { case 1: //曹操 if (yy-ny == 1 && yx == nx && TempZF[nx+1][ny] == 0) || (yy-ny == 1 && yx == nx-1 && TempZF[nx-1][ny] == 0) { //左移 TempZF[yx][yy-1] = 1 TempZF[yx+1][yy-1] = -1 TempZF[yx][yy] = -1 TempZF[yx+1][yy] = -1 TempZF[yx][yy+1] = 0 TempZF[yx+1][yy+1] = 0 selX, selY = yx, yy-1 return TempZF, true } if (ny-yy == 2 && yx == nx && TempZF[nx+1][ny] == 0) || (ny-yy == 2 && yx == nx-1 && TempZF[nx-1][ny] == 0) { //右移 TempZF[yx][yy] = 0 TempZF[yx+1][yy] = 0 TempZF[yx][yy+1] = 1 TempZF[yx+1][yy+1] = -1 TempZF[yx][yy+2] = -1 TempZF[yx+1][yy+2] = -1 selX, selY = yx, yy+1 return TempZF, true } if (yx-nx == 1 && yy == ny && TempZF[nx][ny+1] == 0) || (yx-nx == 1 && yy == ny-1 && TempZF[nx][ny-1] == 0) { //上移 TempZF[yx-1][yy] = 1 TempZF[yx-1][yy+1] = -1 TempZF[yx][yy] = -1 TempZF[yx][yy+1] = -1 TempZF[yx+1][yy] = 0 TempZF[yx+1][yy+1] = 0 selX, selY = yx-1, yy return TempZF, true } if (nx-yx == 2 && yy == ny && TempZF[nx][ny+1] == 0) || (nx-yx == 2 && yy == ny-1 && TempZF[nx][ny-1] == 0) { //下移 TempZF[yx][yy] = 0 TempZF[yx][yy+1] = 0 TempZF[yx+1][yy] = 1 TempZF[yx+1][yy+1] = -1 TempZF[yx+2][yy] = -1 TempZF[yx+2][yy+1] = -1 selX, selY = yx+1, yy return TempZF, true } case 2, 7, 8, 9, 10: //关羽等横块 dqjs := TempZF[yx][yy] if yx == nx && yy-ny == 1 { //左移 TempZF[yx][yy-1] = dqjs TempZF[yx][yy] = -1 TempZF[yx][yy+1] = 0 selX, selY = yx, yy-1 return TempZF, true } if yx == nx && ny-yy == 2 { //右移 TempZF[yx][yy] = 0 TempZF[yx][yy+1] = dqjs TempZF[yx][yy+2] = -1 selX, selY = yx, yy+1 return TempZF, true } if yx == nx && yy-ny == 2 && TempZF[yx][yy-1] == 0 { //左二 TempZF[yx][yy-2] = dqjs TempZF[yx][yy-1] = -1 TempZF[yx][yy] = 0 TempZF[yx][yy+1] = 0 selX, selY = yx, yy-2 return TempZF, true } if yx == nx && ny-yy == 3 && TempZF[yx][yy+2] == 0 { //右二 TempZF[yx][yy] = 0 TempZF[yx][yy+1] = 0 TempZF[yx][yy+2] = dqjs TempZF[yx][yy+3] = -1 selX, selY = yx, yy+2 return TempZF, true } if (yx-nx == 1 && yy == ny && TempZF[nx][ny+1] == 0) || (yx-nx == 1 && yy == ny-1 && TempZF[nx][ny-1] == 0) { //上移 TempZF[yx-1][yy] = dqjs TempZF[yx-1][yy+1] = -1 TempZF[yx][yy] = 0 TempZF[yx][yy+1] = 0 selX, selY = yx-1, yy return TempZF, true } if (nx-yx == 1 && yy == ny && TempZF[nx][ny+1] == 0) || (nx-yx == 1 && yy == ny-1 && TempZF[nx][ny-1] == 0) { //下移 TempZF[yx][yy] = 0 TempZF[yx][yy+1] = 0 TempZF[yx+1][yy] = dqjs TempZF[yx+1][yy+1] = -1 selX, selY = yx+1, yy return TempZF, true } case 3, 4, 5, 6: //张等竖块 dqjs := TempZF[yx][yy] if yy == ny && yx-nx == 1 { //上移 TempZF[yx-1][yy] = dqjs TempZF[yx][yy] = -1 TempZF[yx+1][yy] = 0 selX, selY = yx-1, yy return TempZF, true } if yy == ny && nx-yx == 2 { //下移 TempZF[yx][yy] = 0 TempZF[yx+1][yy] = dqjs TempZF[yx+2][yy] = -1 selX, selY = yx+1, yy return TempZF, true } if yy == ny && yx-nx == 2 && TempZF[yx-1][yy] == 0 { //上二 TempZF[yx-2][yy] = dqjs TempZF[yx-1][yy] = -1 TempZF[yx][yy] = 0 TempZF[yx+1][yy] = 0 selX, selY = yx-2, yy return TempZF, true } if yy == ny && nx-yx == 3 && TempZF[yx+3][yy] == 0 { //下二 TempZF[yx][yy] = 0 TempZF[yx+1][yy] = 0 TempZF[yx+2][yy] = dqjs TempZF[yx+3][yy] = -1 selX, selY = yx+2, yy return TempZF, true } if (yy-ny == 1 && yx == nx && TempZF[nx+1][ny] == 0) || (yy-ny == 1 && yx == nx-1 && TempZF[nx-1][ny] == 0) { //左移 TempZF[yx][yy-1] = dqjs TempZF[yx+1][yy-1] = -1 TempZF[yx][yy] = 0 TempZF[yx+1][yy] = 0 selX, selY = yx, yy-1 return TempZF, true } if (ny-yy == 1 && yx == nx && TempZF[nx+1][ny] == 0) || (ny-yy == 1 && yx == nx-1 && TempZF[nx-1][ny] == 0) { //右移 TempZF[yx][yy] = 0 TempZF[yx+1][yy] = 0 TempZF[yx][yy+1] = dqjs TempZF[yx+1][yy+1] = -1 selX, selY = yx, yy+1 return TempZF, true } case 11: //小兵 if (yx-nx == 1 && yy == ny) || //上移 (nx-yx == 1 && yy == ny) || //下移 (yy-ny == 1 && yx == nx) || //左移 (ny-yy == 1 && yx == nx) || //右移 (yx-nx == 2 && yy == ny && TempZF[yx-1][ny] == 0) || //上移二位 (nx-yx == 2 && yy == ny && TempZF[yx+1][ny] == 0) || //下移二位 (yy-ny == 2 && yx == nx && TempZF[yx][yy-1] == 0) || //左移二位 (ny-yy == 2 && yx == nx && TempZF[yx][yy+1] == 0) || //右移二位 (yx-nx == 1 && yy-ny == 1 && (TempZF[yx-1][yy] == 0 || TempZF[yx][yy-1] == 0)) || //左上 (nx-yx == 1 && yy-ny == 1 && (TempZF[yx][yy-1] == 0 || TempZF[yx+1][yy] == 0)) || //左下 (ny-yy == 1 && yx-nx == 1 && (TempZF[yx][yy+1] == 0 || TempZF[yx-1][yy] == 0)) || //右上 (ny-yy == 1 && nx-yx == 1 && (TempZF[yx][yy+1] == 0 || TempZF[yx+1][yy] == 0)) { //右下 TempZF[yx][yy], TempZF[nx][ny] = TempZF[nx][ny], TempZF[yx][yy] selX, selY = nx, ny return TempZF, true } } return TempZF, false } func getDjInfo(x, y int32, TempZF TZHF) (int32, int32, int8) { //判断点击的位置是否合法 if x < xPy || y < yPy || x > xPy+kHW*4 || y > xPy+kHW*5 { //不在角色范围时返回 return -1, -1, -1 } j := (x - xPy) / kHW i := (y - yPy) / kHW if i > 4 || j > 3 { //不在角色范围时返回 return -1, -1, -1 } if TempZF[i][j] == -1 { //如果为-1点位块时,要找到其主块的角色 i, j = getZhukuai(i, j, TempZF) } return i, j, TempZF[i][j] } func getZhukuai(x, y int32, TempZF TZHF) (int32, int32) { //根据点击的位置,来判断点击的主块是什么角色 if x-1 >= 0 && TempZF[x-1][y] > 0 && TempZF[x-1][y] < 7 && TempZF[x-1][y] != 2 { //横块的点位 return x - 1, y } if y-1 >= 0 && TempZF[x][y-1] > 0 && TempZF[x][y-1] < 11 { //竖块的点位 return x, y - 1 } if x-1 >= 0 && y-1 >= 0 && TempZF[x-1][y-1] > 0 && TempZF[x-1][y-1] < 11 { //曹操的占位 return x - 1, y - 1 } return x, y //都没有,只好返回自己 } ```` 源码下载地址:https://github.com/Jjmgx/Hrd 注:本人在win10下测试通过,如果其它系统,请自行下载相关的govcl的库

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1793 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传