[码云地址](https://gitee.com/janbar/GluttonousSnake)
下面是源码:
```go
package main
import (
"container/list"
"fmt"
"math/rand"
"time"
"github.com/jan-bar/golibs"
)
const (
LengthX = 20 /* 矩阵行数 */
LengthY = 40 /* 矩阵列数 */
NoneFlag = 0 /* 空白标识 */
FoodFlag = 1 /* 食物标识 */
BodyFlag = 2 /* 蛇身标识 */
)
var (
MatrixData [LengthX][LengthY]byte /* 0:表示空位,1表示食物,2表示蛇 */
api *golibs.Win32Api
)
/**
* 初始化
**/
func init() {
api = golibs.NewWin32Api()
api.Clear() /* 清屏 */
rand.Seed(time.Now().UnixNano()) /* 初始化随机数种子 */
api.SetWindowText("贪吃蛇") /* 设置标题 */
api.CenterWindowOnScreen(550, 400) /* 居中显示,并设置窗口大小 */
api.ShowHideCursor(false) /* 隐藏光标 */
var i int
api.GotoXY(1, 0)
for i = 0; i < LengthY; i++ {
fmt.Print("-") /* 画顶部横线 */
}
api.GotoXY(1, LengthX+1)
for i = 0; i < LengthY; i++ {
fmt.Print("-") /* 画底部横线 */
}
for i = 1; i <= LengthX; i++ {
api.GotoXY(0, i)
fmt.Print("|") /* 画左边竖线 */
api.GotoXY(LengthY+1, i)
fmt.Print("|") /* 画右边竖线 */
}
}
/**
* 主程序
**/
func main() {
var (
headPos = golibs.Coord{LengthX / 2, LengthY / 2}
dirNew int32 = golibs.KeyUp
dirOld int32 = golibs.KeyUp
direction = make(chan int32)
tLoop = time.NewTimer(time.Second)
Snake = list.New() /* 创建蛇身链表 */
score = 0 /* 吃一个食物加1分 */
)
DrawSnake(headPos, BodyFlag)
Snake.PushFront(headPos) /* 链表存储蛇头位置 */
RandFood() /* 初始一个食物 */
PrintScore(score) /* 打印初始分数 */
go func() {
for { /* 此处只往通道丢获取的按键值 */
direction <- golibs.WaitKeyBoard()
}
}()
for {
headPos = Snake.Front().Value.(golibs.Coord) /* 每次取蛇头位置 */
select {
case dir, ok := <-direction:
tLoop.Stop() /* 避免改变方向时定时的循环也移动一下蛇身 */
if ok {
dirNew = dir /* 得到新的方向 */
}
case <-tLoop.C: /* 这里,每秒刷新游戏 */
}
tLoop.Reset(time.Second) /* 重置间隔时间 */
switch dirNew {
case golibs.KeyUp: /* 新方向朝上 */
if dirOld == golibs.KeyDown {
dirNew = golibs.KeyDown
headPos.X++ /* 旧方向如果朝下,则继续向下走 */
} else { /* 旧方向为: 左右上 */
dirOld = golibs.KeyUp
headPos.X--
}
case golibs.KeyDown:
if dirOld == golibs.KeyUp {
dirNew = golibs.KeyUp
headPos.X-- /* 旧方向如果朝上,则继续向上走 */
} else { /* 旧方向为: 左右下 */
dirOld = golibs.KeyDown
headPos.X++
}
case golibs.KeyLeft:
if dirOld == golibs.KeyRight {
dirNew = golibs.KeyRight
headPos.Y++ /* 旧方向如果朝右,则继续向右走 */
} else { /* 旧方向为: 上下左 */
dirOld = golibs.KeyLeft
headPos.Y--
}
case golibs.KeyRight:
if dirOld == golibs.KeyLeft {
dirNew = golibs.KeyLeft
headPos.Y-- /* 旧方向如果朝左,则继续向左走 */
} else { /* 旧方向为: 上下右 */
dirOld = golibs.KeyRight
headPos.Y++
}
}
if headPos.X < 0 || headPos.Y < 0 || headPos.X >= LengthX || headPos.Y >= LengthY || MatrixData[headPos.X][headPos.Y] == BodyFlag {
api.GotoXY(LengthY+3, 7)
fmt.Print("你输了,按回车退出!")
break /* 越界了,这个位置是蛇身,游戏结束 */
}
if MatrixData[headPos.X][headPos.Y] == NoneFlag {
var end = Snake.Back() /* 如果头部为空白,则尾部也要去掉一个 */
DrawSnake(end.Value.(golibs.Coord), NoneFlag) /* 将蛇尾置位为空白 */
Snake.Remove(end) /* 下一步是空白,尾部删除一个蛇尾 */
} else { /* 吃到一个食物,则需要再初始化一个食物 */
score++ /* 吃一个食物加1分 */
PrintScore(score)
RandFood()
}
Snake.PushFront(headPos) /* 头部加入链表 */
DrawSnake(headPos, BodyFlag)
}
fmt.Scanln() /* 避免一闪而逝 */
}
/**
* 打印分数
**/
func PrintScore(score int) {
api.GotoXY(LengthY+3, 5)
fmt.Print("分数:", score)
}
/**
* 在指定位置画整个界面数据
* 包括空白,蛇,食物
**/
func DrawSnake(pos golibs.Coord, dType byte) {
MatrixData[pos.X][pos.Y] = dType /* 标记这个位置的类型 */
api.GotoXY(pos.Y+1, pos.X+1)
switch dType {
case NoneFlag:
fmt.Print(" ")
case BodyFlag:
fmt.Print("#")
case FoodFlag:
fmt.Print("+")
}
}
/**
* 在空白位置
* 随机画一个食物
**/
func RandFood() {
var i, j int
for {
i = rand.Intn(LengthX) /* 随机一个X值 */
j = rand.Intn(LengthY) /* 随机一个Y值 */
if NoneFlag == MatrixData[i][j] {
DrawSnake(golibs.Coord{i, j}, FoodFlag)
break /* 空白位置画一个食物 */
}
}
}
/** 下面是对链表库的解释
func (e *Element) Next() *Element //返回该元素的下一个元素,如果没有下一个元素则返回nil
func (e *Element) Prev() *Element//返回该元素的前一个元素,如果没有前一个元素则返回nil。
func New() *List //返回一个初始化的list
func (l *List) Back() *Element //获取list l的最后一个元素
func (l *List) Front() *Element //获取list l的第一个元素
func (l *List) Init() *List //list l初始化或者清除list l
func (l *List) InsertAfter(v interface{}, mark *Element) *Element //在list l中元素mark之后插入一个值为v的元素,并返回该元素,如果mark不是list中元素,则list不改变。
func (l *List) InsertBefore(v interface{}, mark *Element) *Element//在list l中元素mark之前插入一个值为v的元素,并返回该元素,如果mark不是list中元素,则list不改变。
func (l *List) Len() int //获取list l的长度
func (l *List) MoveAfter(e, mark *Element) //将元素e移动到元素mark之后,如果元素e或者mark不属于list l,或者e==mark,则list l不改变。
func (l *List) MoveBefore(e, mark *Element)//将元素e移动到元素mark之前,如果元素e或者mark不属于list l,或者e==mark,则list l不改变。
func (l *List) MoveToBack(e *Element)//将元素e移动到list l的末尾,如果e不属于list l,则list不改变。
func (l *List) MoveToFront(e *Element)//将元素e移动到list l的首部,如果e不属于list l,则list不改变。
func (l *List) PushBack(v interface{}) *Element//在list l的末尾插入值为v的元素,并返回该元素。
func (l *List) PushBackList(other *List)//在list l的尾部插入另外一个list,其中l和other可以相等。
func (l *List) PushFront(v interface{}) *Element//在list l的首部插入值为v的元素,并返回该元素。
func (l *List) PushFrontList(other *List)//在list l的首部插入另外一个list,其中l和other可以相等。
func (l *List) Remove(e *Element) interface{}//如果元素e属于list l,将其从list中删除,并返回元素e的值。
**/
```
![Snake.gif](https://static.studygolang.com/190926/a8e776caa9a8177e20884b4f39c581c1.gif)
上面是效果图,里面win32api也是我参照另一个开源项目写的。
有疑问加站长微信联系(非本文作者)