import pygame
import random
import os
import sys
import time
import numpy as np
import torch
pygame.init()
class Minesweeper:
def __init__(self,grid_width=10,grid_height=10,cell_size=50,mine_count=13,window=True):
#设置游戏窗口的大小、标题和背景色。
#初始化游戏所需的变量,包括地雷数量、格子大小、游戏状态等
# 定义常量
self.GRID_WIDTH = grid_width # 游戏网格宽度
self.GRID_HEIGHT = grid_height # 游戏网格高度
self.CELL_SIZE = cell_size # 单元格尺寸
self.MINE_COUNT = mine_count # 雷的数量
self.RED = (255, 0, 0) # 炸弹颜色,红色
self.WHITE = (255, 255, 255) # 白色底色
self.BLACK = (0, 0, 0) # 黑色,表示未翻开的方块色
self.GREY = (128, 128, 128) # 灰色,表示翻开后的方块颜色
self.font = pygame.font.SysFont(None, 30) # 设置字体
self.window = window
self.akc=False
if self.window:
pygame.display.set_caption("Minesweeper") # 设置窗口标题
self.screen = pygame.display.set_mode((self.GRID_WIDTH * self.CELL_SIZE, self.GRID_HEIGHT * self.CELL_SIZE)) # 设置可视化窗口大小
self.r = 0.
self.R = []
self.actions = []
self.condition = True
self.map = np.zeros([self.GRID_WIDTH, self.GRID_HEIGHT])
self.t = 0
self.count = np.zeros([self.GRID_WIDTH, self.GRID_HEIGHT])
else:
self.r=0.
self.R=[]
self.actions=[]
self.condition=True
self.map=np.zeros([self.GRID_WIDTH,self.GRID_HEIGHT])
self.t = 0
self.count=np.zeros([self.GRID_WIDTH,self.GRID_HEIGHT])
self.grid = [[0 for _ in range(self.GRID_HEIGHT)] for _ in range(self.GRID_WIDTH)] # 创建二维数组,0表示无雷的安全区域
self.mines = [] # 存储地雷的位置
for i in range(self.MINE_COUNT):
while True:
x = random.randint(0, self.GRID_WIDTH - 1) # 随机生成x坐标
y = random.randint(0, self.GRID_HEIGHT - 1) # 随机生成y坐标
if (x, y) not in self.mines: # 如果该位置没有地雷
self.mines.append((x, y)) # 将该位置添加到地雷列表中
self.grid[x][y] = -1 # 在该位置标记为地雷
break
self.revealed = np.array([[False for _ in range(self.GRID_HEIGHT)] for _ in range(self.GRID_WIDTH)]) # 创建未翻开的方块信息
if not self.window:
self.status=self.get_status()
else:
self.status = self.get_status()
def get_adjacent_cells(self, x, y): # 类的子函数 get_status的功能为获取环境当前状态,状态为当前游戏中每个格子的信息(未揭示为1)和点击次数,用作为智能体的输入信息,其返回参数为状态信息status
'''获取目标位置的相邻元素格'''
cells = []
for i in range(max(0, x - 1), min(x + 2, self.GRID_WIDTH)):
for j in range(max(0, y - 1), min(y + 2, self.GRID_HEIGHT)):
if i != x or j != y:
cells.append((i, j))
return cells
def get_status(self):
''' 用于获取环境的当前状态,返回一个包含未翻开的方块信息和雷的数量的数组。'''
status = (self.revealed.astype(np.float64) - 1) + self.map
status = np.stack((status,self.count),axis=0)
return status
def reveal_cell(self, x, y):
'''揭示指定位置的格子'''
self.revealed[x][y] = True # 标记该位置已揭示
if self.window:
rect = pygame.Rect(x * self.CELL_SIZE, y * self.CELL_SIZE, self.CELL_SIZE, self.CELL_SIZE) # 创建矩形
pygame.draw.rect(self.screen, self.WHITE, rect) # 在该位置绘制白色矩形
if self.grid[x][y] == -1: # 如果该位置是地雷
self.map[x, y] = -10
pygame.draw.circle(self.screen, self.RED, rect.center, self.CELL_SIZE // 3) # 在该位置绘制红色圆形
else:
pygame.draw.rect(self.screen, self.GREY, rect) # 在该位置绘制灰色矩形
self.map[x, y] = self.count_adjacent_mines(x, y)
if self.count_adjacent_mines(x, y) > 0: # 如果该位置周围有地雷
text = self.font.render(str(self.count_adjacent_mines(x, y)), True, self.BLACK) # 创建文本
text_rect = text.get_rect(center=rect.center) # 设置文本位置
self.screen.blit(text, text_rect) # 在该位置绘制文本
else:
if self.grid[x][y] == -1:
self.map[x,y]=-10
else:
self.map[x,y]=self.count_adjacent_mines(x, y)
def reveal_all_cells(self):
'''将所有未揭示的格子都揭示出来'''
for i in range(self.GRID_WIDTH):
for j in range(self.GRID_HEIGHT):
if not self.revealed[i][j]:
self.reveal_cell(i, j)
def agent_click(self,x,y): #该函数在与智能体交互时使用
'''智能体点击指定位置的格子'''
if self.revealed[x][y]:
# self.r+=-(100.+4*self.t)/100.
self.r += 0.
elif self.grid[x][y] != -1:
self.reveal_cell(x,y)
self.r=1.
# self.r += (1.+2*self.revealed.sum()/(self.GRID_WIDTH*self.GRID_HEIGHT))
if self.count_adjacent_mines(x, y) == 0: # 如果该位置周围没有地雷
for i, j in self.get_adjacent_cells(x, y):
if self.grid[i][j] != -1 and not self.revealed[i][j]:
self.agent_click(i, j) # 递归揭示周围的位置
else:
self.reveal_all_cells()
# self.r+=-10.
self.r += 0.
self.condition=False
def handle_left_click(self, x, y): #具体功能与函数agent_click类似,最不同的地方是当点击到雷时会重置游戏。
if self.revealed[x][y]:
# self.r+=-(100.+4*self.t)/100.
self.r += 0.
elif self.grid[x][y] != -1: # 如果该位置不是地雷
self.reveal_cell(x, y) # 揭示该位置
self.r = 1.
if self.count_adjacent_mines(x, y) == 0: # 如果该位置周围没有地雷
for i, j in self.get_adjacent_cells(x, y):
if self.grid[i][j] != -1 and not self.revealed[i][j]:
self.handle_left_click(i, j) # 递归揭示周围的位置
else:
self.reveal_all_cells() # 揭示所有位置
self.r += 0.
self.condition = False
pygame.display.flip()
if self.akc:
# self.running=False
time.sleep(2.)
self.reset()
def handle_right_click(self, x, y):
pass
def draw_grid(self):
'''用于绘制游戏窗口中的网格'''
for i in range(self.GRID_WIDTH):
for j in range(self.GRID_HEIGHT):
rect = pygame.Rect(i * self.CELL_SIZE, j * self.CELL_SIZE, self.CELL_SIZE, self.CELL_SIZE) # 创建矩形
pygame.draw.rect(self.screen, self.WHITE, rect, 1) # 在该位置绘制白色矩形
if self.revealed[i][j]: # 如果该位置已揭示
if self.grid[i][j] == -1: # 如果该位置是地雷
pygame.draw.circle(self.screen, self.RED, rect.center, self.CELL_SIZE // 3) # 在该位置绘制红色圆形
else:
pygame.draw.rect(self.screen, self.GREY, rect) # 在该�