CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Day01-15/code/Day10/snake.py
Views: 729
from abc import ABCMeta, abstractmethod1from enum import Enum, unique2from random import randrange3from threading import Thread45import pygame678class Color(object):9"""颜色"""1011GRAY = (242, 242, 242)12BLACK = (0, 0, 0)13GREEN = (0, 255, 0)14PINK = (255, 20, 147)151617@unique18class Direction(Enum):19"""方向"""2021UP = 022RIGHT = 123DOWN = 224LEFT = 3252627class GameObject(object, metaclass=ABCMeta):28"""游戏中的对象"""2930def __init__(self, x=0, y=0, color=Color.BLACK):31"""32初始化方法3334:param x: 横坐标35:param y: 纵坐标36:param color: 颜色37"""38self._x = x39self._y = y40self._color = color4142@property43def x(self):44return self._x4546@property47def y(self):48return self._y4950@abstractmethod51def draw(self, screen):52"""53绘制5455:param screen: 屏幕56"""57pass585960class Wall(GameObject):61"""围墙"""6263def __init__(self, x, y, width, height, color=Color.BLACK):64"""65初始化方法6667:param x: 横坐标68:param y: 纵坐标69:param width: 宽度70:param height: 高度71:param color: 颜色72"""73super().__init__(x, y, color)74self._width = width75self._height = height7677@property78def width(self):79return self._width8081@property82def height(self):83return self._height8485def draw(self, screen):86pygame.draw.rect(screen, self._color,87(self._x, self._y, self._width, self._height), 4)888990class Food(GameObject):91"""食物"""9293def __init__(self, x, y, size, color=Color.PINK):94"""95初始化方法9697:param x: 横坐标98:param y: 纵坐标99:param size: 大小100:param color: 颜色101"""102super().__init__(x, y, color)103self._size = size104self._hidden = False105106def draw(self, screen):107if not self._hidden:108pygame.draw.circle(screen, self._color,109(self._x + self._size // 2, self._y + self._size // 2),110self._size // 2, 0)111self._hidden = not self._hidden112113114class SnakeNode(GameObject):115"""蛇身上的节点"""116117def __init__(self, x, y, size, color=Color.GREEN):118"""119初始化方法120121:param x: 横坐标122:param y: 纵坐标123:param size: 大小124:param color: 颜色125"""126super().__init__(x, y, color)127self._size = size128129@property130def size(self):131return self._size132133def draw(self, screen):134pygame.draw.rect(screen, self._color,135(self._x, self._y, self._size, self._size), 0)136pygame.draw.rect(screen, Color.BLACK,137(self._x, self._y, self._size, self._size), 1)138139140class Snake(GameObject):141"""蛇"""142143def __init__(self, x, y, size=20, length=5):144"""145初始化方法146147:param x: 横坐标148:param y: 纵坐标149:param size: 大小150:param length: 初始长度151"""152super().__init__()153self._dir = Direction.LEFT154self._nodes = []155self._alive = True156self._new_dir = None157for index in range(length):158node = SnakeNode(x + index * size, y, size)159self._nodes.append(node)160161@property162def dir(self):163return self._dir164165@property166def alive(self):167return self._alive168169@property170def head(self):171return self._nodes[0]172173def change_dir(self, new_dir):174"""175改变方向176177:param new_dir: 新方向178"""179if new_dir != self._dir and \180(self._dir.value + new_dir.value) % 2 != 0:181self._new_dir = new_dir182183def move(self):184"""移动"""185if self._new_dir:186self._dir, self._new_dir = self._new_dir, None187snake_dir = self._dir188x, y, size = self.head.x, self.head.y, self.head.size189if snake_dir == Direction.UP:190y -= size191elif snake_dir == Direction.RIGHT:192x += size193elif snake_dir == Direction.DOWN:194y += size195else:196x -= size197new_head = SnakeNode(x, y, size)198self._nodes.insert(0, new_head)199self._nodes.pop()200201def collide(self, wall):202"""203撞墙204205:param wall: 围墙206"""207head = self.head208if head.x < wall.x or head.x + head.size > wall.x + wall.width \209or head.y < wall.y or head.y + head.size > wall.y + wall.height:210self._alive = False211212def eat_food(self, food):213"""214吃食物215216:param food: 食物217218:return: 吃到食物返回True否则返回False219"""220if self.head.x == food.x and self.head.y == food.y:221tail = self._nodes[-1]222self._nodes.append(tail)223return True224return False225226def eat_self(self):227"""咬自己"""228for index in range(4, len(self._nodes)):229node = self._nodes[index]230if node.x == self.head.x and node.y == self.head.y:231self._alive = False232233def draw(self, screen):234for node in self._nodes:235node.draw(screen)236237238def main():239240def refresh():241"""刷新游戏窗口"""242screen.fill(Color.GRAY)243wall.draw(screen)244food.draw(screen)245snake.draw(screen)246pygame.display.flip()247248def handle_key_event(key_event):249"""处理按键事件"""250key = key_event.key251if key == pygame.K_F2:252reset_game()253elif key in (pygame.K_a, pygame.K_w, pygame.K_d, pygame.K_s):254if snake.alive:255if key == pygame.K_w:256new_dir = Direction.UP257elif key == pygame.K_d:258new_dir = Direction.RIGHT259elif key == pygame.K_s:260new_dir = Direction.DOWN261else:262new_dir = Direction.LEFT263snake.change_dir(new_dir)264265def create_food():266"""创建食物"""267unit_size = snake.head.size268max_row = wall.height // unit_size269max_col = wall.width // unit_size270row = randrange(0, max_row)271col = randrange(0, max_col)272return Food(wall.x + unit_size * col, wall.y + unit_size * row, unit_size)273274def reset_game():275"""重置游戏"""276nonlocal food, snake277food = create_food()278snake = Snake(250, 290)279280def background_task():281nonlocal running, food282while running:283if snake.alive:284refresh()285clock.tick(10)286if snake.alive:287snake.move()288snake.collide(wall)289if snake.eat_food(food):290food = create_food()291snake.eat_self()292293"""294class BackgroundTask(Thread):295296def run(self):297nonlocal running, food298while running:299if snake.alive:300refresh()301clock.tick(10)302if snake.alive:303snake.move()304snake.collide(wall)305if snake.eat_food(food):306food = create_food()307snake.eat_self()308"""309310wall = Wall(10, 10, 600, 600)311snake = Snake(250, 290)312food = create_food()313pygame.init()314screen = pygame.display.set_mode((620, 620))315pygame.display.set_caption('贪吃蛇')316# 创建控制游戏每秒帧数的时钟317clock = pygame.time.Clock()318running = True319# 启动后台线程负责刷新窗口和让蛇移动320# BackgroundTask().start()321Thread(target=background_task).start()322# 处理事件的消息循环323while running:324for event in pygame.event.get():325if event.type == pygame.QUIT:326running = False327elif event.type == pygame.KEYDOWN:328handle_key_event(event)329pygame.quit()330331332if __name__ == '__main__':333main()334335336