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

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

带着好奇,开始!

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

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

欢迎大佬们改进哦!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQLiteC/C++接口详细介绍之sqlite3类(十六)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;十五&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍之sqlite3类&#xff08;十七&#xff09;&#xff08;未发表&#xff09; 50.sqlite…

STL:List从0到1

&#x1f389;个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名乐于分享在学习道路上收获的大二在校生 &#x1f648;个人主页&#x1f389;&#xff1a;GOTXX &#x1f43c;个人WeChat&#xff1a;ILXOXVJE &#x1f43c;本文由GOTXX原创&#xff0c;首发CSDN&…

uniapp开发微信小程序调用打电话

在使用uniapp开发微信小程序的时候&#xff0c;经常需要调用打电话功能。 下面我们来讲解一下如何实现该功能&#xff0c;效果图请看下图&#xff1a; 代码部分&#xff1a; <!-- h5部分 --><button click"playphone()"></button><!-- JS部分 …

代码随想录day21(1)二叉树:平衡二叉树(leetcode110)

题目要求&#xff1a;判断一棵树是否为平衡二叉树 思路&#xff1a;递归地比较左右子树&#xff0c;只要有一棵子树不满足条件就说明这棵树不是平衡二叉树。本题采用迭代法较为复杂。 leetcode实战&#xff1a; 代码实现&#xff1a; 递归&#xff1a; 迭代&#xff1a;

【WebAssembly】WebAssembly概念介绍和在js中使用

简言 记录下WebAssembly的概念和在JavaScript中的使用方法。 WebAssembly官网 WebAssembly WebAssembly &#xff08;缩写为 Wasm&#xff09;是一种二进制指令格式&#xff0c;用于基于堆栈的虚拟机。Wasm 被设计为编程语言的可移植编译目标&#xff0c;可在网络上部署客户…

爬虫逆向sm3和sm4 加密 案例

注意&#xff01;&#xff01;&#xff01;&#xff01;某XX网站逆向实例仅作为学习案例&#xff0c;禁止其他个人以及团体做谋利用途&#xff01;&#xff01;&#xff01; 案例--aHR0cDovLzExMS41Ni4xNDIuMTM6MTgwODgvc3Vic2lkeU9wZW4 第一步&#xff1a;分析页面和请求方式 …

代码随想录算法训练营第39天 | 62.不同路径 , 63. 不同路径 II

动态规划章节理论基础&#xff1a; https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 62.不同路径 题目链接&#xff1a;https://leetcode.cn/problems/unique-paths/ 思路&#xff1a; 动规五部曲&#xff1a…

实战Python Socket编程:开发多用户聊天应用

实战Python Socket编程&#xff1a;开发多用户聊天应用 Python Socket 编程概述什么是Socket编程&#xff1f;Socket编程的应用场景Socket编程的重要性基本概念 环境准备Python版本必要的库开发环境配置调试工具 基本Socket编程创建Socket绑定Socket到端口监听连接接受连接发送…

基于C++的反射功能

需求&#xff1a; 利用C的发射机制&#xff0c;实现根据字符串创建实例对象。 代码&#xff1a; #ifndef OBJECT_H #define OBJECT_H#include <string> #include <map>typedef void* (*Constructor)();class CObjectFactory { public:static void registerClass…

Spring Boot轻松整合Minio实现文件上传下载功能【建议收藏】

一、Linux 安装Minio 安装 在/root/xxkfz/soft目录下面创建文件minio文件夹&#xff0c;进入minio文件夹&#xff0c;并创建data目录&#xff1b; [rootxxkfz soft]# mkdir minio [rootxxkfz soft]# cd minio [rootxxkfz minio]# mkdir data执行如下命令进行下载 [rootxxkf…

python基础——字符串的常见操作方法【下标索引,index,count,len,replace,split,strip】

&#x1f4dd;前言&#xff1a; 字符串是一种有序的&#xff0c;允许重复字符串存在的&#xff0c;不可修改的序列 这篇文章主要总结一下python中有关字符串的部分相关知识&#xff0c;以及字符串的常见操作方法&#xff1a; 1&#xff0c;和其他序列极其类似的操作方法 2&…

Three 材质纹理 (总结三)

THREE.MeshLambertMaterial&#xff08;网格 Lambert 材质&#xff09; 该材质使用基于非物理的Lambertian模型来计算反射率。可以用来创建暗淡的并不光亮的表面&#xff0c;该材质非常易用&#xff0c;而且会与场景中的光源产生反应。 MeshLambertMaterial属性 # .color : …

mysql中用逗号隔开的某字段,如何判断其他表的字段值是否在这个字段中

因为要增加需求&#xff0c;需要将线上表中老数据&#xff0c;修改为新数据的规则。 线上两张表&#xff0c;sequence_number中is_use有3作废、2到期状态&#xff0c;需要根据这个状态和school_ai_authorization中的is_deleted修改新增的state字段。 sequence_number表结构&…

数据分析实战-Python实现博客评论数据的情感分析

数据分析实战-Python实现博客评论数据的情感分析 学习建议SnowNLP基础什么是SnowNLP&#xff1f;SnowNLP情感分析 SnowNLP使用SnowNLP安装情感分析中文分词关键词提取拼音、词性标准 SnowNLP实战-博客评论数据的情感分析数据准备数据获取数据分析 总结 学习建议 现在很多网站、…

关于振弦采集仪的应用编写

instruction&#xff1a; 1、本应用基于深圳市安传物联科技有限公司所生产的八通道振弦变送器产品。该产品为MAX485 信号的变送设备&#xff0c; 并以Modbus协议输出。 2、本应用采用python语言编写。 功能实现&#xff1a; 1、发送&#xff1a; 01 03 10 00 00 02 C0 CB并…

JVM之调优(一)

背景&#xff1a;生产环境由于堆内存较大&#xff0c;fullgc 垃圾回收导致程序卡顿问题&#xff08;假死&#xff09; 目录 一、程序卡顿导致的影响 前端页面空白后端数据重复 二、解决方法 降低堆内存大小使用合适的垃圾回收器&#xff08;可以尝试&#xff0c;还未进行测试…

【python】爬取杭州市二手房销售数据做数据分析【附源码】

一、背景 在数据分析和市场调研中&#xff0c;获取房地产数据是至关重要的一环。本文介绍了如何利用 Python 中的 requests、lxml 库以及 pandas 库&#xff0c;结合 XPath 解析网页信息&#xff0c;实现对链家网二手房销售数据的爬取&#xff0c;并将数据导出为 Excel 文件的过…

捋顺【反函数求导】

设 d y d x f ( x ) 则 d x d y 1 f ( x ) 以 y t a n x 为 例 &#xff0c; d y / d x s e c 2 x , d x / d y 1 s e c 2 x c o s 2 x 到 此 为 止 &#xff0c; 似 乎 难 以 推 导 &#xff0c; 但 是 假 如 用 t a n x ( 也 就 是 y ) 将 c o s 2 x 表 示 出 来 &…

jenkins容器中安装python遇到问题

在Jenkins容器中安装配置Python时遇到问题 执行./configure --prefix/opt/python3/时遇到configure: error: no acceptable C compiler found in $PATH 这个问题就是缺少gcc编译环境。将gcc安装上即可&#xff1a; yum install -y gcc##前提是容器里的系统是cenos才可以&#…

实在智能Agent——RPA终极进化方向

RPA技术备受瞩目&#xff0c;它通过“机器人”自动化了人力执行的重复性、低复杂度任务&#xff0c;解放了员工并降低了企业成本。RPA机器人全天候运行&#xff0c;避免人为错误&#xff0c;高效处理任务&#xff0c;成为处理事务、操作数据、回应查询的理想选择。在管理后台&a…