贪吃蛇小游戏再经典不过了,作为编程爱好者,代码编译的贪吃蛇,又能有怎样的成绩呢?
带着好奇,开始!
先做一个普通的贪吃蛇游戏
引入相关package
import pygame
定义相关配置变量
# 定义字体
font = pygame.font.Font(None, 36)# 定义颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)# 游戏窗口大小
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480# 贪吃蛇方格大小
CELL_SIZE = 20
初始化游戏和pygame窗口
# 初始化Pygame
pygame.init()# 创建游戏窗口
window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption('贪吃蛇')
定义游戏循环中的相关变量
# 定义贪吃蛇初始位置和速度
snake_x = WINDOW_WIDTH // 2
snake_y = WINDOW_HEIGHT // 2
snake_speed_x = CELL_SIZE
snake_speed_y = 0# 定义食物初始位置
food_x = random.randint(0, WINDOW_WIDTH - CELL_SIZE) // CELL_SIZE * CELL_SIZE
food_y = random.randint(0, WINDOW_HEIGHT - CELL_SIZE) // CELL_SIZE * CELL_SIZE# 定义贪吃蛇身体
snake_body = []
snake_length = 1# 游戏主循环
running = True
game_speed = 10
clock = pygame.time.Clock()
食物生成函数
def generate_food():while True:food_x = random.randint(0, WINDOW_WIDTH - CELL_SIZE) // CELL_SIZE * CELL_SIZEfood_y = random.randint(0, WINDOW_HEIGHT - CELL_SIZE) // CELL_SIZE * CELL_SIZEif (food_x, food_y) not in snake_body: #确保食物不会生成在蛇的身体breakreturn food_x, food_y
游戏主函数
while running:for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseelif event.type == pygame.KEYDOWN: #设置各方向移动if event.key == pygame.K_UP and snake_speed_y != CELL_SIZE:snake_speed_x = 0snake_speed_y = -CELL_SIZEelif event.key == pygame.K_DOWN and snake_speed_y != -CELL_SIZE:snake_speed_x = 0snake_speed_y = CELL_SIZEelif event.key == pygame.K_LEFT and snake_speed_x != CELL_SIZE:snake_speed_x = -CELL_SIZEsnake_speed_y = 0elif event.key == pygame.K_RIGHT and snake_speed_x != -CELL_SIZE:snake_speed_x = CELL_SIZEsnake_speed_y = 0elif event.key == pygame.K_w: #设置为加快速度game_speed+=2elif event.key == pygame.K_s:game_speed-=2# 更新贪吃蛇位置snake_x += snake_speed_xsnake_y += snake_speed_y# 检测是否吃到食物if snake_x == food_x and snake_y == food_y:food_x ,food_y = generate_food()snake_length += 1# 更新贪吃蛇身体snake_body.append((snake_x, snake_y))if len(snake_body) > snake_length:del snake_body[0]# 检测贪吃蛇是否碰到边界或自身if snake_x < 0 or snake_x >= WINDOW_WIDTH or snake_y < 0 or snake_y >= WINDOW_HEIGHT or (snake_x, snake_y) in snake_body[:-1]:print('贪吃蛇碰到了自己')running = False# 清空窗口(这里可以设置背景颜色)window.fill(BLACK)# 绘制贪吃蛇身体和食物for x, y in snake_body:pygame.draw.rect(window, GREEN, (x, y, CELL_SIZE, CELL_SIZE))pygame.draw.rect(window, RED, (food_x, food_y, CELL_SIZE, CELL_SIZE))# 绘制分数文本score_text = font.render("Score: " + str(len(snake_body)), True, WHITE)window.blit(score_text, (10, 10))speed_text = font.render("Speed: " + str(game_speed), True, WHITE)window.blit(speed_text, (200, 10))# 刷新窗口pygame.display.flip()# 控制游戏帧率clock.tick(game_speed)
至此一个普通的贪吃蛇游戏就完成了!
给贪吃蛇加上自动挡
为了让贪吃蛇能够通过代码自行判断行走方向,我们需要额外编写一个函数,根据当前的一些状态变量,输出speed_x和speed_y 也就是他的行进方向。
设计算法
由于贪吃蛇的行进路线实际上就是一个寻路算法,也就是搜索方法。基础的可以使用广度搜索深度搜索。但在这里,博主选择使用了a*搜索算法,设计评估函数。有关a* 算法的知识,可以自行搜索。
算法中我们可以把蛇的身体当作障碍物
可以参考下面这个blog:寻路算法——A*算法详解并附带实现代码
算法的启发函数
def heuristic(x, y):# 使用曼哈顿距离作为启发式函数return abs(x - food_x) + abs(y - food_y)
对于这个启发函数,还可以进行优化让贪吃蛇的表现更好,比如考虑到蛇头附近蛇身体的数量,以免绕入死胡同。
自动挡函数
首先需要引入一个队列包
from queue import PriorityQueue
def computer_move():# 创建一个优先级队列用于A*搜索queue = PriorityQueue()queue.put((0, (snake_x, snake_y, []))) # (priority, (x, y, path))# 创建一个集合用于记录访问过的位置visited = set()visited.add((snake_x, snake_y))# 定义可行的移动方向directions = [(CELL_SIZE, 0), (-CELL_SIZE, 0), (0, CELL_SIZE), (0, -CELL_SIZE)]while not queue.empty():_, (x, y, path) = queue.get()if (x, y) == (food_x, food_y):if path:return path[0] # 返回路径的第一个移动方向for dx, dy in directions:new_x = x + dxnew_y = y + dyif (new_x, new_y) not in visited and is_valid_move(new_x, new_y):queue.put((heuristic(new_x, new_y), (new_x, new_y, path + [(dx, dy)])))visited.add((new_x, new_y))valid_directions = [(dx, dy) for dx, dy in directions if is_valid_move(snake_x + dx, snake_y + dy)]# 如果无法找到最优路径,返回一个随机方向return random.choice(valid_directions) if valid_directions else None
有了computer_move()函数,我们只需要修改主循环中的按键控制部分就好:
snake_speed_x, snake_speed_y = computer_move()
最后贪吃蛇自动挡也就完成了
自动手动可切换的贪吃蛇
添加一个computer_mode变量作为贪吃蛇是否自动的依据,通过空格按键来切换自动手动。最后的完整代码如下:
import pygame
import random
from queue import PriorityQueuecomputer_mode = True# 游戏窗口大小
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480# 贪吃蛇方格大小
CELL_SIZE = 20# 初始化Pygame
pygame.init()# 创建游戏窗口
window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption('贪吃蛇')# 定义字体
font = pygame.font.Font(None, 36)# 定义颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)# 定义贪吃蛇初始位置和速度
snake_x = WINDOW_WIDTH // 2
snake_y = WINDOW_HEIGHT // 2
snake_speed_x = CELL_SIZE
snake_speed_y = 0# 定义食物初始位置
food_x = random.randint(0, WINDOW_WIDTH - CELL_SIZE) // CELL_SIZE * CELL_SIZE
food_y = random.randint(0, WINDOW_HEIGHT - CELL_SIZE) // CELL_SIZE * CELL_SIZE# 定义贪吃蛇身体
snake_body = []
snake_length = 1# 游戏主循环
running = True
game_speed = 10
clock = pygame.time.Clock()def generate_food():while True:food_x = random.randint(0, WINDOW_WIDTH - CELL_SIZE) // CELL_SIZE * CELL_SIZEfood_y = random.randint(0, WINDOW_HEIGHT - CELL_SIZE) // CELL_SIZE * CELL_SIZEif (food_x, food_y) not in snake_body: #确保食物不会生成在蛇的身体breakreturn food_x, food_ydef computer_move():# 创建一个优先级队列用于A*搜索queue = PriorityQueue()queue.put((0, (snake_x, snake_y, []))) # (priority, (x, y, path))# 创建一个集合用于记录访问过的位置visited = set()visited.add((snake_x, snake_y))# 定义可行的移动方向directions = [(CELL_SIZE, 0), (-CELL_SIZE, 0), (0, CELL_SIZE), (0, -CELL_SIZE)]while not queue.empty():_, (x, y, path) = queue.get()if (x, y) == (food_x, food_y):if path:return path[0] # 返回路径的第一个移动方向for dx, dy in directions:new_x = x + dxnew_y = y + dyif (new_x, new_y) not in visited and is_valid_move(new_x, new_y):queue.put((heuristic(new_x, new_y), (new_x, new_y, path + [(dx, dy)])))visited.add((new_x, new_y))valid_directions = [(dx, dy) for dx, dy in directions if is_valid_move(snake_x + dx, snake_y + dy)]# 如果无法找到最优路径,返回一个随机方向return random.choice(valid_directions) if valid_directions else Nonedef heuristic(x, y):# 使用曼哈顿距离作为启发式函数return abs(x - food_x) + abs(y - food_y)def is_valid_move(x, y):if 0 <= x < WINDOW_WIDTH and 0 <= y < WINDOW_HEIGHT and (x, y) not in snake_body:return Truereturn Falsewhile running:for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseelif event.type == pygame.KEYDOWN:if event.key == pygame.K_UP and snake_speed_y != CELL_SIZE:snake_speed_x = 0snake_speed_y = -CELL_SIZEcomputer_mode = Falseelif event.key == pygame.K_DOWN and snake_speed_y != -CELL_SIZE:snake_speed_x = 0snake_speed_y = CELL_SIZEcomputer_mode = Falseelif event.key == pygame.K_LEFT and snake_speed_x != CELL_SIZE:snake_speed_x = -CELL_SIZEsnake_speed_y = 0computer_mode = Falseelif event.key == pygame.K_RIGHT and snake_speed_x != -CELL_SIZE:snake_speed_x = CELL_SIZEsnake_speed_y = 0computer_mode = Falseelif event.key == pygame.K_SPACE:computer_mode = not computer_modeelif event.key == pygame.K_w:game_speed+=2elif event.key == pygame.K_s:game_speed-=2if computer_mode:snake_speed_x, snake_speed_y = computer_move()# 更新贪吃蛇位置snake_x += snake_speed_xsnake_y += snake_speed_y# 检测是否吃到食物if snake_x == food_x and snake_y == food_y:food_x ,food_y = generate_food()snake_length += 1# 更新贪吃蛇身体snake_body.append((snake_x, snake_y))if len(snake_body) > snake_length:del snake_body[0]# 检测贪吃蛇是否碰到边界或自身if snake_x < 0 or snake_x >= WINDOW_WIDTH or snake_y < 0 or snake_y >= WINDOW_HEIGHT or (snake_x, snake_y) in snake_body[:-1]:print('贪吃蛇碰到了自己')running = False# 清空窗口window.fill(BLACK)# 绘制贪吃蛇身体和食物for x, y in snake_body:pygame.draw.rect(window, GREEN, (x, y, CELL_SIZE, CELL_SIZE))pygame.draw.rect(window, RED, (food_x, food_y, CELL_SIZE, CELL_SIZE))# 绘制分数文本score_text = font.render("Score: " + str(len(snake_body)), True, WHITE)window.blit(score_text, (10, 10))speed_text = font.render("Speed: " + str(game_speed), True, WHITE)window.blit(speed_text, (200, 10))# 刷新窗口pygame.display.flip()# 控制游戏帧率clock.tick(game_speed)# 退出游戏
pygame.quit()
欢迎大佬们改进哦!