#### 总体思路
使用链表来表示蛇,蛇吃蛋的时候,在表头插入新的节点。
关键点:每次移动只需要确定表示蛇头的节点的移动是否合法以及应该移动到哪个位置,其他节点只能移动到前置节点之前所在的位置。
二维数组表示游戏地图,数组有四种元素:
1. 蛇头所在位置
2. 蛇身所在位置
3. 蛇要去吃的彩蛋... 所在位置
4. 空地
下面介绍实现思路和流程,代码的github链接为:
https://github.com/mikellxy/little_pineapple
下面是游戏截图("4"代表蛇头,"1"代表蛇身,"2"代表彩蛋)...
```
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0
0 0 0 1 0 0 0 0 0
0 0 0 1 0 0 2 0 0
0 0 0 4 0 0 0 0 0
strike edge error, Game Over!
```
```
0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0
0 0 0 1 1 1 1 4 0
0 0 0 0 0 0 0 2 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
you win, Game Over!
```
#### 实现细节
1. 链表节点,需要记录在游戏地图上的坐标
```
type Node struct {
Next *Node
xCoor int // x coordinate point
yCoor int // y coordinate point
}
```
2. 链表
记录蛇头和游戏地图的一些信息。定义一个结构体持有表头和移动方向等,可以让链表的整体使用更加方便。
```
type List struct {
Head *Node
Len int
WinLen int
GameMap [][]string
LeftLimit int
RightLimit int
UpLimit int
DownLimit int
AutoDir string
GameOver bool
MilliSeconds int
sync.Mutex
}
```
3. 初始化snake和地图,传入地图的宽度和高度,初始移动方向,吃到多少个彩蛋胜出,间隔多少毫秒移动一次
`func NewSnake(winLen int, autoDir string, gmWidth, gmHeight, ms int) *List`
4. 周期性的进行移动,每次移动后刷新标准输出的游戏地图
```
func (l *List) Start() {
l.showGameMap()
go l.getInput()
for {
time.Sleep(time.Duration(l.MilliSeconds) * time.Millisecond)
err := l.AutoMove()
if err != nil {
fmt.Println("")
l.GameOver = true
if err == errWin {
l.showGameMap()
}
fmt.Printf("%s%s, Game Over!\n\n", " ", err.Error())
return
}
l.showGameMap()
}
}
```
5. 起一个go routine获取用户输入的移动方向,每次周期性移动的方向是用户最近一次输入的方向或者初始化方向
`func (l *List) getInput()`
6. 每次移动,需要做三件事情: 校验移动是否合法,确定移动后蛇头的位置并移动,依次移动剩下的链表节点
不合法的情况有:撞到地图边缘、撞到自己、往上次移动的反方向移动
移动后需要校验:判断链表长度,看是否胜出游戏。
```
func (l *List) PrepareMove(dir string) (int, int, error)
func (l *List) Move(dir string) error
func (l *List) moveOneByOne(nextX, nextY int)
```
有疑问加站长微信联系(非本文作者)