贪吃蛇小游戏再经典不过了,作为编程爱好者,代码编译的贪吃蛇,又能有怎样的成绩呢?
带着好奇,开始!
先做一个普通的贪吃蛇游戏
引入相关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_SIZE
food_y = random.randint(0, WINDOW_HEIGHT - CELL_SIZE) // CELL_SIZE * CELL_SIZE
if (food_x, food_y) not in snake_body: #确保食物不会生成在蛇的身体
break
return food_x, food_y
游戏主函数
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN: #设置各方向移动
if event.key == pygame.K_UP and snake_speed_y != CELL_SIZE:
snake_speed_x = 0
snake_speed_y = -CELL_SIZE
elif event.key == pygame.K_DOWN and snake_speed_y != -CELL_SIZE:
snake_speed_x = 0
snake_speed_y = CELL_SIZE
elif event.key == pygame.K_LEFT and snake_speed_x != CELL_SIZE:
snake_speed_x = -CELL_SIZE
snake_speed_y = 0
elif event.key == pygame.K_RIGHT and snake_speed_x != -CELL_SIZE:
snake_speed_x = CELL_SIZE
snake_speed_y = 0
elif event.key == pygame.K_w: #设置为加快速度
game_speed+=2
elif event.key == pygame.K_s:
game_speed-=2
# 更新贪吃蛇位置
snake_x += snake_speed_x
snake_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 + dx
new_y = y + dy
if (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 PriorityQueue
computer_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_SIZE
food_y = random.randint(0, WINDOW_HEIGHT - CELL_SIZE) // CELL_SIZE * CELL_SIZE
if (food_x, food_y) not in snake_body: #确保食物不会生成在蛇的身体
break
return food_x, food_y
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 + dx
new_y = y + dy
if (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
def 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 True
return False
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP and snake_speed_y != CELL_SIZE:
snake_speed_x = 0
snake_speed_y = -CELL_SIZE
computer_mode = False
elif event.key == pygame.K_DOWN and snake_speed_y != -CELL_SIZE:
snake_speed_x = 0
snake_speed_y = CELL_SIZE
computer_mode = False
elif event.key == pygame.K_LEFT and snake_speed_x != CELL_SIZE:
snake_speed_x = -CELL_SIZE
snake_speed_y = 0
computer_mode = False
elif event.key == pygame.K_RIGHT and snake_speed_x != -CELL_SIZE:
snake_speed_x = CELL_SIZE
snake_speed_y = 0
computer_mode = False
elif event.key == pygame.K_SPACE:
computer_mode = not computer_mode
elif event.key == pygame.K_w:
game_speed+=2
elif event.key == pygame.K_s:
game_speed-=2
if computer_mode:
snake_speed_x, snake_speed_y = computer_move()
# 更新贪吃蛇位置
snake_x += snake_speed_x
snake_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()
欢迎大佬们改进哦!