一个能够自我游戏的贪吃蛇(pygame与搜索算法)

news2025/1/11 17:00:51

贪吃蛇小游戏再经典不过了,作为编程爱好者,代码编译的贪吃蛇,又能有怎样的成绩呢?

带着好奇,开始!

先做一个普通的贪吃蛇游戏

引入相关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()

欢迎大佬们改进哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1520070.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 根据IP获取IP地址信息(离线)

<!-- https://mvnrepository.com/artifact/org.lionsoul/ip2region --><dependency><groupId>org.lionsoul</groupId><artifactId>ip2region</artifactId><version>2.7.0</version></dependency> 地址&#xff1a;http…

DBA面试题:MySQL缓存池LRU算法做了哪些改进?

下图是MySQL&#xff08;MySQL5.7版本&#xff09;体系架构图 MySQL的InnoDb Buffer Pool 缓冲池是主内存中的一个区域&#xff0c;用来缓存InnoDB在访问表和索引时的数据。对于频繁使用的数据可以直接从内存中访问&#xff0c;从而加快处理速度。如果一台服务器专用作MySQL数据…

逻辑数据平台的 NoETL 之道(内含QA)

作者简介&#xff1a; 余俊&#xff0c;Aloudata 合伙人 & 技术副总裁。拥有 18 年互联网技术和大数据平台相关架构经验。作为主架构师及核心研发主导并完成了 Alibaba B2B 首个海量分布式 KV 存储系统&#xff0c;作为网站架构师负责 Aliexpress 全球买全球卖交易系统的第…

【四 (1)数据可视化之如何选用正确的图表】

目录 文章导航一、数据分析中可视化的作用1、揭示数据关联和模式2、支持数据分析和决策3、提升沟通和共享效果4、强调关键信息和发现5、增强故事叙述和记忆效果6、有效增强数据交互性数据7、复杂信息易理解8、数据多维度显示 二、如何选用合适的图表1、简洁性避免使用过于复杂或…

Adobe PDF背景设置护眼模式,缓解眼部疲劳

一、背景 在用Adobe PDF看论文时&#xff0c;默认的白色背景看久了&#xff0c;眼睛会特别疲劳&#xff0c;下面介绍如何设置背景为护眼模式。 二、设置PDF为护眼模式 使用Adobe Acrobat Pro DC打开任意PDF文件&#xff0c;在上方工具栏选择“编辑”&#xff0c;在下拉菜单栏…

VS2022一个项目中运行多个c++程序

VS2022一个项目中运行多个c程序设置 问题情况解决 问题 一般使用vs2022都需要配置好一些路径依赖&#xff0c;但一个项目中只能使用一个源文件&#xff0c;这也是为了避免找不到那些依赖&#xff0c;可是我们就是想为了可以快速编写&#xff0c;而不是浪费在那些配置环境的时间…

基于java实用的音乐软件微信小程序的设计与实现【附项目源码】分享

基于实用的音乐软件微信小程序的设计与实现: 源码地址&#xff1a;https://download.csdn.net/download/weixin_43894652/88842586 一、引言 随着移动互联网的普及和微信小程序的兴起&#xff0c;音乐类小程序成为了用户随时随地享受音乐的重要工具。本需求文档旨在详细阐述一…

mac安装rust开发环境,使用brew安装和全局配置

mac下使用brew可以一键安装环境&#xff1a; brew install rustup 安装完成执行&#xff1a; rustup-init 按照提示配置即可&#xff1a; 出现&#xff1a; 想要全局生效&#xff1a; echo export PATH"$HOME/.cargo/bin:$PATH" >> ~/.bash_profile source…

企业级授权源码 – 高价值企业授权系统,内含授权系统、工单系统和盗版检测功能

企业授权系统功能简介&#xff1a; 1、网站管理&#xff1a;包括基本管理、系统设置、公告设置、接口设置、价格设置和下载设置等。 2、内容管理&#xff1a;包括文章管理和广告轮图管理&#xff0c;以及添加授权、授权列表和授权日志等。 3、订单管理&#xff1a;包括支付订…

iOS 腾讯Pag动画框架-实现PagView的截图功能

背景 产品想要一个首页的截图功能&#xff0c;一听这个功能&#xff0c;心想那还不简单&#xff0c;将父视图控件转换成图片保存就行了。按照这个思路实现&#xff0c;很快就打脸啦&#xff0c;首页的这些动画一个都没有截出来&#xff0c;就像消失啦似的。然后蠢蠢的将动画暂…

STM32初识2

复位和时钟控制&#xff08;RCC&#xff1a;reset clock control&#xff09; 系统复位 当发生以下任一事件时&#xff0c;产生一个系统复位&#xff1a; 1. NRST 引脚上的低电平 ( 外部复位 ) 2. 窗口看门狗计数终止 (WWDG 复位 ) 3. 独立看门狗计数终止 (IWDG 复位 ) …

4、鸿蒙学习-@ohos.promptAction (弹窗)

创建并显示文本提示框、对话框和操作菜单。 说明 本模块首批接口从API version 9开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 该模块不支持在UIAbility的文件声明处使用&#xff0c;即不能在UIAbility的生命周期中调用&#xff0c;需要在创建…

云计算 3月14号 (TCP三次握手和四次挥手)

1.TCP三次握手和四次挥手 1.TCP的传输过程&#xff1a; Seq 序列号 保障传输过程可靠。 ACK &#xff08;确认消息&#xff09; SYN &#xff08;在建立TCP连接的时候使用&#xff09; FIN &#xff08;在关闭TCP连接的时候使用&#xff09; 3.TCP建立连接的过程&…

3.Gen<I>Cam文件配置

Gen<I>Cam踩坑指南 我使用的是大恒usb相机&#xff0c;第一步到其官网下载大恒软件安装包,安装完成后图标如图所示&#xff0c;之后连接相机&#xff0c;打开软件&#xff0c;相机显示一切正常。之后查看软件的安装目录如图&#xff0c;发现有GenICam和GenTL两个文件&am…

arcgis pro植被冠层分析及单木识别

测试正射影像和点云数据介绍(文末分享):点云数据每平方米包含 0.5-1 个点。准备的课程如下;地面、水、桥或未分类。两个数据的最小单位覆盖面积为 2.5 x 2.5 公里。两个数据均位于 SWEREF 99 TM 坐标系中。正射影像数据由 RGBI(红、绿、蓝和近红外)波段组成。两个数据的空…

Spark杂谈

文章目录 什么是Spark对比HadoopSpark应用场景Spark数据处理流程什么是RDDSpark架构相关进程入门案例&#xff1a;统计单词数量Spark开启historyServer 什么是Spark Spark是一个用于大规模数据处理的统一计算引擎Spark一个重要的特性就是基于内存计算&#xff0c;从而它的速度…

计算机网络——物理层(奈氏准则和香农定理)

计算机网络——物理层&#xff08;奈氏准则和香农定理&#xff09; 失真码间串扰奈氏准则&#xff08;奈奎斯特定理&#xff09;极限数据率 噪声信噪比香农定理奈氏准则和香农定理的区别 前面我们已经了解一些数据通信的基本知识&#xff0c;没有看过上一篇得小伙伴可以点击这里…

老电脑装什么系统流畅

对于一些老旧电脑来说&#xff0c;重装系统是提升电脑性能的最佳选择。那么&#xff0c;老电脑装什么系统流畅呢&#xff1f;推荐Windows 7系统&#xff0c;它对硬件的需求相对较低。配置较低的电脑运行Windows 7可以更好地利用系统资源&#xff0c;提高电脑的运行速度和响应能…

ChatGPT解决hmm...something seems to have gone wrong.

ChatGPT解决hmm…something seems to have gone wrong. 这里是官方社区的一种workaround办法。仅仅只是mark一下。 我这边遇到的现象是&#xff0c;ChatGPT 3.5是正常的&#xff0c;但是使用ChatGPT 4就会频繁的出现这样的输出。而且恶心的是&#xff0c;即使是这种输出&…

RabbitMQ:1.概述及安装

概述 AMQP协议 MQ Message Queue&#xff08;消息队列&#xff09;是在消息的传输过程中保存消息的容器&#xff0c;多用于系统之间的异步通信 AMQP Advanced Message Queuing Protocol(高级消息队列协议)是一个网络协议&#xff0c;2006年AMQP规范发布【类比HTTP】 专门为消…