Golang控制台百⾏代码贪吃蛇⼩游戏
go get gitee/redfire0922/snakego
最终效果
其中最关键的还是对系统函数的调⽤。因为golang读取⽤户输⼊都是按整⾏读取。也就是说⽤户在输⼊数据按下回车键之前,golang是⼀点点数据都读取不到。这对交互稍微丰富点的程序来说⼤⼤限制了想象⼒。其次是对输⼊位置的控制。普通情况下要录⼊⼀份类似表单的数据,golang只能⼀问⼀答录⼊。本⽂正是给出了另外⼀种⽅案。
// +bulid windows
package main
import (
"syscall"
"unsafe"
)
type (
SHORT int16
DWORD uint32
WORD  uint16
BOOL  int32
WCHAR uint16
COORD struct {
X SHORT
Y SHORT
}
CONSOLE_SCREEN_BUFFER_INFO struct {
Size              COORD
CursorPosition    COORD
Attributes        WORD
Window            SMALL_RECT
SMALL_RECT struct {
Left  SHORT
Top    SHORT
Right  SHORT
Bottom SHORT
}
INPUT_RECORD struct {
EventType WORD
_        [2]byte
Event    [16]byte
}
KEY_EVENT_RECORD struct {
KeyDown        BOOL
RepeatCount    WORD
VirtualKeyCode  WORD
VirtualScanCode WORD
UnicodeChar    WCHAR
ControlKeyState DWORD
}
)
const (
VK_ARROW_UP    = 0x26
VK_ARROW_DOWN  = 0x28
VK_ARROW_LEFT  = 0x25
VK_ARROW_RIGHT = 0x27
)
var (
Stdout  syscall.Handle
Stdin  syscall.Handle
tmp_arg DWORD
)
func (this SMALL_RECT) uintptr() uintptr {
return uintptr(*(*int32)(unsafe.Pointer(&this)))
}
func (this COORD) uintptr() uintptr {
return uintptr(*(*int32)(unsafe.Pointer(&this)))
}
//获取输⼊,输出handle
func InitSyscall() {
cout, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) if err != nil {
panic(err)
}
Stdout = cout
cin, err := syscall.Open("CONIN$", syscall.O_RDWR, 0)
if err != nil {
panic(err)
}
Stdin = cin
}
//获取光标位置
func GetConsoleCursorPosition() COORD {
var tmp_info CONSOLE_SCREEN_BUFFER_INFO
err := GetConsoleScreenBufferInfo(&tmp_info)
return tmp_info.CursorPosition
}
//获取控制台信息
func GetConsoleScreenBufferInfo(info *CONSOLE_SCREEN_BUFFER_INFO) (err error) {
r0, _, e1 := proc_get_console_screen_buffer_info.Call(uintptr(Stdout), uintptr(unsafe.Pointer(info)))
if int(r0) == 0 {
if e1 != nil {
err = e1
} else {
err = syscall.EINVAL
}
}
return
}
//设置光标位置新光标位置必须在已有数据的控制台Buffer范围内。
func SetConsoleCursorPosition(pos COORD) (err error) {
r0, _, e1 := proc_set_console_cursor_position.Call(uintptr(Stdout), pos.uintptr())
if int(r0) == 0 {
if e1 != nil {
err = e1
} else {
err = syscall.EINVAL
}
}
return
}
//读取⽤户输⼊
func ReadConsoleInput(record *INPUT_RECORD) (err error) {
r0, _, e1 := proc_read_console_input.Call(uintptr(Stdin), uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&tmp_arg))) if int(r0) == 0 {
if e1 != nil {
err = e1
} else {
err = syscall.EINVAL
}
}
return
}
//等待事件 WaitForMultipleObjects等待Windows中的所有的内核对象(关于该函数的描述和例⼦见MSDN)
func WaitForMultipleObjects(objects []syscall.Handle) (err error) {
r0, _, e1 := syscall.Syscall6(proc_wait_for_multiple_objects.Addr(),
4, uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])),
0, 0xFFFFFFFF, 0, 0)
if uint32(r0) == 0xFFFFFFFF {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
proc_get_console_screen_buffer_info = kernel32.NewProc("GetConsoleScreenBufferInfo")
proc_set_console_cursor_position    = kernel32.NewProc("SetConsoleCursorPosition")
proc_read_console_input            = kernel32.NewProc("ReadConsoleInputW")
proc_wait_for_multiple_objects      = kernel32.NewProc("WaitForMultipleObjects")
以下是贪吃蛇的实现,也可以说是上⾯系统函数的调⽤例⼦package main
import (
"container/list"
"errors"
"fmt"
"strings"
"syscall"
"time"
"unsafe"
)
var (
height = 30//整体⾼ 30⾏
width  = 30//整体宽 30*2字符宽
origPos  COORD
workArea SMALL_RECT //活动区间
food    SHORT //⾷物占⽤块
empty    = make(map[SHORT]bool) //空⽩块集合
snake    = list.New() //蛇占⽤块
)
func main() {
InitSyscall()
StartAnimation()
snakeHead := popCell()
snake.PushFront(snakeHead)
delete(empty, snakeHead)
food = popCell()
drawCell(snakeHead, "■")
drawCell(food, "$")
err := snakeRun()
SetConsoleCursorPosition(COORD{origPos.X, origPos.Y + 2})
if err != nil {
fmt.Println(err)
}
fmt.Printf("得分:%v\n", (snake.Len()-1)*100)
}
//游戏核⼼实现
func snakeRun() error {
keypress := make(chan rune)
go hanldeInput(keypress)
direction := 's'
for {
select {
case <-time.Tick(time.Millisecond * 200):
break
}
newhead := snake.Front().Value.(SHORT)
var kp rune
select {
case kp = <-keypress:
default:
}
if direction != 'd' {
direction = kp
}
case'w':
if direction != 's' {
direction = kp
}
case's':
if direction != 'w' {
direction = kp
}
case'd':
贪吃蛇的编程代码if direction != 'a' {
direction = kp
}
case'q':
return nil
}
switch direction {
case'a':
newhead = (newhead - (1 << 8)) case'w':
newhead = newhead + 1
case's':
newhead = newhead - 1
case'd':
newhead = (newhead + (1 << 8))        }
_, exists := empty[newhead]
if exists {
snake.PushFront(newhead)
delete(empty, newhead)
drawCell(newhead, "■")
if food != newhead {
back := snake.Back()
snake.Remove(back)
tail := back.Value.(SHORT)
empty[tail] = true
drawCell(tail, " ")
} else {
food = popCell()
if food == -1 {
return errors.New("游戏结束")                } else {
drawCell(food, "$")
}
}
} else {
//游戏失败
return errors.New("游戏失败")
}
}
return nil
}
//开场动画
func StartAnimation() {
for _, ch := range []string{"■", "  "} {
for i := 0; i < height; i++ {
for n := 0; n < width; n++ {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。