python3贪吃蛇_Python3实践之:用curses实现《贪食蛇》游戏

在这篇文章中,你将学会如何用40行Python代码,来实现一个简单的《贪食蛇》游戏。

我们在上期的文章中介绍了Python3的curses包的用法,并实现了一个控制台时钟。如果你对curses的用法还不熟,请先阅读这一期的文章。酸痛鱼:Python3实践之:控制台时钟​zhuanlan.zhihu.com

本文中用到的所有知识点,你可以在如下两篇文章中学习到。一个是python官方的教材,一个是这个教材的中文翻译,请任选一个阅读。

官网文章:https://docs.python.org/3/howto/curses.html​docs.python.org

中文翻译:CSDN-专业IT技术社区-登录​blog.csdn.net

0、问题描述

本期文章将继续深入介绍curses,并实现两个版本的控制台《贪食蛇》游戏。

第一个版本我们将直接运用我们上期学到的知识,实现一个文字(字符)版本的《贪食蛇》游戏。在文字版本中,我们将深入介绍《贪食蛇》游戏的实现细节,这些细节在第二个版本中,将不再重复介绍。

第二个版本,我们引入了色彩系统,我们将使用色块来代替字符,使得游戏更加美观和完善。

1、文字版本《贪食蛇》

这个版本的实现中,游戏窗口的范围是整个控制台。这样做是为了减少没必要的边界和位移处理,以便将重心放在游戏的核心逻辑的实现上。

由于游戏功能比较简单,代码量比较少(除去空行和注释,其实只有不到40行的代码),所以我将不会拆分讲解游戏的实现,所有的实现细节将以代码+注释的形式来呈现。如果你对代码有不能理解之处,请给我留言。

在贴代码之前,你可能需要了解curses的以下几个知识点:

# 关闭光标闪烁效果

curses.curs_set(0)

# 开启光标闪烁效果(默认为开启)

curses.curs_set(1)

# 键盘无响应超时

# 即调用 getch、getkey等方法是,最长的等待时间

# 在指定的时间内,如200ms,如果用户输入,则返回ERR或者抛出异常

stdscr.timeout(200)

# 等待用户的输入,返回一个整数,curses为每个键的字声明了一个常量

# 如果用户敲击回车键,key的值为10

# 左、右、下、上 四个箭头键的常量分别为:

# curses.KEY_LEFT, curses.KEY_RIGHT, curses.KEY_DOWN, curses.KEY_UP

key = stdscr.getch()

游戏效果如下图。我建议你在理解的基础上自己实现一下,并运行体验一下。如果你不太理解游戏的玩法,可能拷贝本文的代码,运行并体验游戏,以便加深对代码的理解。文版本游戏效果

代码来啦:

import curses

from random import randint

def game(stdscr):

curses.curs_set(0) # 关闭光标闪烁效果

h = curses.LINES

w = curses.COLS

stdscr.timeout(200) # 键盘无响应超时

# 蛇身的初始长度为4, snake[0]为蛇头

snake = [(h//2, x) for x in range(w//2 - 2, w//2 + 2)]

# 移动方向:下,上,左,右

dirs = [(1, 0), (-1, 0), (0, -1), (0, 1)]

cur_dir = 2 # 初始移动方向为“左”

food = None # 食物位置

# 展示初始的蛇身

for node in snake:

stdscr.addch(node[0], node[1], "+")

# 游戏循环

while True:

key = stdscr.getch()

if key == 10: # 监听到回车键,退出游戏

break

# 监听到 左、右、下、上 键,改变蛇的移动方向

if key in (curses.KEY_LEFT, curses.KEY_RIGHT, \

curses.KEY_DOWN, curses.KEY_UP):

cur_dir = key - curses.KEY_DOWN

# 如果食物被吃,重新生成食物,并展示

if not food:

food = (randint(0, h - 1), randint(0, w - 1))

while food in snake: # 食物不可生成蛇向上

food = (randint(0, h - 1), randint(0, w - 1))

stdscr.addch(food[0], food[1], "*", curses.A_BLINK)

# 新蛇头:new_head = snake[0] + dirs[cur_dir]

new_head = (snake[0][0] + dirs[cur_dir][0],

snake[0][1] + dirs[cur_dir][1])

# 蛇头超出屏蔽或者撞到自己,则判失败

if new_head[0] < 0 or new_head[0] >= h \

or new_head[1] < 0 or new_head[1] >= w \

or new_head in snake:

break

# 把新蛇头放入蛇链中, 并显示

snake.insert(0, new_head)

stdscr.addch(new_head[0], new_head[1], "+")

if new_head == food:

# 吃到食物,则蛇尾不移动,相当于吃到食物放到食尾

food = None

else:

# 未吃到食物,则将蛇尾弹出,并从屏幕中擦除

tail = snake.pop()

stdscr.addch(tail[0], tail[1], " ")

# 启动游戏

curses.wrapper(game)

2、色块版本《贪食蛇》

这个版本将以40x40的格子阵列作为游戏区。由于字符的高度是宽度的2倍,为了呈现正方形的色块,我们需要用两个字符来表示一个格子。加上游戏的边框,控制台至少要有84列和至少43行。如果你的控制台窗口太小,程序启动后会得到一个窗口太小的提示,游戏将无法进行。

每个字符都有前景颜色(即文字的颜色)和背景颜色,如果我们将前景颜色和背景颜色设置成一样的,那么我们将无法看到字符,而是看到一个纯色块,我们就利用这个原理来呈现一个色块的。字符前景和背景的设置如下代码所示:

# 初始化颜色1,前景为黑色,背景为白色

curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)

# 初始化颜色2,前景为白色,背景为黑色

curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK)

# 在屏幕中展示字符串,字黑底白(使用颜色1)

stdscr.addstr(0, 0, "Hello World!", curses.color_pair(1))

# 在屏幕中展示字符串,字白底黑(使用颜色2)

stdscr.addstr(0, 0, "Hola Mondo!", curses.color_pair(2))

游戏效果如图:色块版游戏效果

代码(没有注释)

import curses

from random import randint

class Snake:

def __init__(self):

self.score = 0

self.width = 80

self.height = 40

self.dirs = [(1, 0), (-1, 0) , (0, -2), (0, 2)]

self.dir = 3

self.snake = [(20, 44), (20, 42), (20, 40), (20, 38)]

self.food = None

def __call__(self, stdscr):

curses.curs_set(0)

self.stdscr = stdscr

self.win_h = curses.LINES

self.win_w = curses.COLS

self.start_x = (self.win_w - self.width) // 2

self.start_y = (self.win_h - self.height) // 2

self.score_y = self.start_y - 1

if self.win_w < 84 or self.win_h < 43:

self.stdscr.addstr(1, 0, "Screen size is less than 42x43. Please resize it")

self.stdscr.addstr(2, 0, "Press any key to exit")

self.stdscr.getch()

return

self.startUp()

self.runForever()

self.closeDown()

def draw(self, pos, color):

self.stdscr.addstr(self.start_y + pos[0], self.start_x + pos[1], self.node, color)

def startUp(self):

curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_BLACK)

curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_WHITE)

curses.init_pair(3, curses.COLOR_CYAN, curses.COLOR_CYAN)

curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_GREEN)

curses.init_pair(5, curses.COLOR_RED, curses.COLOR_WHITE)

self.sn_color = curses.color_pair(1)

self.bg_color = curses.color_pair(2)

self.fr_color = curses.color_pair(3)

self.fd_color = curses.color_pair(4)

self.failed_color = curses.color_pair(5)

self.node = " "

self.stdscr.addstr(self.score_y, self.start_x, "Score:0")

for y in range(self.height + 2):

for x in range(0, self.width + 4, 2):

if x in (0, self.width + 2) or y in (0, self.height + 1):

color = self.fr_color

else:

color = self.bg_color

self.draw((y, x), color)

for n in self.snake:

self.draw(n, self.sn_color)

self.stdscr.timeout(200)

def runForever(self):

while True:

key = self.stdscr.getch()

if key == 10:

break

if key in (curses.KEY_LEFT, curses.KEY_RIGHT, curses.KEY_DOWN, curses.KEY_UP):

self.dir = key - curses.KEY_DOWN

ok = self.gameLoop()

if not ok:

self.onFailed()

break

def closeDown(self):

self.stdscr.nodelay(False)

def onFailed(self):

self.stdscr.addstr(self.win_h//2 - 1, self.win_w//2 - 2, " Failed! ", self.failed_color)

self.stdscr.addstr(self.win_h//2, self.win_w//2 - 2, "Press Any Key To Exit", self.failed_color)

self.stdscr.timeout(5000)

self.stdscr.getch()

def gameLoop(self):

if not self.food:

self.food = randint(1, 40), 2 * randint(1, 40)

while self.food in self.snake:

self.food = randint(1, 40), 2 * randint(1, 40)

self.draw(self.food, self.fd_color)

new_head = self.snake[0][0] + self.dirs[self.dir][0], self.snake[0][1] + self.dirs[self.dir][1]

if new_head in self.snake or new_head[0] in (0, 41) or new_head[1] in (0, 82):

return False

if new_head == self.food:

self.food = None

else:

tail = self.snake.pop()

self.draw(tail, self.bg_color)

self.snake.insert(0, new_head)

self.draw(new_head, self.sn_color)

self.stdscr.addstr(self.score_y, self.start_x, "Score:{}".format(len(self.snake)))

return True

if __name__ == "__main__":

snake = Snake()

curses.wrapper(snake)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值