使用python制作扫雷游戏

简介: 扫雷是一款单人益智游戏,相信大部分人都在以前上微机课的时候玩过。游戏的目标是借助每个区域中相邻地雷数量的线索,清除包含隐藏的“地雷”或炸弹的单元格,但不引爆其中任何一个,全部清除后即可获胜。今天我们用 Python 完成这个小程序,并且用AI来学习并实现它。看看我们将要实现的最终样子。👇运行扫雷1.确保安装了Python 3.6+2.安装Pygame3.克隆这个存储库:

扫雷是一款单人益智游戏,相信大部分人都在以前上微机课的时候玩过。游戏的目标是借助每个区域中相邻地雷数量的线索,清除包含隐藏的“地雷”或炸弹的单元格,但不引爆其中任何一个,全部清除后即可获胜。今天我们用 Python 完成这个小程序,并且用AI来学习并实现它。

看看我们将要实现的最终样子。👇

运行扫雷

1.确保安装了Python 3.6+。

2.安装Pygame。

3.克隆这个存储库:

GitHub地址:github.com/wanghao22/

设置 minesweeper.py

扫雷游戏表示

1.classMinesweeper():
def__init__(self, height=8, width=8, mines=8):
# 设置初始宽度、高度和地雷数量    self.height=heightself.width=widthself.mines=set()
# 初始化一个没有地雷的空字段    self.board= []    
foriinrange(self.height):
row= []        
forjinrange(self.width):
row.append(False)
self.board.append(row)
# 随机添加地雷    whilelen(self.mines) !=mines:        
i=random.randrange(height)
j=random.randrange(width)        
ifnotself.board[i][j]:
self.mines.add((i, j))
self.board[i][j] =True# 最开始,玩家没有发现地雷    self.mines_found=set()

输出地雷所在位置的基于文本的表示

defprint(self):
foriinrange(self.height):
print("--"*self.width+"-")
forjinrange(self.width):
ifself.board[i][j]:
print("|X", end="")
else:
print("| ", end="")
print("|")
print("--"*self.width+"-")
defis_mine(self, cell):
i, j=cellreturnself.board[i][j]
defnearby_mines(self, cell):

返回给定单元格的一行和一列内的地雷数,不包括单元格本身.def nearby_mines(self, cell): # 保持附近地雷的数量 count = 0 # 遍历一行和一列内的所有单元格 for i in range(cell[0] - 1, cell[0] + 2): for j in range(cell[1] - 1, cell[1] + 2): # 忽略单元格本身 if (i, j) == cell: continue # 如果单元格在边界内并且是地雷,则更新计数 if 0 <= i < self.height and 0 <= j < self.width: if self.board[i][j]: count += 1 return count

检查是否已标记所有地雷。

defwon(self):
returnself.mines_found==self.mines

关于扫雷游戏的逻辑语句 一个句子由一组棋盘单元和这些单元格的数量组成。

classSentence():
def__init__(self, cells, count):
self.cells=set(cells)
self.count=countdef__eq__(self, other):
returnself.cells==other.cellsandself.count==other.countdef__str__(self):
returnf"{self.cells} = {self.count}"defknown_mines(self):

返回 self.cells 中已知为地雷的所有单元格的集合

def known_mines(self):

if len(self.cells) == self.count:

 return self.cells


返回 self.cells 中已知安全的所有单元格的集合。


def known_safes(self):      

if self.count == 0:

 return self.cells


鉴于已知单元格是地雷,更新内部知识表示。


def mark_mine(self, cell):      

if cell in self.cells:

 self.cells.discard(cell)

 self.count -= 1


鉴于已知单元格是安全的,更新内部知识表示。


def mark_safe(self, cell):  

       if cell in self.cells:

           self.cells.discard(cell)


扫雷游戏玩家


class MinesweeperAI():

   def __init__(self, height=8, width=8):

       # 设置初始高度和宽度        

       self.height = height

       self.width = width

       # 跟踪点击了哪些单元格

       self.moves_made = set()

       # 跟踪已知安全或地雷的细胞        

       self.mines = set()

       self.safes = set()

       # 关于已知为真游戏的句子列表

       self.knowledge = []

将一个单元格标记为地雷,并更新所有知识以将该单元格也标记为地雷。

defmark_mine(self, cell):
self.mines.add(cell)
forsentenceinself.knowledge:
sentence.mark_mine(cell)

将一个单元格标记为安全,并更新所有知识以将该单元格也标记为安全

defmark_safe(self, cell):  
self.safes.add(cell)        
forsentenceinself.knowledge:
sentence.mark_safe(cell)

用于获取所有附近的单元格

defnearby_cells(self, cell):
cells=set()
foriinrange(cell[0] -1, cell[0] +2):
forjinrange(cell[1] -1, cell[1] +2):
if (i, j) ==cell:
continueif0<=i<self.heightand0<=j<self.width:
cells.add((i, j))
returncells

当扫雷板告诉我们,对于给定的安全单元,有多少相邻单元中有地雷时调用。

这个功能应该:

1)将单元格标记为已进行的移动

2)将单元格标记为安全

3)根据 cell 和 count 的值在 AI 的知识库中添加一个新句子

4)如果可以根据 AI 的知识库得出结论,则将任何其他单元格标记为安全或地雷

5) 如果可以从现有知识中推断出任何新句子,则将其添加到 AI 的知识库中

defadd_knowledge(self, cell, count): 
self.moves_made.add(cell)
# 标记单元格安全ifcellnotinself.safes:    
self.mark_safe(cell)
# 获取所有附近的单元格nearby=self.nearby_cells(cell)       
nearby-=self.safes|self.moves_madenew_sentence=Sentence(nearby, count)
self.knowledge.append(new_sentence)
new_safes=set()
new_mines=set()
forsentenceinself.knowledge:
iflen(sentence.cells) ==0:
self.knowledge.remove(sentence)           
else:
tmp_new_safes=sentence.known_safes()
tmp_new_mines=sentence.known_mines()                
iftype(tmp_new_safes) isset:
new_safes|=tmp_new_safesiftype(tmp_new_mines) isset:
new_mines|=tmp_new_minesforsafeinnew_safes:
self.mark_safe(safe)        
formineinnew_mines:
self.mark_mine(mine)
prev_sentence=new_sentencenew_inferences= []
forsentenceinself.knowledge:
iflen(sentence.cells) ==0:
self.knowledge.remove(sentence)
elifprev_sentence==sentence:
breakelifprev_sentence.cells<=sentence.cells:
inf_cells=sentence.cells-prev_sentence.cellsinf_count=sentence.count-prev_sentence.countnew_inferences.append(Sentence(inf_cells, inf_count))
prev_sentence=sentenceself.knowledge+=new_inferencesdefmake_safe_move(self):

返回一个安全的单元格以在扫雷板上选择。必须知道该移动是安全的,而不是已经做出的移动。 该函数可以使用 self.mines、self.safes 和 self.moves_made 中的知识,但不应修改任何这些值。

1.defmake_safe_move(self): 
safe_moves=self.safes.copy()
safe_moves-=self.moves_madeiflen(safe_moves) ==0:
returnNonereturnsafe_moves.pop()
defmake_random_move(self):

返回在扫雷板上进行的移动。应该在以下单元格中随机选择:

1) 尚未被选中

2) 不知道是

BLACK= (0, 0, 0)
GRAY= (180, 180, 180)
WHITE= (255, 255, 255)

创建游戏

pygame.init()
size=width, height=600, 400screen=pygame.display.set_mode(size)

字体

字体可以在自己电脑中C:\Windows\Fonts的位置选择自己喜欢的复制到项目中 assets/fonts目录下即可,我用的是楷体

OPEN_SANS="assets/fonts/simkai.ttf"smallFont=pygame.font.Font(OPEN_SANS, 20)
mediumFont=pygame.font.Font(OPEN_SANS, 28)
largeFont=pygame.font.Font(OPEN_SANS, 40)

计算面板尺寸

BOARD_PADDING=20board_width= ((2/3) *width) - (BOARD_PADDING*2)
board_height=height- (BOARD_PADDING*2)
cell_size=int(min(board_width/WIDTH, board_height/HEIGHT))
board_origin= (BOARD_PADDING, BOARD_PADDING)

添加图片 这里我们只用了两张图,一个是地雷,一个是用来标记地雷的旗帜

flag=pygame.image.load("assets/images/flag.png")
flag=pygame.transform.scale(flag, (cell_size, cell_size))
mine=pygame.image.load("assets/images/mine.png")
mine=pygame.transform.scale(mine, (cell_size, cell_size))

创建游戏和 AI 代理

game=Minesweeper(height=HEIGHT, width=WIDTH, mines=MINES)
ai=MinesweeperAI(height=HEIGHT, width=WIDTH)

跟踪显示的单元格、标记的单元格以及是否被地雷击中

revealed=set()
flags=set()
lost=False

最初显示游戏说明

instructions=TruewhileTrue:
# 检查游戏是否退出foreventinpygame.event.get():
ifevent.type==pygame.QUIT:
sys.exit()
screen.fill(BLACK)
# 显示游戏说明ifinstructions:
# 标题title=largeFont.render("海拥 | 扫雷", True, WHITE)
titleRect=title.get_rect()
titleRect.center= ((width/2), 50)
screen.blit(title, titleRect)
# Rulesrules= [
"单击一个单元格以显示它",
"右键单击一个单元格以将其标记为地雷",
"成功标记所有地雷以获胜!"        ]
fori, ruleinenumerate(rules):
line=smallFont.render(rule, True, WHITE)
lineRect=line.get_rect()
lineRect.center= ((width/2), 150+30*i)
screen.blit(line, lineRect)
# 开始游戏按钮buttonRect=pygame.Rect((width/4), (3/4) *height, width/2, 50)
buttonText=mediumFont.render("开始游戏", True, BLACK)
buttonTextRect=buttonText.get_rect()
buttonTextRect.center=buttonRect.centerpygame.draw.rect(screen, WHITE, buttonRect)
screen.blit(buttonText, buttonTextRect)
# 检查是否点击播放按钮click, _, _=pygame.mouse.get_pressed()
ifclick==1:
mouse=pygame.mouse.get_pos()
ifbuttonRect.collidepoint(mouse):
instructions=Falsetime.sleep(0.3)
pygame.display.flip()
continue

画板

cells= []
foriinrange(HEIGHT):
row= []
forjinrange(WIDTH):
# 为单元格绘制矩形rect=pygame.Rect(
board_origin[0] +j*cell_size,
board_origin[1] +i*cell_size,
cell_size, cell_size        )
pygame.draw.rect(screen, GRAY, rect)
pygame.draw.rect(screen, WHITE, rect, 3)
# 如果需要,添加地雷、旗帜或数字ifgame.is_mine((i, j)) andlost:
screen.blit(mine, rect)
elif (i, j) inflags:
screen.blit(flag, rect)
elif (i, j) inrevealed:
neighbors=smallFont.render(
str(game.nearby_mines((i, j))),
True, BLACK            )
neighborsTextRect=neighbors.get_rect()
neighborsTextRect.center=rect.centerscreen.blit(neighbors, neighborsTextRect)
row.append(rect)
cells.append(row)

AI 移动按钮

aiButton=pygame.Rect(
    (2/3) *width+BOARD_PADDING, (1/3) *height-50,
    (width/3) -BOARD_PADDING*2, 50)
buttonText=mediumFont.render("AI 移动", True, BLACK)
buttonRect=buttonText.get_rect()
buttonRect.center=aiButton.centerpygame.draw.rect(screen, WHITE, aiButton)
screen.blit(buttonText, buttonRect)

重置按钮

resetButton=pygame.Rect(
        (2/3) *width+BOARD_PADDING, (1/3) *height+20,
        (width/3) -BOARD_PADDING*2, 50    )
buttonText=mediumFont.render("重置", True, BLACK)
buttonRect=buttonText.get_rect()
buttonRect.center=resetButton.centerpygame.draw.rect(screen, WHITE, resetButton)
screen.blit(buttonText, buttonRect)

显示文字

text="失败"iflostelse"获胜"ifgame.mines==flagselse""text=mediumFont.render(text, True, WHITE)
textRect=text.get_rect()
textRect.center= ((5/6) *width, (2/3) *height)
screen.blit(text, textRect)
move=Noneleft, _, right=pygame.mouse.get_pressed()

检查右键单击以切换标记

ifright==1andnotlost:
mouse=pygame.mouse.get_pos()
foriinrange(HEIGHT):
forjinrange(WIDTH):
ifcells[i][j].collidepoint(mouse) and (i, j) notinrevealed:
if (i, j) inflags:
flags.remove((i, j))
else:
flags.add((i, j))
time.sleep(0.2)
elifleft==1:
mouse=pygame.mouse.get_pos()

如果单击 AI 按钮,则进行 AI 移动

ifaiButton.collidepoint(mouse) andnotlost:
move=ai.make_safe_move()
ifmoveisNone:
move=ai.make_random_move()
ifmoveisNone:
flags=ai.mines.copy()
print("No moves left to make.")
else:
print("No known safe moves, AI making random move.")
else:
print("AI making safe move.")
time.sleep(0.2)

重置游戏状态

elifresetButton.collidepoint(mouse):
game=Minesweeper(height=HEIGHT, width=WIDTH, mines=MINES)
ai=MinesweeperAI(height=HEIGHT, width=WIDTH)
revealed=set()
flags=set()
lost=Falsecontinue

用户自定义动作

elifnotlost:
foriinrange(HEIGHT):
forjinrange(WIDTH):
if (cells[i][j].collidepoint(mouse)
and (i, j) notinflagsand (i, j) notinrevealed):
move= (i, j)

行动起来,更新AI知识

ifmove:
ifgame.is_mine(move):
lost=Trueelse:
nearby=game.nearby_mines(move)
revealed.add(move)
ai.add_knowledge(move, nearby)
pygame.display.flip()


相关文章
|
1天前
|
Python
python编写下象棋游戏|4-14
python编写下象棋游戏|4-14
|
19天前
|
人工智能 算法 图形学
总有一个是你想要的分享40个Python游戏源代码
这是一系列基于Python开发的游戏项目集合,包括中国象棋、麻将、足球、坦克大战、扑克等多种类型游戏,运用了Pygame等库实现图形界面与AI算法。此外还包含迷宫、数独、推箱子等益智游戏及经典游戏如《仙剑奇侠传二战棋版》和《星露谷物语》的Python版本,适合编程学习与娱乐。
48 11
|
21天前
|
消息中间件 数据采集 数据库
庆祝吧!Python IPC让进程间的合作,比团队游戏还默契
【9月更文挑战第7天】在这个数字化时代,软件系统日益复杂,单进程已难以高效处理海量数据。Python IPC(进程间通信)技术应运而生,使多进程协作如同训练有素的电竞战队般默契。通过`multiprocessing`模块中的Pipe等功能,进程间可以直接传递数据,无需依赖低效的文件共享或数据库读写。此外,Python IPC还提供了消息队列、共享内存和套接字等多种机制,适用于不同场景,使进程间的合作更加高效、精准。这一技术革新让开发者能轻松应对复杂挑战,构建更健壮的软件系统。
28 1
|
30天前
|
机器学习/深度学习 存储 定位技术
强化学习Agent系列(一)——PyGame游戏编程,Python 贪吃蛇制作实战教学
本文是关于使用Pygame库开发Python贪吃蛇游戏的实战教学,介绍了Pygame的基本使用、窗口初始化、事件处理、键盘控制移动、以及实现游戏逻辑和对象交互的方法。
|
2月前
|
机器学习/深度学习 人工智能 开发者
Python适合做游戏吗?
【7月更文挑战第2天】Python适合做游戏吗?
100 56
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
【机器学习】python之人工智能应用篇--游戏生成技术
游戏生成技术,特别是生成式人工智能(Generative Artificial Intelligence, 简称Generative AI),正逐步革新游戏开发的多个层面,从内容创作到体验设计。这些技术主要利用机器学习、深度学习以及程序化内容生成(Procedural Content Generation, PCG)来自动创造游戏内的各种元素,显著提高了开发效率、丰富了游戏内容并增强了玩家体验。以下是生成式AI在游戏开发中的几个关键应用场景概述
30 2
|
1月前
|
Python
【python】Python成语接龙游戏[1-3难度均有](源码+数据)【独一无二】
【python】Python成语接龙游戏[1-3难度均有](源码+数据)【独一无二】
|
1月前
|
Python
【Leetcode刷题Python】174. 地下城游戏
LeetCode 174题 "地下城游戏" 的Python解决方案,使用动态规划算法计算骑士从左上角到右下角拯救公主所需的最低初始健康点数。
43 3
|
1月前
|
算法 索引 Python
【Leetcode刷题Python】55. 跳跃游戏
解决LeetCode "跳跃游戏"问题的Python实现代码,使用了贪心算法的思路。代码中初始化最远可到达位置 max_k,并遍历数组 nums,通过更新 max_k 来记录每次能跳到的最远位置,如果在任何时刻 max_k 大于或等于数组的最后一个索引,则返回 True,表示可以到达数组的末尾;如果当前索引 i 超出了 max_k,则返回 False,表示无法继续前进。时间复杂度为 O(n),空间复杂度为 O(1)。
36 1
|
1月前
|
算法 Python
【python】python基于 Q-learning 算法的迷宫游戏(源码+论文)【独一无二】
【python】python基于 Q-learning 算法的迷宫游戏(源码+论文)【独一无二】