Python命令行贪吃蛇实现:基于msvcrt的手动控制版本

Evan
Evan
发布时间:2026-10-13| 2人浏览过
原创

本文介绍在Python 3.6.1及Win10环境下,利用msvcrt模块实现命令行贪吃蛇游戏的完整代码。由于Python缺乏C语言的kbhit()函数,本版本采用wsad键手动控制蛇的移动方向,而非自动运行。文章详细解析了Board、Snake和Game三个核心类的逻辑,并总结了Python中字典模拟Switch、引用复制与深拷贝的区别以及self关键字的使用要点,适合Python初学者练习基础语法与面向对象编程。

近期正在学习Python,为了巩固所学知识并练习基础语法,决定动手编写一个项目。虽然命令行贪吃蛇通常是C语言初学者的经典练习项目,但在没有找到其他合适练手项目的情况下,决定先通过实现一个简单的贪吃蛇游戏来熟悉Python的基本语法结构。

在Python中监听键盘输入相对复杂,因为标准库中没有像C语言那样便捷的kbhit()函数。因此,这个版本的贪吃蛇游戏不具备自动移动功能,需要用户通过按键控制。具体的运行效果如下所示:

游戏规则设定如下:使用字符#绘制游戏边框,用*表示随机生成的食物,小写o代表蛇身,大写O代表蛇头。玩家需通过键盘输入w、s、a、d四个方向键来控制蛇的移动方向。

开发所使用的Python版本为3.6.1。

测试运行的系统环境为Windows 10。

代码主要包含以下类结构:

board类:负责管理棋盘,即游戏的主要显示区域。

snake类:负责管理贪吃蛇的状态,通过记录蛇身体每个节点的位置坐标来维护蛇的当前形态。

game类:负责整个游戏的逻辑控制与流程调度。

原本计划单独创建一个food类,但考虑到食物仅需存储一个坐标位置以及生成逻辑,直接使用列表(list)存储坐标并在game类中处理生成逻辑更为简洁,从架构逻辑上看并未产生明显问题。

以下是完整的源代码实现:

# Write By Guobao
# 2017/4//7
#
# 贪吃蛇
# 用#做边界,*做食物,o做身体和头部
# python 3.6.1

import copy
import random
import os
import msvcrt

# the board class, used to put everything
class board:

 __points =[]

 def __init__(self):
 self.__points.clear()
 for i in range(22):
 line = []
 if i == 0 or i == 21:
 for j in range(22):
  line.append('#')
 else:
 line.append('#')
 for j in range(20):
  line.append(' ')
 line.append('#')
 self.__points.append(line)

 def getPoint(self, location):
 return self.__points[location[0]][location[1]]

 def clear(self):
 self.__points.clear()
 for i in range(22):
 line = []
 if i == 0 or i == 21:
 for j in range(22):
  line.append('#')
 else:
 line.append('#')
 for j in range(20):
  line.append(' ')
 line.append('#')
 self.__points.append(line)

 def put_snake(self, snake_locations):
 # clear the board
 self.clear()

 # put the snake points
 for x in snake_locations:
 self.__points[x[0]][x[1]] = 'o'

 # the head
 x = snake_locations[len(snake_locations) - 1]
 self.__points[x[0]][x[1]] = 'O'

 def put_food(self, food_location):
 self.__points[food_location[0]][food_location[1]] = '*'

 def show(self):
 os.system("cls")
 for i in range(22):
 for j in range(22):
 print(self.__points[i][j], end='')
 print()

# the snake class
class snake:
 __points = []

 def __init__(self):
 for i in range(1, 6):
 self.__points.append([1, i])

 def getPoints(self):
 return self.__points

 # move to the next position
 # give the next head
 def move(self, next_head):
 self.__points.pop(0)
 self.__points.append(next_head)

 # eat the food
 # give the next head
 def eat(self, next_head):
 self.__points.append(next_head)

 # calc the next state
 # and return the direction
 def next_head(self, direction='default'):

 # need to change the value, so copy it
 head = copy.deepcopy(self.__points[len(self.__points) - 1])

 # calc the "default" direction
 if direction == 'default':
 neck = self.__points[len(self.__points) - 2]
 if neck[0] > head[0]:
 direction = 'up'
 elif neck[0] < head[0]:
 direction = 'down'
 elif neck[1] > head[1]:
 direction = 'left'
 elif neck[1] < head[1]:
 direction = 'right'

 if direction == 'up':
 head[0] = head[0] - 1
 elif direction == 'down':
 head[0] = head[0] + 1
 elif direction == 'left':
 head[1] = head[1] - 1
 elif direction == 'right':
 head[1] = head[1] + 1
 return head

# the game
class game:

 board = board()
 snake = snake()
 food = []
 count = 0

 def __init__(self):
 self.new_food()
 self.board.clear()
 self.board.put_snake(self.snake.getPoints())
 self.board.put_food(self.food)

 def new_food(self):
 while 1:
 line = random.randint(1, 20)
 column = random.randint(1, 20)
 if self.board.getPoint([column, line]) == ' ':
 self.food = [column, line]
 return

 def show(self):
 self.board.clear()
 self.board.put_snake(self.snake.getPoints())
 self.board.put_food(self.food)
 self.board.show()


 def run(self):
 self.board.show()

 # the 'w a s d' are the directions
 operation_dict = {b'w': 'up', b'W': 'up', b's': 'down', b'S': 'down', b'a': 'left', b'A': 'left', b'd': 'right', b'D': 'right'}
 op = msvcrt.getch()

 while op != b'q':
 if op not in operation_dict:
 op = msvcrt.getch()
 else:
 new_head = self.snake.next_head(operation_dict[op])

 # get the food
 if self.board.getPoint(new_head) == '*':
  self.snake.eat(new_head)
  self.count = self.count + 1
  if self.count >= 15:
  self.show()
  print("Good Job")
  break
  else:
  self.new_food()
  self.show()

 # 反向一Q日神仙
 elif new_head == self.snake.getPoints()[len(self.snake.getPoints()) - 2]:
  pass

 # rush the wall
 elif self.board.getPoint(new_head) == '#' or self.board.getPoint(new_head) == 'o':
  print('GG')
  break

 # normal move
 else:
  self.snake.move(new_head)
  self.show()
 op = msvcrt.getch()

game().run()

在编写过程中记录了一些关键的技术笔记:

第一,Python语言标准语法中不包含Switch case语句,在实际开发中可以利用字典(dictionary)结构来模拟实现Switch case的功能。

第二,Python中的赋值运算符=执行的是引用复制而非值复制。若需要进行深拷贝,必须调用copy模块中的deepcopy()函数来实现。

第三,在类的成员函数内部访问成员变量时,必须显式使用self关键字进行引用,这与C++和Java等语言的处理方式存在显著差异。

如果您对Python游戏开发感兴趣,欢迎点击查看以下相关专题内容:

Python俄罗斯方块游戏代码合集

Python经典小游戏代码汇总

Python微信跳一跳游戏实现合集

更多有趣的经典小游戏实现专题,分享给大家参考:

C++经典小游戏代码汇总

JavaScript经典游戏代码合集

Java经典小游戏代码汇总

JavaScript经典小游戏代码合集

以上就是本文的全部内容,希望这些代码和解析能对各位的学习有所帮助。如果大家觉得有用,欢迎多多支持本站。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。