当前位置: 首页 > news >正文

pygame AI snake 大乱斗

今年正值AI技术蓬勃发展的蛇年,趁着蛇年尾声,整一款AI贪吃蛇游戏来应景吧!

先看看游戏界面截图,感受一下氛围:

🎮 游戏概述

小游戏融合了经典贪吃蛇玩法与AI技术的策略游戏。游戏提供两种主要模式:

  1. 单人模式​:玩家控制一条蛇与AI蛇对战

  2. AI对战观战模式​:观看多个AI蛇互相竞争

游戏采用数码风格设计,具有以下核心特点:

  • 网格化游戏界面

  • 蛇身渐变色效果

  • 实时显示AI路径规划

  • 数码风格字体和视觉效果

  • 多玩家分数系统

🧠 AI路径规划系统详解

1. ​A寻路算法*​

AI蛇使用A*算法作为核心路径规划算法,这是游戏中最智能的路径规划基础。

算法特点​:

  • 启发式搜索​:使用曼哈顿距离作为启发函数

  • 路径优化​:寻找最短路径到食物

  • 动态调整​:根据游戏状态实时更新路径

算法流程​:

  1. 初始化​:

    • 将蛇头位置加入开放列表

    • 设置起点g值(实际代价)为0

    • 计算起点f值(g值 + 启发式值)

  2. 主循环​:

    • 从开放列表取出f值最小的节点

    • 如果该节点是食物位置,重建路径

    • 检查四个方向(上、下、左、右)的相邻节点

    • 对于每个相邻节点:

      • 跳过边界外的节点

      • 跳过不安全的位置

      • 计算新的g值

      • 如果新g值更小,更新节点信息

      • 计算f值(g值 + 启发式值)

      • 将节点加入开放列表

  3. 路径重建​:

    • 从食物位置回溯到蛇头位置

    • 反转路径得到从蛇头到食物的路径

启发式函数​:

def heuristic(self, a, b):"""启发式函数 - 曼哈顿距离"""return abs(a[0] - b[0]) + abs(a[1] - b[1])

2. ​动态路径调整

AI蛇不会只计算一次路径,而是根据游戏状态动态调整路径:

  1. 定期更新​:

    • 每0.5秒重新计算路径

    • 适应游戏状态变化

  2. 安全性检查​:

    • 移动前检查路径是否安全

    • 路径上的位置是否会被其他蛇占据

    • 预测其他蛇的移动位置

  3. 路径中断处理​:

    • 当路径变得不安全时,立即重新规划路径

    • 确保AI蛇不会沿着危险路径前进

3. ​安全移动策略

当A*算法无法找到路径或路径不安全时,AI蛇使用安全移动策略:

  1. 安全方向评估​:

    • 检查四个方向的安全性

    • 判断移动后是否会碰撞

    • 预测其他蛇的移动位置

  2. 风险最小化​:

    • 如果没有安全方向,计算每个方向的风险值

    • 风险因素:

      • 距离边界越近风险越高

      • 距离其他蛇越近风险越高

      • 位置在蛇的路径上风险增加

    • 选择风险最小的方向移动

风险计算函数​:

def calculate_risk(self, position, other_snakes):"""计算移动到指定位置的风险值"""risk = 0# 边界风险 - 增加权重risk += min(position[0], GRID_WIDTH - 1 - position[0]) * self.boundary_risk_factorrisk += min(position[1], GRID_HEIGHT - 1 - position[1]) * self.boundary_risk_factor# 特别关注靠近边界的位置if position[0] < self.boundary_safe_distance or position[0] > GRID_WIDTH - 1 - self.boundary_safe_distance:risk += 10.0if position[1] < self.boundary_safe_distance or position[1] > GRID_HEIGHT - 1 - self.boundary_safe_distance:risk += 10.0# 其他蛇的距离风险for snake in other_snakes:if snake.player_id != self.player_id and snake.alive and snake.body:head = snake.body[0]distance = math.sqrt((position[0] - head[0])**2 + (position[1] - head[1])**2)risk += max(0, 5 - distance) * 2  # 距离越近风险越高# 如果位置在蛇的路径上,增加风险if snake.path and position in snake.path:risk += 3return risk

4. ​碰撞预测

AI蛇能够预测其他蛇的移动,避免碰撞:

  1. 蛇头位置预测​:

    def predict_head_position(self, snake):"""预测蛇的下一步位置"""head = snake.body[0].copy()if snake.direction == "UP":head[1] -= 1elif snake.direction == "DOWN":head[1] += 1elif snake.direction == "LEFT":head[0] -= 1elif snake.direction == "RIGHT":head[0] += 1return head
  2. 路径冲突检测​:

    • 检查路径上的位置是否会被其他蛇占据

    • 预测其他蛇头可能移动到的位置

    • 避免移动到危险位置

5. ​边界处理优化

针对边界问题进行了专门优化:

  1. 边界风险评估​:

    • 提高边界风险权重

    • 增加边界位置代价

    • 使AI更倾向于远离边界

  2. 边界安全距离​:

    • 定义边界安全距离

    • 位置在安全距离内时增加额外风险

  3. 接近边界时重新规划​:

    • 当蛇接近边界时强制重新规划路径

    • 确保及时调整方向避免撞墙

🎮 游戏功能详解

1. ​游戏模式

单人模式
  • 玩家控制一条蛇(WASD键)

  • 与2个AI蛇对战

  • 竞争吃食物获得高分

AI对战观战模式
  • 观看4个AI蛇互相竞争

  • 观察AI的生存策略

  • 无需玩家操作

2. ​核心游戏机制

蛇移动系统
  • 蛇头控制方向

  • 吃到食物后身体增长

  • 撞墙或撞到自身/其他蛇则死亡

食物系统
  • 随机生成食物

  • 吃到食物得分增加

  • 食物不会出现在蛇身上

分数系统
  • 吃食物得分

  • 存活时间越长分数越高

  • 实时显示玩家分数和状态

3. ​游戏界面

主菜单
  • 数码风格标题

  • 三角形选择指示器

  • 操作提示

游戏界面
  • 网格背景

  • 蛇身渐变色着色

  • 蛇头显示玩家ID

  • AI路径可视化

暂停界面
  • 居中显示"PAUSED"

  • 继续游戏提示

  • 返回主菜单选项

游戏结束界面
  • 显示获胜玩家

  • 重新开始选项

  • 返回主菜单选项

4. ​控制方式

菜单导航
  • 上/下方向键:选择菜单选项

  • 回车键/空格键:确认选择

游戏控制
  • 空格键:暂停/继续游戏

  • ESC键:返回主菜单

  • R键:重新开始游戏

单人模式
  • WASD键:控制蛇移动

🚀 技术亮点

1. ​智能避障系统

  • 多层安全检测:边界检查、自身碰撞检查、其他蛇碰撞检查

  • 实时风险评估:动态计算每个移动方向的风险值

  • 预测性移动:预测其他蛇的移动位置

2. ​高效路径规划

  • A*算法优化:使用最小堆提高搜索效率

  • 路径缓存:重用部分路径减少计算量

  • 增量式更新:只更新变化的部分路径

3. ​动态适应性

  • 游戏状态感知:根据其他蛇的位置调整策略

  • 路径中断处理:当路径被阻挡时快速调整

  • 多策略切换:在最优路径和安全移动之间动态切换

4. ​可视化反馈

  • 路径颜色编码:白色安全,红色危险,橙色边界危险

  • 蛇身渐变色:视觉上区分蛇头和蛇身

  • 分数实时显示:了解游戏进展

📊 性能优化

  1. 路径更新频率控制​:

    • 限制路径更新频率(0.5秒/次)

    • 避免过度计算影响游戏性能

  2. 局部路径检查​:

    • 只检查路径上的前几个位置

    • 避免全路径检查的计算开销

  3. 高效碰撞检测​:

    • 使用空间分区优化碰撞检测

    • 只检查相关区域的对象

  4. 算法优化​:

    • 使用曼哈顿距离替代欧几里得距离

    • 减少不必要的计算

🎯 总结

AI路径规划系统是一个复杂而高效的智能系统,结合了:

  • A*寻路算法的基础

  • 动态路径调整的灵活性

  • 安全移动策略的鲁棒性

  • 碰撞预测的预见性

这些技术共同作用,使AI蛇能够智能地寻找食物、避开障碍物,并在复杂环境中生存。游戏不仅提供了娱乐性,还展示了路径规划算法的实际应用。

🚀 开始挑战AI蛇啦

import pygame
import random
import sys
import math
import heapq
from collections import deque# 初始化pygame
pygame.init()# 游戏设置
WIDTH, HEIGHT = 800, 600
GRID_SIZE = 20
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Digital Snake Battle - AI Enhanced")# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
PURPLE = (128, 0, 128)
CYAN = (0, 255, 255)
ORANGE = (255, 165, 0)
DIGITAL_GREEN = (57, 255, 20)  # 数码风格绿色
GRID_COLOR = (40, 40, 40)      # 网格颜色
PATH_COLOR = (255, 255, 255)   # AI路径颜色
DANGER_COLOR = (255, 100, 100) # 危险路径颜色
BOUNDARY_DANGER_COLOR = (255, 150, 0) # 边界危险颜色# 玩家颜色列表
PLAYER_COLORS = [GREEN, BLUE, YELLOW, PURPLE, CYAN, ORANGE]# 字体设置
try:digital_font = pygame.font.Font("digital.ttf", 70)title_font = pygame.font.Font("digital.ttf", 80)menu_font = pygame.font.Font("digital.ttf", 40)
except:digital_font = pygame.font.SysFont('couriernew', 70, bold=True)title_font = pygame.font.SysFont('couriernew', 80, bold=True)menu_font = pygame.font.SysFont('couriernew', 40, bold=True)font = pygame.font.SysFont('couriernew', 25, bold=True)
big_font = pygame.font.SysFont('couriernew', 50, bold=True)class Snake:def __init__(self, x, y, color, player_id, is_ai=False, control_keys=None):self.body = [[x, y]]self.direction = "RIGHT"self.next_direction = "RIGHT"self.color = colorself.score = 0self.grow = Falseself.player_id = player_idself.control_keys = control_keysself.alive = Trueself.is_ai = is_aiself.path = []  # AI路径self.last_move_time = 0self.last_path_update = -10  # 初始值设为负数,确保第一次更新self.path_update_interval = 0.5  # 路径更新间隔(秒)self.boundary_risk_factor = 1.0  # 边界风险因子(提高)self.boundary_safe_distance = 3  # 边界安全距离(增加)self.initial_path_planned = False  # 初始路径是否已规划self.boundary_danger_threshold = 3  # 边界危险阈值def change_direction(self, new_direction):"""改变蛇的方向"""if (new_direction == "UP" and self.direction != "DOWN") or \(new_direction == "DOWN" and self.direction != "UP") or \(new_direction == "LEFT" and self.direction != "RIGHT") or \(new_direction == "RIGHT" and self.direction != "LEFT"):self.next_direction = new_directiondef move(self):"""移动蛇并返回是否死亡"""if not self.alive:return Trueself.direction = self.next_directionhead = self.body[0].copy()if self.direction == "UP":head[1] -= 1elif self.direction == "DOWN":head[1] += 1elif self.direction == "LEFT":head[0] -= 1elif self.direction == "RIGHT":head[0] += 1# 边界检查if (head[0] < 0 or head[0] >= GRID_WIDTH or head[1] < 0 or head[1] >= GRID_HEIGHT):self.alive = Falsereturn True# 自身碰撞检查if head in self.body[1:]:self.alive = Falsereturn Trueself.body.insert(0, head)if not self.grow:self.body.pop()else:self.grow = Falseself.score += 1return Falsedef check_collision_with_other(self, other_snakes):"""检查与其他蛇的碰撞"""if not self.alive:return Falsehead = self.body[0]for snake in other_snakes:if snake.player_id != self.player_id and snake.alive:if head in snake.body:self.alive = Falsereturn Truereturn Falsedef ai_move(self, foods, other_snakes, current_time):"""AI决策算法 - 增强版"""# 确保初始路径已规划if not self.initial_path_planned:self.find_path_to_food(foods, other_snakes)self.initial_path_planned = Trueself.last_path_update = current_time# 检查是否接近边界head = self.body[0]near_boundary = (head[0] < self.boundary_danger_threshold or head[0] > GRID_WIDTH - 1 - self.boundary_danger_threshold orhead[1] < self.boundary_danger_threshold or head[1] > GRID_HEIGHT - 1 - self.boundary_danger_threshold)# 定期更新路径或当路径不安全或接近边界时更新if (not self.path or current_time - self.last_path_update > self.path_update_interval ornot self.is_path_safe(other_snakes) ornear_boundary):self.find_path_to_food(foods, other_snakes)self.last_path_update = current_timeif self.path:next_pos = self.path[0]# 根据下一个位置决定方向head = self.body[0]if next_pos[0] > head[0]:self.change_direction("RIGHT")elif next_pos[0] < head[0]:self.change_direction("LEFT")elif next_pos[1] > head[1]:self.change_direction("DOWN")elif next_pos[1] < head[1]:self.change_direction("UP")# 移动到下一个位置后从路径中移除self.path.pop(0)else:# 如果没有路径,安全移动(避免被困)self.safe_move(other_snakes)def is_path_safe(self, other_snakes):"""检查当前路径是否安全"""if not self.path:return False# 检查路径上的每个位置是否安全for i, pos in enumerate(self.path):# 只检查前几个位置(避免过度检查)if i > 3:break# 检查是否会被其他蛇占据for snake in other_snakes:if snake.player_id != self.player_id and snake.alive:# 预测其他蛇可能移动到的位置if pos in snake.body:return False# 检查蛇头可能移动到的位置if snake.body and pos == snake.body[0]:return Falsereturn Truedef safe_move(self, other_snakes):"""安全移动算法 - 避免碰撞"""# 获取所有安全方向safe_directions = []directions = ["UP", "DOWN", "LEFT", "RIGHT"]for dir in directions:test_head = self.body[0].copy()if dir == "UP":test_head[1] -= 1elif dir == "DOWN":test_head[1] += 1elif dir == "LEFT":test_head[0] -= 1elif dir == "RIGHT":test_head[0] += 1if (0 <= test_head[0] < GRID_WIDTH and 0 <= test_head[1] < GRID_HEIGHT and test_head not in self.body andnot self.will_collide(test_head, other_snakes)):safe_directions.append(dir)# 如果有安全方向,选择远离边界的方向if safe_directions:# 优先选择远离边界的方向best_dir = Nonemax_distance = -1for dir in safe_directions:test_head = self.body[0].copy()if dir == "UP":test_head[1] -= 1elif dir == "DOWN":test_head[1] += 1elif dir == "LEFT":test_head[0] -= 1elif dir == "RIGHT":test_head[0] += 1# 计算到边界的距离distance_to_boundary = min(test_head[0], GRID_WIDTH - 1 - test_head[0],test_head[1],GRID_HEIGHT - 1 - test_head[1])if distance_to_boundary > max_distance:max_distance = distance_to_boundarybest_dir = dirif best_dir:self.change_direction(best_dir)else:# 如果没有安全方向,选择风险最小的方向min_risk = float('inf')best_dir = Nonefor dir in directions:test_head = self.body[0].copy()if dir == "UP":test_head[1] -= 1elif dir == "DOWN":test_head[1] += 1elif dir == "LEFT":test_head[0] -= 1elif dir == "RIGHT":test_head[0] += 1if not (0 <= test_head[0] < GRID_WIDTH and 0 <= test_head[1] < GRID_HEIGHT):continuerisk = self.calculate_risk(test_head, other_snakes)if risk < min_risk:min_risk = riskbest_dir = dirif best_dir:self.change_direction(best_dir)def will_collide(self, position, other_snakes):"""检查移动到指定位置是否会碰撞"""# 检查自身碰撞if position in self.body[1:]:return True# 检查其他蛇for snake in other_snakes:if snake.player_id != self.player_id and snake.alive:if position in snake.body:return True# 预测蛇头移动if snake.body and self.predict_head_position(snake) == position:return Truereturn Falsedef predict_head_position(self, snake):"""预测蛇的下一步位置"""head = snake.body[0].copy()if snake.direction == "UP":head[1] -= 1elif snake.direction == "DOWN":head[1] += 1elif snake.direction == "LEFT":head[0] -= 1elif snake.direction == "RIGHT":head[0] += 1return headdef calculate_risk(self, position, other_snakes):"""计算移动到指定位置的风险值"""risk = 0# 边界风险 - 增加权重risk += min(position[0], GRID_WIDTH - 1 - position[0]) * self.boundary_risk_factorrisk += min(position[1], GRID_HEIGHT - 1 - position[1]) * self.boundary_risk_factor# 特别关注靠近边界的位置if position[0] < self.boundary_safe_distance or position[0] > GRID_WIDTH - 1 - self.boundary_safe_distance:risk += 10.0  # 增加边界风险权重if position[1] < self.boundary_safe_distance or position[1] > GRID_HEIGHT - 1 - self.boundary_safe_distance:risk += 10.0# 其他蛇的距离风险for snake in other_snakes:if snake.player_id != self.player_id and snake.alive and snake.body:head = snake.body[0]distance = math.sqrt((position[0] - head[0])**2 + (position[1] - head[1])**2)risk += max(0, 5 - distance) * 2  # 距离越近风险越高# 如果位置在蛇的路径上,增加风险if snake.path and position in snake.path:risk += 3return riskdef find_path_to_food(self, foods, other_snakes):"""使用A*算法寻找路径 - 增强版"""start = tuple(self.body[0])# 找到最近的食物closest_food = Nonemin_distance = float('inf')for food in foods:distance = abs(start[0] - food.position[0]) + abs(start[1] - food.position[1])if distance < min_distance:min_distance = distanceclosest_food = foodif not closest_food:returngoal = tuple(closest_food.position)# 如果食物在蛇身体里,重新生成路径if goal in [tuple(segment) for segment in self.body]:self.path = []return# A*算法实现open_set = []heapq.heappush(open_set, (0, start))came_from = {start: None}g_score = {start: 0}f_score = {start: self.heuristic(start, goal)}while open_set:current = heapq.heappop(open_set)[1]if current == goal:# 重建路径self.path = []while current in came_from and came_from[current] is not None:self.path.append(list(current))current = came_from[current]self.path.reverse()return# 检查四个方向for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:neighbor = (current[0] + dx, current[1] + dy)# 检查边界if not (0 <= neighbor[0] < GRID_WIDTH and 0 <= neighbor[1] < GRID_HEIGHT):continue# 检查是否安全if not self.is_position_safe(neighbor, other_snakes):continue# 对于起始点,检查方向是否合法(不能立即掉头)if current == start:# 计算方向向量direction_vector = (dx, dy)# 检查是否与当前方向相反if self.is_opposite_direction(direction_vector):continue# 计算新的g分数 - 增加边界风险tentative_g_score = g_score[current] + 1# 增加边界位置的代价if (neighbor[0] < self.boundary_safe_distance or neighbor[0] > GRID_WIDTH - 1 - self.boundary_safe_distance orneighbor[1] < self.boundary_safe_distance or neighbor[1] > GRID_HEIGHT - 1 - self.boundary_safe_distance):tentative_g_score += 10  # 增加边界位置的代价if neighbor not in g_score or tentative_g_score < g_score[neighbor]:came_from[neighbor] = currentg_score[neighbor] = tentative_g_scoref_score[neighbor] = tentative_g_score + self.heuristic(neighbor, goal)heapq.heappush(open_set, (f_score[neighbor], neighbor))# 如果没有找到路径self.path = []def is_position_safe(self, position, other_snakes):"""检查位置是否安全"""# 检查自身碰撞 - 排除蛇尾(因为蛇尾会移动)if position in [tuple(segment) for segment in self.body[1:-1]]:return False# 检查其他蛇for snake in other_snakes:if snake.player_id != self.player_id and snake.alive:# 当前位置是否在蛇身上if position in [tuple(segment) for segment in snake.body]:return False# 预测蛇头移动if snake.body and self.predict_head_position(snake) == position:return Falsereturn Truedef is_opposite_direction(self, direction_vector):"""检查方向是否与当前方向相反"""dx, dy = direction_vectorif self.direction == "UP" and dy == 1:  # 向下return Trueelif self.direction == "DOWN" and dy == -1:  # 向上return Trueelif self.direction == "LEFT" and dx == 1:  # 向右return Trueelif self.direction == "RIGHT" and dx == -1:  # 向左return Truereturn Falsedef heuristic(self, a, b):"""启发式函数 - 曼哈顿距离"""return abs(a[0] - b[0]) + abs(a[1] - b[1])def draw(self, surface, other_snakes=None):"""绘制蛇和AI路径"""if not self.alive:returnfor i, segment in enumerate(self.body):# 蛇头用深色,蛇身用渐变色if i == 0:color = self.colorelse:r = min(255, self.color[0] + i * 3)g = min(255, self.color[1] + i * 3)b = min(255, self.color[2] + i * 3)color = (r, g, b)pygame.draw.rect(surface, color, [segment[0] * GRID_SIZE, segment[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE])# 绘制玩家ID在蛇头上(居中显示)if i == 0:id_text = font.render(str(self.player_id), True, BLACK)# 计算文本居中位置text_x = segment[0] * GRID_SIZE + (GRID_SIZE - id_text.get_width()) // 2text_y = segment[1] * GRID_SIZE + (GRID_SIZE - id_text.get_height()) // 2surface.blit(id_text, [text_x, text_y])# 绘制AI路径if self.is_ai and self.path:for i, pos in enumerate(self.path):# 根据路径安全性使用不同颜色color = PATH_COLOR# 检查是否靠近边界if (pos[0] < self.boundary_danger_threshold or pos[0] > GRID_WIDTH - 1 - self.boundary_danger_threshold orpos[1] < self.boundary_danger_threshold or pos[1] > GRID_HEIGHT - 1 - self.boundary_danger_threshold):color = BOUNDARY_DANGER_COLOR# 检查是否会被其他蛇占据elif other_snakes and i < 3:  # 只检查前几个位置for snake in other_snakes:if snake.body and pos == tuple(snake.body[0]):color = DANGER_COLORbreakpygame.draw.rect(surface, color, [pos[0] * GRID_SIZE + GRID_SIZE//4, pos[1] * GRID_SIZE + GRID_SIZE//4, GRID_SIZE//2, GRID_SIZE//2], 1)class Food:def __init__(self):self.position = [0, 0]self.color = REDself.randomize_position()def randomize_position(self):"""随机生成食物位置"""self.position = [random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1)]def draw(self, surface):"""绘制食物"""pygame.draw.rect(surface, self.color, [self.position[0] * GRID_SIZE, self.position[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE])class Game:def __init__(self):self.snakes = []self.foods = []self.mode = None  # "single" 或 "ai_battle"self.running = Trueself.clock = pygame.time.Clock()self.game_over = Falseself.winner = Noneself.show_menu = Trueself.menu_selection = 0# 缩短菜单选项字符串self.menu_options = ["Single Player", "AI Battle", "Exit"]self.last_key_press_time = 0self.key_delay = 200self.paused = True  # 游戏默认暂停状态# 初始化食物for _ in range(3):self.foods.append(Food())def init_single_player(self):"""初始化单人模式"""self.snakes.clear()self.foods.clear()# 添加本地玩家player_snake = self.add_player(1, GRID_WIDTH // 4, GRID_HEIGHT // 2, DIGITAL_GREEN, control_keys={"UP": pygame.K_w, "DOWN": pygame.K_s,"LEFT": pygame.K_a, "RIGHT": pygame.K_d})# 添加2个AI玩家for i in range(2, 4):self.add_ai_player(i)for _ in range(3):self.foods.append(Food())# 立即规划AI路径self.plan_ai_paths()def init_ai_battle(self):"""初始化AI对战模式"""self.snakes.clear()self.foods.clear()# 添加4个AI玩家for i in range(1, 5):self.add_ai_player(i)for _ in range(3):self.foods.append(Food())# 立即规划AI路径self.plan_ai_paths()def add_player(self, player_id, x, y, color, is_ai=False, control_keys=None):"""添加一个玩家"""snake = Snake(x, y, color, player_id, is_ai, control_keys)self.snakes.append(snake)return snakedef add_ai_player(self, player_id):"""添加一个AI玩家"""color = PLAYER_COLORS[(player_id - 1) % len(PLAYER_COLORS)]# 找一个不会立即碰撞的位置 - 确保不靠近边界while True:# 确保AI蛇初始位置不靠近边界x = random.randint(3, GRID_WIDTH - 4)y = random.randint(3, GRID_HEIGHT - 4)overlap = Falsefor snake in self.snakes:if [x, y] in snake.body:overlap = Truebreakif not overlap:breaksnake = self.add_player(player_id, x, y, color, is_ai=True)# 随机初始化方向(避免所有AI都向右移动)directions = ["UP", "DOWN", "LEFT", "RIGHT"]snake.direction = random.choice(directions)snake.next_direction = snake.directionreturn snakedef plan_ai_paths(self):"""立即规划所有AI玩家的路径"""for snake in self.snakes:if snake.is_ai and snake.alive:other_snakes = [s for s in self.snakes if s.player_id != snake.player_id]snake.find_path_to_food(self.foods, other_snakes)def handle_input(self):"""处理用户输入"""current_time = pygame.time.get_ticks()if current_time - self.last_key_press_time < self.key_delay:returnkeys = pygame.key.get_pressed()# 菜单界面控制if self.show_menu:if keys[pygame.K_UP]:self.menu_selection = (self.menu_selection - 1) % len(self.menu_options)self.last_key_press_time = current_timeelif keys[pygame.K_DOWN]:self.menu_selection = (self.menu_selection + 1) % len(self.menu_options)self.last_key_press_time = current_timeelif keys[pygame.K_RETURN] or keys[pygame.K_SPACE]:if self.menu_selection == 0:  # 单人模式self.mode = "single"self.show_menu = Falseself.init_single_player()self.paused = True  # 进入游戏后默认暂停elif self.menu_selection == 1:  # AI对战模式self.mode = "ai_battle"self.show_menu = Falseself.init_ai_battle()self.paused = True  # 进入游戏后默认暂停elif self.menu_selection == 2:  # 退出游戏self.running = Falseself.last_key_press_time = current_timeelif keys[pygame.K_ESCAPE]:self.running = Falseself.last_key_press_time = current_timereturn# 游戏界面控制if keys[pygame.K_SPACE]:self.paused = not self.pausedself.last_key_press_time = current_timeif keys[pygame.K_ESCAPE]:self.show_menu = Trueself.paused = Falseself.last_key_press_time = current_timereturn# 单人模式控制if self.mode == "single" and not self.paused:local_snake = Nonefor snake in self.snakes:if snake.control_keys is not None:local_snake = snakebreakif local_snake and local_snake.alive:if keys[pygame.K_w]:local_snake.change_direction("UP")elif keys[pygame.K_s]:local_snake.change_direction("DOWN")elif keys[pygame.K_a]:local_snake.change_direction("LEFT")elif keys[pygame.K_d]:local_snake.change_direction("RIGHT")def update(self):"""更新游戏状态"""if self.show_menu or self.game_over or self.paused:returncurrent_time = pygame.time.get_ticks() / 1000.0  # 转换为秒# AI移动for snake in self.snakes:if snake.is_ai and snake.alive:other_snakes = [s for s in self.snakes if s.player_id != snake.player_id]snake.ai_move(self.foods, other_snakes, current_time)# 移动所有蛇for snake in self.snakes:if snake.alive:snake.move()# 检查蛇之间的碰撞for snake in self.snakes:if snake.alive:other_snakes = [s for s in self.snakes if s.player_id != snake.player_id]snake.check_collision_with_other(other_snakes)# 检查食物碰撞for snake in self.snakes:if snake.alive:for food in self.foods:if snake.body[0] == food.position:snake.grow = Truefood.randomize_position()# 确保食物不会出现在蛇身上while any(food.position in s.body for s in self.snakes if s.alive):food.randomize_position()# 检查游戏是否结束(所有蛇都死亡)alive_players = [s for s in self.snakes if s.alive]if not alive_players:self.game_over = Trueself.winner = Noneelif len(alive_players) == 1:# 只剩一个玩家时不立即结束游戏,继续直到该玩家死亡passdef draw(self, surface):"""绘制游戏界面"""surface.fill(BLACK)# 绘制网格(确保在所有其他元素之前绘制)for x in range(0, WIDTH, GRID_SIZE):pygame.draw.line(surface, GRID_COLOR, (x, 0), (x, HEIGHT))for y in range(0, HEIGHT, GRID_SIZE):pygame.draw.line(surface, GRID_COLOR, (0, y), (WIDTH, y))if self.show_menu:self.draw_menu(surface)return# 绘制食物for food in self.foods:food.draw(surface)# 绘制蛇for snake in self.snakes:other_snakes = [s for s in self.snakes if s.player_id != snake.player_id]snake.draw(surface, other_snakes)# 绘制分数self.draw_scores(surface)# 游戏暂停提示if self.paused:# 绘制暂停文字(居中显示)pause_text = big_font.render("PAUSED", True, DIGITAL_GREEN)pause_width = pause_text.get_width()surface.blit(pause_text, [WIDTH//2 - pause_width//2, HEIGHT//2 - 50])continue_text = font.render("Press SPACE to continue", True, DIGITAL_GREEN)continue_width = continue_text.get_width()surface.blit(continue_text, [WIDTH//2 - continue_width//2, HEIGHT//2 + 20])menu_text = font.render("ESC: Main Menu", True, DIGITAL_GREEN)menu_width = menu_text.get_width()surface.blit(menu_text, [WIDTH//2 - menu_width//2, HEIGHT//2 + 50])# 游戏结束画面elif self.game_over:if self.winner:winner_text = big_font.render(f"Player {self.winner.player_id} Wins!", True, DIGITAL_GREEN)winner_width = winner_text.get_width()surface.blit(winner_text, [WIDTH//2 - winner_width//2, HEIGHT//2 - 50])else:draw_text = big_font.render("Game Over!", True, DIGITAL_GREEN)draw_width = draw_text.get_width()surface.blit(draw_text, [WIDTH//2 - draw_width//2, HEIGHT//2 - 50])restart_text = font.render("R: Restart", True, DIGITAL_GREEN)restart_width = restart_text.get_width()surface.blit(restart_text, [WIDTH//2 - restart_width//2, HEIGHT//2 + 20])menu_text = font.render("ESC: Main Menu", True, DIGITAL_GREEN)menu_width = menu_text.get_width()surface.blit(menu_text, [WIDTH//2 - menu_width//2, HEIGHT//2 + 50])def draw_menu(self, surface):"""绘制开始菜单"""# 绘制背景网格for x in range(0, WIDTH, GRID_SIZE * 2):pygame.draw.line(surface, (20, 40, 20), (x, 0), (x, HEIGHT))for y in range(0, HEIGHT, GRID_SIZE * 2):pygame.draw.line(surface, (20, 40, 20), (0, y), (WIDTH, y))# 绘制标题(居中显示)title_text = title_font.render("DIGITAL SNAKE", True, DIGITAL_GREEN)title_shadow = title_font.render("DIGITAL SNAKE", True, (0, 80, 0))title_width = title_text.get_width()surface.blit(title_shadow, [WIDTH//2 - title_width//2 + 3, 100 + 3])surface.blit(title_text, [WIDTH//2 - title_width//2, 100])# 绘制菜单选项for i, option in enumerate(self.menu_options):color = DIGITAL_GREEN if i == self.menu_selection else (0, 180, 0)text = menu_font.render(option, True, color)text_shadow = menu_font.render(option, True, (0, 40, 0))# 计算文本位置(居中)text_width, text_height = text.get_size()x = WIDTH//2 - text_width//2y = 250 + i * 70surface.blit(text_shadow, [x + 2, y + 2])surface.blit(text, [x, y])# 绘制三角形选择指示器if i == self.menu_selection:# 左侧三角形pygame.draw.polygon(surface, DIGITAL_GREEN, [(x - 30, y + text_height//2 - 10),(x - 10, y + text_height//2),(x - 30, y + text_height//2 + 10)])# 右侧三角形pygame.draw.polygon(surface, DIGITAL_GREEN, [(x + text_width + 30, y + text_height//2 - 10),(x + text_width + 10, y + text_height//2),(x + text_width + 30, y + text_height//2 + 10)])# 绘制操作提示hint_text = font.render("UP/DOWN: Select    ENTER/SPACE: Confirm", True, (0, 180, 0))hint_width = hint_text.get_width()surface.blit(hint_text, [WIDTH//2 - hint_width//2, HEIGHT - 50])# 绘制动画小蛇for i in range(3):x = 150 + i * 30y = 500segment_color = (0, random.randint(100, 200), 0)pygame.draw.rect(surface, segment_color, [x, y, GRID_SIZE, GRID_SIZE])def draw_scores(self, surface):"""绘制分数"""y_offset = 10for snake in sorted(self.snakes, key=lambda x: x.score, reverse=True):color = snake.color if snake.alive else (0, 80, 0)status = "ALIVE" if snake.alive else "DEAD"score_text = font.render(f"P{snake.player_id}: {snake.score} {status}", True, color)surface.blit(score_text, [10, y_offset])y_offset += 30def reset_game(self):"""重置游戏"""self.snakes.clear()self.foods.clear()self.game_over = Falseself.winner = Noneself.paused = True  # 重置后默认暂停for _ in range(3):self.foods.append(Food())if self.mode == "single":self.init_single_player()elif self.mode == "ai_battle":self.init_ai_battle()def main():"""主游戏循环"""game = Game()while game.running:for event in pygame.event.get():if event.type == pygame.QUIT:game.running = Falseelif event.type == pygame.KEYDOWN:if event.key == pygame.K_r and not game.show_menu:game.reset_game()game.handle_input()game.update()game.draw(SCREEN)pygame.display.update()game.clock.tick(10)pygame.quit()sys.exit()if __name__ == "__main__":main()

http://www.dtcms.com/a/392846.html

相关文章:

  • TCP FIN,TCP RST
  • 睡眠PSG统一数据集的设计思路
  • 告别Vibe Coding!敏捷AI驱动开发:用AI高效构建可维护的复杂项目
  • EA-LSS:边缘感知 Lift-splat-shot 框架用于三维鸟瞰视角目标检测
  • 和为 K 的子数组
  • 从流量红利到运营核心:“开源AI智能名片+链动2+1模式+S2B2C商城小程序”驱动电商行业价值重构
  • 【ICLR 2024】MogaNet:多阶门控聚合网络
  • 小语言模型(SLM):构建可扩展智能体AI的关键
  • ​​[硬件电路-293]:不同频率对应不同周期时间对应表
  • 自定义你的tqdm
  • Tiny10 os是啥?原来是精简的Windows10
  • ThingsBoard部署APP过程错误-flutterr Resolving dependencies
  • webpack入门基础
  • 机器视觉VUE3手势识别+手势检测控制相机缩放
  • AI大模型:(三)1.3 Dify文本生成快速搭建旅游助手
  • Linux文件下载卡在0%进度问题处理
  • 【车载开发系列】区分Flash,RAM与E2PROM的概念
  • 未来展望:小模型撬动大未来
  • TenstoRT加速YOLOv11——python端加速
  • 探索LiveTalking:开启实时数字人交互新时代
  • 【开题答辩全过程】以 Javaweb的火花流浪动物救助系统设计与实现为例,包含答辩的问题和答案
  • 链家二手房数据爬虫与预测项目 Python 线性回归 Scrapy+Django+Echarts 机器学习 大数据✅
  • Heptagon: 一项Scade工具的学术版原型
  • 师徒对决!阿森纳战曼城伤病情况 预计两队共11人缺席
  • 第37篇:AI伦理:偏见、公平性与负责任的人工智能
  • ubuntu18.04 编译VTK
  • ES6 新增特性
  • Pycharm中切换虚拟环境
  • 人工智能训练师
  • 【Linux】权限管理