我将如何检查我的扫雷游戏的所有相邻单元格?

how would i check all adjacent cells for my minesweeper game?

我一直在努力寻找一种方法来检查我的扫雷游戏的每个相邻单元格,但我做空了。我是 python 的初学者,也想开始使用 OOP。然而,在我到达那里之前,我需要纠正这个问题。我看到的所有教程都不使用基本的 python,而是使用与我使用的 IDLE 不同的版本,所以我很挣扎。谁能帮我?我需要能够绕过每个相邻的牢房并检查那里是否有炸弹。检查是否有炸弹的值是 1,也会变成红色。非常感谢大家! 如果你能帮我把它简化一点,那就太好了。

import random
import pygame

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0) 
WIDTH = 20
HEIGHT = 20 
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
    grid.append([])
    for column in range(10):
        grid[row].append(0)
print(grid)

pygame.init()
 
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
 
pygame.display.set_caption("Array Backed Grid")
 
done = False
 
clock = pygame.time.Clock()

#class bomb:
    #def revealed(self,pick):#this is the grid thats been picked
     #   self.shown = shown
def placebomb():
    for i in range(bombnum):
        while True:
            row  = random.randint(0,8)
            column = random.randint(0,8)
            if grid[row][column] == 0:
                grid[row][column] = 1
                break
placebomb()  
        
                
# -------- Main Program Loop -----------
while not done:
    for event in pygame.event.get():  
        if event.type == pygame.QUIT:  
            done = True 
        elif event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
            column = pos[0] // (WIDTH + MARGIN)
            row = (pos[1]-50) // (HEIGHT + MARGIN)
            grid[row][column] = 1
            print("Click ", pos, "Grid coordinates: ", row, column)


    screen.fill(BLACK)
 
    for row in range(10):
        for column in range(10):
            color = WHITE
            if grid[row][column] == 1:
                color = RED
            pygame.draw.rect(screen,
                             color,
                             [(MARGIN + WIDTH) * (column) + MARGIN,
                              50+(MARGIN + HEIGHT) * row + MARGIN,
                              WIDTH,
                              HEIGHT])
 

    clock.tick(60)
 

    pygame.display.flip()
 

pygame.quit()

所以基本上就像这个线程中的另一个答案一样,您可以通过向当前的 x 和 y 添加 -1、0 或 1 来检查 surrounding/adjacent 图块。

您可以编写一个单独的函数,该函数将 return 带有炸弹的周围瓷砖的坐标列表,并将瓷砖坐标和网格作为参数,如下所示:

def check_surrounding_tiles(current_x, current_y, grid):
     bomb_tiles = []
     #Check starting from position (current_x - 1, current_y - 1)
     for i in range(-1, 2):
          for j in range(-1, 2):
               #Ignore the current x and y, and do bounds checking
               if (i != 0 or j != 0) and (current_x + i) > -1 and (current_y + j) > -1 and (current_x + i) < len(grid) and (current_y + j) < len(grid[0]):
                    if grid[current_x + i][current_y + j] == 1
                         bomb_tiles.append[(current_x + i, current_y + j)]
     return bomb_tiles

如上所述,这将 return 包含炸弹的坐标对列表。

如果需要获取炸弹数量,只需使用以下命令:

adjacent_bomb_count = len(check_surrounding_tiles(<x position>,<y position>, grid))

只有8([1,1],[1,0],[1,-1],[0,1],[0,-1],[-1,1],[ -1,0],[-1,-1] 相对于点击的网格坐标)相邻的方块,所以只用“if”语句

就可以很容易地做你想做的事情

无论如何,只需检查周围的方块,如果为真,则添加到变量。

import random
import pygame

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0) 
WIDTH = 20
HEIGHT = 20 
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
    grid.append([])
    for column in range(10):
        grid[row].append(0)
#print(grid)

pygame.init()
 
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
 
pygame.display.set_caption("Array Backed Grid")
 
done = False
 
clock = pygame.time.Clock()

#class bomb:
    #def revealed(self,pick):#this is the grid thats been picked
     #   self.shown = shown
def placebomb():
    for i in range(bombnum):
        while True:
            row  = random.randint(0,8)
            column = random.randint(0,8)
            if grid[row][column] == 0:
                grid[row][column] = 1
                break
placebomb()  
print(grid)                   
# -------- Main Program Loop -----------
while not done:
    for event in pygame.event.get():  
        if event.type == pygame.QUIT:  
            done = True 
        elif event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
            column = pos[0] // (WIDTH + MARGIN)
            row = (pos[1]-50) // (HEIGHT + MARGIN)
            grid[row][column] = 1
            print("Click ", pos, "Grid coordinates: ", row, column)
            NBombs = 0
            for i in range(-1,2):
                for k in range(-1,2):
                    if (i!=0 or k!=0) and (row+i>=0) and (column+k>=0) and (row+i<len(grid)) and (column+k<len(grid[0])):#prevents from both being 0, or for the index to be negative or out of range
                        print(i,k)
                        if grid[row+i][column+k] == 1:
                            NBombs+=1
            print("Number of bombs:",NBombs)
                    
                        

    screen.fill(BLACK)
 
    for row in range(10):
        for column in range(10):
            color = WHITE
            if grid[row][column] == 1:
                color = RED
            pygame.draw.rect(screen,
                             color,
                             [(MARGIN + WIDTH) * (column) + MARGIN,
                              50+(MARGIN + HEIGHT) * row + MARGIN,
                              WIDTH,
                              HEIGHT])
 

    clock.tick(60)
 

    pygame.display.flip()
 

pygame.quit()

首先,您肯定想以此为基础制作一个 OOP 项目。扫雷可能接近您无需 object-oriented 编程即可合理完成的复杂性极限,但如果您想采用基本的扫雷概念并使其更有趣/更复杂,您将需要更好的结构。

即使您不考虑让它变得更复杂,考虑一下您 可以 添加什么样的复杂性也有助于规划您的 classes。 (也许你没有意识到有一个“计划你的 classes”步骤?我会讲到。)我这里的步骤应该适用于几乎所有开始的 OOP 项目,因为扫雷似乎只是一个方便的例子.

我意识到这个答案将 giant 从 OP 的 how-do-I-check-the-nearest-neighbors 问题转移,但 OP 也在询问 OOP 并在 OOP 上下文中回答他们的问题意味着得到class 模型到位。尝试将 OOP 改造为 non-OOP 代码是可能的,但通常比从头开始进行 OOP 更难。

  1. 如果您是 Python 中的 OOP 新手,请检查 these two tutorials。它们都 运行 通过基础知识,既不需要 IDE,也不会引入超出您需要开始的复杂性/功能。我的其余回答将假定您熟悉如何编写 classes。我强烈建议您在自己的终端上输入这两个教程,并在继续之前尝试几个变体。
  2. 既然您已经大致了解了 class 是什么,,请列出您在扫雷程序中需要的所有操作,以及哪些类型里面有很多东西。这里的重点不是写代码,自然语言将在这里充当伪代码。您可能想要包括的内容:“将地雷添加到网格上的单元格”、“检查网格上的单元格”、“显示网格上给定单元格附近的点数”。您还可以包括一些“未来计划”,例如“在游戏中间调整网格大小”、“限制移动次数”、“获得提示”、“允许地雷移动”、“让人们穿过雷区”,多种类型的检查/地雷/单元格,作为正方形网格替代的六角网格,strangely-shaped 和 3D 网格等。如果您还不知道如何编写这些东西,请不要担心。如果您有一个有趣的想法,请将其添加到列表中。
  3. 浏览该列表并对不断出现的主要词/概念做一些笔记。这些将是您的 classes。对于 Minesweeper,我会列出“Cell”、“Grid”、“Game”、“Check”和“Mine”,但您的里程数可能会有所不同。这些 不是 必然 最终的 class 列表。它们只是组织您的想法的一个步骤,因此您可以将 high-level“扫雷游戏”变成具体且可扩展的游戏。您可以稍后添加/删除 classes,一旦您对实际使用的内容有了更好的了解。
  4. 注意 classes 对象之间的关系,包括需要描述哪些对象才能使其他对象有意义/被实例化。这一步类似于你如何计划一个relational database:一个游戏有一个且只有一个网格,一个网格是以固定模式排列的单元格的集合,一个单元格可能包含也可能不包含一个地雷,一个检查显示地雷是否在给定单元格中,如果不在,则显示与已检查单元格相邻的单元格中地雷的数量等。
  5. 打开您的 IDE,然后开始在文件中屏蔽 classes。这里的重点不是编写代码,而是将所有 classes 和注释放入代码中以便于参考。如果这个文件开始变大,想一想 classes 如何组合在一起并将一个文件拆分成几个文件,并用 import 语句将它们连接起来。通过“屏蔽 classes”,我的意思是“编写最简单的 classes 可以编译,但不要试图让它们成为 运行”。在此阶段,您可以/应该拥有如下所示的 classes:
class Grid:
    """A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern"""
    def __init__(self, game, **setup_params):
        """Initialize the grid given a game and possibly other parameters for shape and size"""
        raise NotImplementedError('This code has not yet been written.')

    def resize_grid(self, new_size):
        raise NotImplementedError('This code has not yet been written.')

    def check_cell_at(self, position):
        raise NotImplementedError('This code has not yet been written.')

需要检查的事项:这是完全合法的 Python 并且可以正常编译。第 2 步到第 4 步中的所有注释都应该以文档字符串结尾。您在笔记中描述的所有目标功能都对应于 classes 上与这些功能有关的特定方法。您描述的每个 class 都存在,并且有一个描述其目的和结构的文档字符串。每个 class 的 __init__() 方法都采用实例化 class 所需的参数。每个方法都有一些可能有用的参数。您以后总是可以编辑参数列表,但同样,重点是在编写代码之前组织代码,以便轻松跟踪关系和功能。如果您正在使用 Git 或其他 version-tracking 工具,请在完成此步骤后立即进行首次提交。

  1. 现在你已经掌握了所有的结构封锁,弄清楚“主要入口点”是什么,实例化 class(在我的方法中可能是游戏),并检查您的代码是否编译,运行s,并以NotImplementedError 与您期望的行和消息一起出现。 (如果是这样,那就成功了!)如果项目感觉很大,或者如果您要与其他开发人员共享代码,您可能还想在此阶段添加单元测试。期望每个测试都会失败,但编写测试有助于记录计划的功能。
  2. 一个一个地填写方法,添加单元测试以配合新的或更新的方法,然后尝试 运行一次一点地修改代码。您不必按任何特定顺序编写方法,但是一旦所有 __init__() 方法都完成后测试会更容易,而且这些方法通常很简单。

现在,您如何检查单元格的邻居?

  • 您应该在 Cell 上有“获取我的邻居列表”和“获取此单元格是否包含地雷”的方法。您可能在 Grid 上还有一个方法来“让 Cell 到位”。您可能有多种类型的检查,但基础扫雷游戏只有一种,因此 Cell 有一个方法,如
   def check(self):
       """Returns "BOMB" if this cell has a Bomb. Otherwise, returns the number of neighbors with bombs as an integer."""
       if self.has_bomb:
           return "BOMB"
       neighboring_mines = 0
       for cell in self.grid.get_neighbors_of(self.position):
            if cell.has_bomb:
                neighboring_mines += 1
       return neighboring_mines