1. package main
    2. import (
    3. "fmt"
    4. "math/rand"
    5. "os"
    6. "time"
    7. )
    8. /*
    9. #include <windows.h>
    10. #include <conio.h>
    11. // 使用了WinAPI来移动控制台的光标
    12. void gotoxy(int x,int y)
    13. {
    14. COORD c;
    15. c.X=x,c.Y=y;
    16. SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
    17. }
    18. // 从键盘获取一次按键,但不显示到控制台
    19. int direct()
    20. {
    21. return _getch();
    22. }
    23. */
    24. import "C" // go中可以嵌入C语言的函数
    25. // 表示光标的位置
    26. type loct struct {
    27. i, j int
    28. }
    29. var (
    30. area = [20][20]byte{} // 记录了蛇、食物的信息
    31. food bool // 当前是否有食物
    32. lead byte // 当前蛇头移动方向
    33. head loct // 当前蛇头位置
    34. tail loct // 当前蛇尾位置
    35. size int // 当前蛇身长度
    36. )
    37. // 随机生成一个位置,来放置食物
    38. func place() loct {
    39. k := rand.Int() % 400
    40. return loct{k / 20, k % 20}
    41. }
    42. // 用来更新控制台的显示,在指定位置写字符,使用错误输出避免缓冲
    43. func draw(p loct, c byte) {
    44. C.gotoxy(C.int(p.i*2+4), C.int(p.j+2))
    45. fmt.Fprintf(os.Stderr, "%c", c)
    46. }
    47. func init() {
    48. // 初始化蛇的位置和方向、首尾;初始化随机数
    49. head, tail = loct{4, 4}, loct{4, 4}
    50. lead, size = 'R', 1
    51. area[4][4] = 'H'
    52. rand.Seed(int64(time.Now().Unix()))
    53. // 输出初始画面
    54. fmt.Fprintln(os.Stderr, `
    55. #-----------------------------------------#
    56. | |
    57. | |
    58. | |
    59. | |
    60. | * |
    61. | |
    62. | |
    63. | |
    64. | |
    65. | |
    66. | |
    67. | |
    68. | |
    69. | |
    70. | |
    71. | |
    72. | |
    73. | |
    74. | |
    75. | |
    76. #-----------------------------------------#
    77. `)
    78. // 我们使用一个单独的go程来捕捉键盘的动作,因为是单独的,不怕阻塞
    79. go func() {
    80. for { // 函数只写入lead,外部只读取lead,无需设锁
    81. switch byte(C.direct()) {
    82. case 72:
    83. lead = 'U'
    84. case 75:
    85. lead = 'L'
    86. case 77:
    87. lead = 'R'
    88. case 80:
    89. lead = 'D'
    90. case 32:
    91. lead = 'P'
    92. }
    93. }
    94. }()
    95. }
    96. func main() { // 主程序
    97. for { // 程序更新周期,400毫秒
    98. time.Sleep(time.Millisecond * 400) // 暂停,还是要有滴
    99. if lead == 'P' {
    100. continue
    101. } // 放置食物
    102. if !food {
    103. give := place()
    104. if area[give.i][give.j] == 0 { // 食物只能放在空闲位置
    105. area[give.i][give.j] = 'F'
    106. draw(give, '$') // 绘制食物
    107. food = true
    108. }
    109. } // 我们在蛇头位置记录它移动的方向
    110. area[head.i][head.j] = lead // 根据lead来移动蛇头
    111. switch lead {
    112. case 'U':
    113. head.j--
    114. case 'L':
    115. head.i--
    116. case 'R':
    117. head.i++
    118. case 'D':
    119. head.j++
    120. } // 判断蛇头是否出界
    121. if head.i < 0 || head.i >= 20 || head.j < 0 || head.j >= 20 {
    122. C.gotoxy(0, 23) // 让光标移动到画面下方
    123. break // 跳出死循环
    124. } // 获取蛇头位置的原值,来判断是否撞车,或者吃到食物
    125. eat := area[head.i][head.j]
    126. if eat == 'F' { // 吃到食物
    127. food = false
    128. // 增加蛇的尺寸,并且不移动蛇尾
    129. size++
    130. } else if eat == 0 { // 普通移动
    131. draw(tail, ' ') // 擦除蛇尾
    132. // 注意我们记录了它移动的方向
    133. dir := area[tail.i][tail.j]
    134. // 我们需要擦除蛇尾的记录
    135. area[tail.i][tail.j] = 0
    136. // 移动蛇尾
    137. switch dir {
    138. case 'U':
    139. tail.j--
    140. case 'L':
    141. tail.i--
    142. case 'R':
    143. tail.i++
    144. case 'D':
    145. tail.j++
    146. }
    147. } else { // 撞车了
    148. C.gotoxy(0, 23)
    149. break
    150. }
    151. draw(head, '*') // 绘制蛇头
    152. } // 收尾了
    153. switch {
    154. case size < 22:
    155. fmt.Fprintf(os.Stderr, "Faild! You've eaten %d $\\n", size-1)
    156. case size < 42:
    157. fmt.Fprintf(os.Stderr, "Try your best! You've eaten %d $\\n", size-1)
    158. default:
    159. fmt.Fprintf(os.Stderr, "Congratulations! You've eaten %d $\\n", size-1)
    160. }
    161. }
    162. "fmt"
    163. "math/rand"
    164. "os"
    165. "time"
    166. )
    167. /*
    168. #include <windows.h>
    169. #include <conio.h>
    170. // 使用了WinAPI来移动控制台的光标
    171. void gotoxy(int x,int y)
    172. {
    173. COORD c;
    174. c.X=x,c.Y=y;
    175. SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
    176. }
    177. // 从键盘获取一次按键,但不显示到控制台
    178. int direct()
    179. {
    180. return _getch();
    181. }
    182. */
    183. import "C" // go中可以嵌入C语言的函数
    184. // 表示光标的位置
    185. type loct struct {
    186. i, j int
    187. }
    188. var (
    189. area = [20][20]byte{} // 记录了蛇、食物的信息
    190. food bool // 当前是否有食物
    191. lead byte // 当前蛇头移动方向
    192. head loct // 当前蛇头位置
    193. tail loct // 当前蛇尾位置
    194. size int // 当前蛇身长度
    195. )
    196. // 随机生成一个位置,来放置食物
    197. func place() loct {
    198. k := rand.Int() % 400
    199. return loct{k / 20, k % 20}
    200. }
    201. // 用来更新控制台的显示,在指定位置写字符,使用错误输出避免缓冲
    202. func draw(p loct, c byte) {
    203. C.gotoxy(C.int(p.i*2+4), C.int(p.j+2))
    204. fmt.Fprintf(os.Stderr, "%c", c)
    205. }
    206. func init() {
    207. // 初始化蛇的位置和方向、首尾;初始化随机数
    208. head, tail = loct{4, 4}, loct{4, 4}
    209. lead, size = 'R', 1
    210. area[4][4] = 'H'
    211. rand.Seed(int64(time.Now().Unix()))
    212. // 输出初始画面
    213. fmt.Fprintln(os.Stderr, `
    214. #-----------------------------------------#
    215. | |
    216. | |
    217. | |
    218. | |
    219. | * |
    220. | |
    221. | |
    222. | |
    223. | |
    224. | |
    225. | |
    226. | |
    227. | |
    228. | |
    229. | |
    230. | |
    231. | |
    232. | |
    233. | |
    234. | |
    235. #-----------------------------------------#
    236. `)
    237. // 我们使用一个单独的go程来捕捉键盘的动作,因为是单独的,不怕阻塞
    238. go func() {
    239. for { // 函数只写入lead,外部只读取lead,无需设锁
    240. switch byte(C.direct()) {
    241. case 72:
    242. lead = 'U'
    243. case 75:
    244. lead = 'L'
    245. case 77:
    246. lead = 'R'
    247. case 80:
    248. lead = 'D'
    249. case 32:
    250. lead = 'P'
    251. }
    252. }
    253. }()
    254. }
    255. func main() { // 主程序
    256. for { // 程序更新周期,400毫秒
    257. time.Sleep(time.Millisecond * 400) // 暂停,还是要有滴
    258. if lead == 'P' {
    259. continue
    260. } // 放置食物
    261. if !food {
    262. give := place()
    263. if area[give.i][give.j] == 0 { // 食物只能放在空闲位置
    264. area[give.i][give.j] = 'F'
    265. draw(give, '$') // 绘制食物
    266. food = true
    267. }
    268. } // 我们在蛇头位置记录它移动的方向
    269. area[head.i][head.j] = lead // 根据lead来移动蛇头
    270. switch lead {
    271. case 'U':
    272. head.j--
    273. case 'L':
    274. head.i--
    275. case 'R':
    276. head.i++
    277. case 'D':
    278. head.j++
    279. } // 判断蛇头是否出界
    280. if head.i < 0 || head.i >= 20 || head.j < 0 || head.j >= 20 {
    281. C.gotoxy(0, 23) // 让光标移动到画面下方
    282. break // 跳出死循环
    283. } // 获取蛇头位置的原值,来判断是否撞车,或者吃到食物
    284. eat := area[head.i][head.j]
    285. if eat == 'F' { // 吃到食物
    286. food = false
    287. // 增加蛇的尺寸,并且不移动蛇尾
    288. size++
    289. } else if eat == 0 { // 普通移动
    290. draw(tail, ' ') // 擦除蛇尾
    291. // 注意我们记录了它移动的方向
    292. dir := area[tail.i][tail.j]
    293. // 我们需要擦除蛇尾的记录
    294. area[tail.i][tail.j] = 0
    295. // 移动蛇尾
    296. switch dir {
    297. case 'U':
    298. tail.j--
    299. case 'L':
    300. tail.i--
    301. case 'R':
    302. tail.i++
    303. case 'D':
    304. tail.j++
    305. }
    306. } else { // 撞车了
    307. C.gotoxy(0, 23)
    308. break
    309. }
    310. draw(head, '*') // 绘制蛇头
    311. } // 收尾了
    312. switch {
    313. case size < 22:
    314. fmt.Fprintf(os.Stderr, "Faild! You've eaten %d $\\n", size-1)
    315. case size < 42:
    316. fmt.Fprintf(os.Stderr, "Try your best! You've eaten %d $\\n", size-1)
    317. default:
    318. fmt.Fprintf(os.Stderr, "Congratulations! You've eaten %d $\\n", size-1)
    319. }
    320. }

    Golang 实现控制台贪吃蛇 - 图1