golang 实现华容道
来源:互联网 发布:淘宝网买的学车模拟器 编辑:程序博客网 时间:2024/05/08 13:50
游戏介绍
华容道游戏共有10个棋子, 在5*4的棋盘上. 其中曹操占2*2格, 张飞,关羽, 赵云, 黄忠, 马超 占2*1格, 这些棋子有横向和竖向的区别. 4个卒各占1格. 目标: 将曹操这枚棋子移动到棋盘正下方. 棋子不能横跨棋子,每次只移动一格.
设计思路
因为没有图形界面, 于是将每个棋子和移动方向编号.
* 棋子编号: 0.曹操 1.张飞 2.关羽 3.赵云 4.黄忠 5.马超 6.兵 7.卒 8.勇 9.士
* 移动方向编号: 1.上 2.下 3.左 4.右
用户输入两个整数, 第一个选择棋子, 第二个选择方向.
如: 0, 1 将曹操向上移动一格.
将棋盘看作一个对象, 它有5*4的一个二维数组当作棋盘.
有10个棋子组成的map.
棋子结构体定义如下:
// 棋子type Chessman struct { Name string `desc:"棋子的名字"` Code string `desc:"棋子在棋盘上的标志符"` StartPoint [2]int `desc:"起点"` GridNums int `desc:"格子数"` Direction int `desc:"方向, 0 代表竖着, 1 代表横着"`}
棋盘的定义如下:
// 华容道type Klotski struct { Array *[5][4]string `desc:"5*4的地图"` Chessmen map[int]*Chessman `desc:"存放棋子的字典"`}
因为棋盘的摆放位置多种多样, 本程序没有模拟这个随机算法.只选取其中一种摆放位置.
// 用于初始化华容道地图// 初始地图有很多种,所以将这部分提取出来,以后可扩展func initKlotski() *Klotski { return &Klotski{ Array: &[5][4]string{}, Chessmen: map[int]*Chessman{ 0: {Name: "曹操", Code: "曹", StartPoint: [2]int{0, 1}, GridNums: 4}, 1: {Name: "张飞", Code: "张", StartPoint: [2]int{0, 0}, GridNums: 2, Direction: 0}, 2: {Name: "关羽", Code: "关", StartPoint: [2]int{2, 1}, GridNums: 2, Direction: 1}, 3: {Name: "赵云", Code: "赵", StartPoint: [2]int{0, 3}, GridNums: 2, Direction: 0}, 4: {Name: "黄忠", Code: "黄", StartPoint: [2]int{2, 0}, GridNums: 2, Direction: 0}, 5: {Name: "马超", Code: "马", StartPoint: [2]int{2, 3}, GridNums: 2, Direction: 0}, 6: {Name: "兵", Code: "兵", StartPoint: [2]int{3, 1}, GridNums: 1}, 7: {Name: "卒", Code: "卒", StartPoint: [2]int{3, 2}, GridNums: 1}, 8: {Name: "勇", Code: "勇", StartPoint: [2]int{4, 0}, GridNums: 1}, 9: {Name: "士", Code: "士", StartPoint: [2]int{4, 3}, GridNums: 1}, }, }}
摆放完棋子后, 要初始化二维数组,一则方便呈现给用户看, 二则可以用来判断棋子的位置, 周围的情况.
const BLANK = " " // 空白// 根据棋子的位置,初始化华容道对象中的数组// 将棋子的code填入棋盘上对应的位置中func (this *Klotski) initArray() { arr := this.Array // 0-9 棋子 for i := 0; i < 10; i++ { chessman := this.Chessmen[i] // 占据2个格子的棋子,根据棋子方向,填入code if chessman.GridNums == 2 { arr[chessman.StartPoint[0]][chessman.StartPoint[1]] = chessman.Code if chessman.Direction == 0 { // 总坐标 + 1 arr[chessman.StartPoint[0] + 1][chessman.StartPoint[1]] = chessman.Code }else { // 横坐标 + 1 arr[chessman.StartPoint[0]][chessman.StartPoint[1] + 1] = chessman.Code } } // 占据1个格子的棋子,在起点位置填入code if chessman.GridNums == 1 { arr[chessman.StartPoint[0]][chessman.StartPoint[1]] = chessman.Code } // 占据4个格子的棋子,填入code if chessman.GridNums == 4 { arr[chessman.StartPoint[0]][chessman.StartPoint[1]] = chessman.Code arr[chessman.StartPoint[0] + 1][chessman.StartPoint[1]] = chessman.Code arr[chessman.StartPoint[0]][chessman.StartPoint[1] + 1] = chessman.Code arr[chessman.StartPoint[0] + 1][chessman.StartPoint[1] + 1] = chessman.Code } } // 填入空白 for i := 0; i < len(arr); i++ { for j := 0; j < len(arr[0]); j++ { if arr[i][j] == "" { arr[i][j] = BLANK } } }}
常量BLANK的目的是代表2个空格, 让棋盘显示的好看一点.
开始游戏前要初始化,返回一个棋盘对象
// 返回一个华容道func NewKlotski() *Klotski { // 1. 初始化棋子的位置 klotski := initKlotski() // 2. 初始化数组 klotski.initArray() return klotski}
接收到用户的输入后,要移动棋子.
移动棋子时要注意会不会越界, 可不可以移动, 移动后的二维数组又会变成什么样.
move方法没有分解, 代码很长,这里就只给出函数签名, 最后会附上源码.
move时华容道对象的一个方法. 接收棋子的ID和移动方向.返回值是是否移动了棋子.
// 移动棋子的方法func (this *Klotski) move(chessID, direct int) bool
源码
package mainimport ( "fmt" "bytes" "os")const BLANK = " " // 空白// 棋子type Chessman struct { Name string `desc:"棋子的名字"` Code string `desc:"棋子在棋盘上的标志符"` StartPoint [2]int `desc:"起点"` GridNums int `desc:"格子数"` Direction int `desc:"方向, 0 代表竖着, 1 代表横着"`}// 华容道type Klotski struct { Array *[5][4]string `desc:"5*4的地图"` Chessmen map[int]*Chessman `desc:"存放棋子的字典"`}// 移动棋子的方法func (this *Klotski) move(chessID, direct int) bool { arr := this.Array chessman := this.Chessmen[chessID] x, y := chessman.StartPoint[0], chessman.StartPoint[1] // 1. 输入校验 if direct == 1 && x == 0 { // 向上移动时,x坐标要大于0 return false } if direct == 2 && x == 4 { // 向下移动时,x坐标要小于4 return false } if direct == 3 && y == 0 { // 向左移动时, y的坐标要大于0 return false } if direct == 4 && y == 3 { // 向左移动时, y的坐标要小于3 return false } // 2. 判断是否可以移动, 可以就移动 // 占据2个格子的棋子,根据棋子方向, 移动 isMove := false if chessman.GridNums == 2 { switch direct { case 1: // 上 if chessman.Direction == 0 { // 竖着 if arr[x-1][y] == BLANK { this.switchGrid(x, y, x-1, y) this.switchGrid(x+1, y, x, y) isMove = true } }else { // 横着 if arr[x-1][y] == BLANK && arr[x-1][y+1] == BLANK { this.switchGrid(x, y, x-1, y) this.switchGrid(x, y+1, x-1, y+1) isMove = true } } case 2: // 下 if chessman.Direction == 0 { // 竖着 if arr[x+2][y] == BLANK { this.switchGrid(x+1, y, x+2, y) this.switchGrid(x, y, x+1, y) isMove = true } }else { // 横着 if arr[x+1][y] == BLANK && arr[x+1][y+1] == BLANK { this.switchGrid(x, y, x+1, y) this.switchGrid(x, y+1, x+1, y+1) isMove = true } } case 3: // 左 if chessman.Direction == 0 { // 竖着 if arr[x][y-1] == BLANK && arr[x+1][y-1] == BLANK { this.switchGrid(x, y, x, y-1) this.switchGrid(x+1, y, x+1, y-1) isMove = true } }else { // 横着 if arr[x][y-1] == BLANK { this.switchGrid(x, y, x, y-1) this.switchGrid(x, y+1, x, y) isMove = true } } case 4: // 右 if chessman.Direction == 0 { // 竖着 if arr[x][y+1] == BLANK && arr[x+1][y+1] == BLANK { this.switchGrid(x, y, x, y+1) this.switchGrid(x+1, y, x+1, y+1) isMove = true } }else { // 横着 if arr[x][y+2] == BLANK { this.switchGrid(x, y+1, x, y+2) this.switchGrid(x, y, x, y+1) isMove = true } } } } // 占据1个格子的棋子, 移动 if chessman.GridNums == 1 { switch direct { case 1: // 上 if arr[x-1][y] == BLANK { arr[x][y], arr[x-1][y] = arr[x-1][y], arr[x][y] isMove = true } case 2: // 下 if arr[x+1][y] == BLANK { arr[x][y], arr[x+1][y] = arr[x+1][y], arr[x][y] isMove = true } case 3: // 左 if arr[x][y-1] == BLANK { arr[x][y], arr[x][y-1] = arr[x][y-1], arr[x][y] isMove = true } case 4: // 右 if arr[x][y+1] == BLANK { arr[x][y], arr[x][y+1] = arr[x][y+1], arr[x][y] isMove = true } } } // 占据4个格子的棋子, 移动 if chessman.GridNums == 4 { switch direct { case 1: // 上 if arr[x-1][y] == BLANK && arr[x-1][y+1] == BLANK { this.switchGrid(x, y, x-1, y) this.switchGrid(x, y+1, x-1, y+1) this.switchGrid(x+1, y, x, y) this.switchGrid(x+1, y+1, x, y+1) isMove = true } case 2: // 下 if arr[x+2][y] == BLANK && arr[x+2][y+1] == BLANK { this.switchGrid(x+1, y, x+2, y) this.switchGrid(x+1, y+1, x+2, y+1) this.switchGrid(x, y, x+1, y) this.switchGrid(x, y+1, x+1, y+1) isMove = true } case 3: // 左 if arr[x][y-1] == BLANK && arr[x+1][y] == BLANK { this.switchGrid(x, y, x, y-1) this.switchGrid(x+1, y, x+1, y-1) this.switchGrid(x, y+1, x, y) this.switchGrid(x+1, y+1, x+1, y) isMove = true } case 4: // 右 if arr[x][y+2] == BLANK && arr[x+1][y+2] == BLANK { this.switchGrid(x, y+1, x, y+2) this.switchGrid(x+1, y+1, x+1, y+2) this.switchGrid(x, y, x, y+1) this.switchGrid(x+1, y, x+1, y+1) isMove = true } } } // 3. 如果移动,修改棋子起点位置 if isMove { switch direct { case 1: // 上 chessman.StartPoint[0] = x - 1 case 2: // 下 chessman.StartPoint[0] = x + 1 case 3: // 左 chessman.StartPoint[1] = y - 1 case 4: // 右 chessman.StartPoint[1] = y + 1 } } // 4. 判断用户是否胜利 if this.Array[4][1] == "曹" && this.Array[4][2] == "曹" { fmt.Println("恭喜您胜利了!") fmt.Println("游戏退出.") os.Exit(0) } return isMove}// 交换数组中的(a, b) 和 (c, d)func (this *Klotski) switchGrid(a, b, c, d int) { this.Array[a][b], this.Array[c][d] = this.Array[c][d], this.Array[a][b]}// 将华容道地图变成可打印的字符串func (this *Klotski) String() string { var buffer bytes.Buffer for i := 0; i < 5; i++ { for j := 0; j < 4; j++ { buffer.WriteString(this.Array[i][j]) buffer.WriteString(" ") } buffer.WriteString("\n") } return buffer.String()}// 返回一个华容道func NewKlotski() *Klotski { // 1. 初始化棋子的位置 klotski := initKlotski() // 2. 初始化数组 klotski.initArray() return klotski}// 用于初始化华容道地图// 初始地图有很多种,所以将这部分提取出来,以后可扩展func initKlotski() *Klotski { return &Klotski{ Array: &[5][4]string{}, Chessmen: map[int]*Chessman{ 0: {Name: "曹操", Code: "曹", StartPoint: [2]int{0, 1}, GridNums: 4}, 1: {Name: "张飞", Code: "张", StartPoint: [2]int{0, 0}, GridNums: 2, Direction: 0}, 2: {Name: "关羽", Code: "关", StartPoint: [2]int{2, 1}, GridNums: 2, Direction: 1}, 3: {Name: "赵云", Code: "赵", StartPoint: [2]int{0, 3}, GridNums: 2, Direction: 0}, 4: {Name: "黄忠", Code: "黄", StartPoint: [2]int{2, 0}, GridNums: 2, Direction: 0}, 5: {Name: "马超", Code: "马", StartPoint: [2]int{2, 3}, GridNums: 2, Direction: 0}, 6: {Name: "兵", Code: "兵", StartPoint: [2]int{3, 1}, GridNums: 1}, 7: {Name: "卒", Code: "卒", StartPoint: [2]int{3, 2}, GridNums: 1}, 8: {Name: "勇", Code: "勇", StartPoint: [2]int{4, 0}, GridNums: 1}, 9: {Name: "士", Code: "士", StartPoint: [2]int{4, 3}, GridNums: 1}, }, }}// 根据棋子的位置,初始化华容道对象中的数组// 将棋子的code填入棋盘上对应的位置中func (this *Klotski) initArray() { arr := this.Array // 0-9 棋子 for i := 0; i < 10; i++ { chessman := this.Chessmen[i] // 占据2个格子的棋子,根据棋子方向,填入code if chessman.GridNums == 2 { arr[chessman.StartPoint[0]][chessman.StartPoint[1]] = chessman.Code if chessman.Direction == 0 { // 总坐标 + 1 arr[chessman.StartPoint[0] + 1][chessman.StartPoint[1]] = chessman.Code }else { // 横坐标 + 1 arr[chessman.StartPoint[0]][chessman.StartPoint[1] + 1] = chessman.Code } } // 占据1个格子的棋子,在起点位置填入code if chessman.GridNums == 1 { arr[chessman.StartPoint[0]][chessman.StartPoint[1]] = chessman.Code } // 占据4个格子的棋子,填入code if chessman.GridNums == 4 { arr[chessman.StartPoint[0]][chessman.StartPoint[1]] = chessman.Code arr[chessman.StartPoint[0] + 1][chessman.StartPoint[1]] = chessman.Code arr[chessman.StartPoint[0]][chessman.StartPoint[1] + 1] = chessman.Code arr[chessman.StartPoint[0] + 1][chessman.StartPoint[1] + 1] = chessman.Code } } // 填入空白 for i := 0; i < len(arr); i++ { for j := 0; j < len(arr[0]); j++ { if arr[i][j] == "" { arr[i][j] = BLANK } } }}// 游戏开始提示func init() { fmt.Println("华容道游戏开始...") fmt.Println("操作提示: 输入棋子的序号和移动方向,然后回车. 如 0 1 代表将曹操向上移动一格. 输入0 0或其他字符退出游戏.") fmt.Println("0.曹操 1.张飞 2.关羽 3.赵云 4.黄忠 5.马超 6.兵 7.卒 8.勇 9.士") fmt.Println("1.上 2.下 3.左 4.右") fmt.Println()}// 用户输入func input() (int, int) { fmt.Print("(棋子序号, 移动方向): ") chessID, direct := 0, 0 fmt.Scan(&chessID, &direct) // 判断游戏是否结束 if chessID == 0 && direct == 0 { fmt.Println("游戏退出.") os.Exit(0) } fmt.Println(chessID, direct) return chessID, direct}func main() { klotski := NewKlotski() fmt.Println(klotski.String()) fmt.Println() for { chessID, direct := input() isMove := klotski.move(chessID, direct) if isMove { fmt.Println(klotski.String()) }else { fmt.Println("被包围了,不能移动") } }}
设计一个文件格式保存华容道?
- 使用json, 将华容道klotski这个对象进行序列化.
golang的json模块可以做到. - txt文件, 保存棋子的ID和起点位置. 注: 起点位置是棋子最左上角的坐标.
如何自动完成处于某个状态的华容道游戏?
华容道游戏的下棋步骤分支很多, 有点像树的结构. 因为游戏是要尽可能找到最少步骤, 要求出最优解.所以分支限界算法可以处理这个问题.
阅读全文
0 0
- golang 实现华容道
- 实现华容道
- 华容道
- 华容道
- 华容道
- 华容道
- 华容道!
- 华容道
- 华容道
- 华容道
- cocos2dx c++实现小游戏--华容道
- J2Me华容道游戏的实现(转载)
- 【java】华容道游戏设计与搜索算法实现
- Golang实现Llog日志
- GOLANG 实现的 fastcgi
- Golang实现的红黑树
- avl树 golang实现
- golang实现快速排序
- mysql 密码修改过后忘记了重置的方法
- yii2 gii模块自动生成代码,让双手更自由
- input[file]标签的accept=”image/*”属性响应很慢的解决办法
- nodeJs -- 基于Express、superagent 和 cheerio
- Trailing Zeroes (III)
- golang 实现华容道
- oracle检查数据库是否有坏块的命令
- linux下crontab
- 联想 Newifi mini Y1 Padavan固件设置5Ghz桥接
- AI时代将给人来带来的利与弊
- 第7章 Scrapy突破反爬虫的限制
- android UiAutomator获取视频播放进度的方法
- Java 四种线程池的用法分析
- linux export