扫雷舰:显示周围街区功能冻结

2024-10-03 11:16:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我在为我正在制作的扫雷游戏做一个函数。此函数的目的是显示给定x和y的元素(元素所在的位置)。这可能不是实现它的最优雅的方法,但是我为每个标题为newField的tile创建了一个['-']列表。一个“-”代表一个隐藏的块(你不知道它是不是一个炸弹或者它周围有多少炸弹)。然后,我将newField中的一个元素更改为等于listField(列表列表)中相应的块。其中的每个列表表示一行)。一个X代表一颗炸弹,数字代表有多少颗炸弹环绕在这个区域。你知道吗

在扫雷艇中,当一个周围没有炸弹的街区被发现时,它周围的街区也会被发现。我做了一个名为revealSurroundings的函数来实现这一点。当我像下面那样在函数revealElement中运行revealSurroundings时,它会冻结我的计算机。但是,如果我在函数revealElement之外运行revealSurroundings,它就可以正常工作。你知道吗

如果有人对如何解决这个问题和/或提高效率有任何建议(因为我知道我使用的方法非常昂贵),请告诉我。你知道吗

wl = 10

listField = 
    [['1', '1', '0', '0', '0', '0', '0', '0', '1', 'X'], 
    ['X', '2', '2', '2', '2', '2', '2', '1', '1', '1'], 
    ['1', '2', 'X', 'X', '2', 'X', 'X', '2', '2', '2'], 
    ['1', '2', '3', '2', '2', '2', '3', '4', 'X', 'X'], 
    ['1', 'X', '1', '0', '0', '1', '3', 'X', 'X', '3'], 
    ['1', '2', '2', '1', '0', '1', 'X', 'X', '4', '2'], 
    ['2', '3', 'X', '1', '0', '1', '2', '2', '2', 'X'], 
    ['X', 'X', '2', '1', '1', '1', '1', '0', '1', '1'], 
    ['4', '5', '4', '3', '3', 'X', '2', '1', '0', '0'], 
    ['X', 'X', 'X', 'X', 'X', '3', 'X', '1', '0', '0']]
hiddenField = ['-' for i in range(wl*wl)]

def removeN(ls, n, iterations):
  items = ls
  try:
    for i in range(iterations):
      items.remove(n)
  except:
    pass
  return items

def revealElement(wl, x, y, userField):
  yReal = y-1
  xReal = x-1
  newField = list(userField)
  removeN(newField, '\n', wl-1)
  newField[yReal*wl + xReal] = listField[yReal][xReal]
  if newField[yReal*wl + xReal] == '0':
    revealSurroundings(wl, x, y, userField)
  for i in range(wl-1, 0, -1): # go backwards
      newField.insert(wl*i, '\n')

  return "".join(newField) # make it a string

def revealSurroundings(wl, x, y, userField):
  yReal = y-1
  xReal = x-1
  newField = userField
  try:
    newField = revealElement(wl, x+1, y, newField)
  except: 
    pass
  #right
  try:
    if xReal != 0:
      newField = revealElement(wl, x-1, y, newField)
  except:
    pass
  #left
  try:
    if yReal != 0:
      newField = revealElement(wl, x, y-1, newField)
  except:
    pass
  #up
  try:
    newField = revealElement(wl, x, y+1, newField)
  except:
    pass
  #down
  try:
    if yReal != 0:
      newField = revealElement(wl, x+1, y-1, newField)
  except:
    pass
  #upper-right
  try:
    if yReal != 0 and xReal != 0:
      newField = revealElement(wl, x-1, y-1, newField)
  except:
    pass
  #upper left
  try:
    newField = revealElement(wl, x+1, y+1, newField)
  except:
    pass
  #bottom-right
  try:
    if  xReal != 0:
      newField= revealElement(wl, x-1, y+1, newField)
  except:
    pass
  #bottom-left
  return newField

print revealSurroundings(10, 7, 2, hiddenField)

Tags: 函数元素列表ifpass炸弹tryexcept
2条回答

问题似乎是,当您在revealElement中运行revealSurroundings时,您正在创建一个永无止境的循环。你知道吗

运行revealElement时,如果元素为0,则运行函数revealSurroundings。在revealSurroundings内运行revealElement。如果显示的新元素也是零,它将再次运行revealSurroundings,并从revealElement的第一次迭代中检测零。你知道吗

这开始了一个无限循环,永不结束。我建议在revealSurroundings中添加另一个条件,比如检查是否已经通过简单的if语句显示了它旁边的字符。我还建议完全重新编写代码,因为@gorlen所说的。你知道吗

这种逻辑对于一个基本的扫雷揭示算法来说似乎是杀伤力过大。一些想法:

  • 扫雷艇网格是有状态的,由多个变量和函数组成。类是将所有这些逻辑连接的变量和函数封装在单个实体中的典型构造。可以使用__str__方法打印电路板,并将网格传递给初始值设定项。不需要为了算法的目的而将网格字符串化,只在内部将其作为一个列表。你知道吗
  • 在可见和不可见的两个单独列表中保留正方形是不可伸缩的。如果需要添加第三个属性,则必须创建另一个二维列表。考虑一个Tile类/recordtype/dict,其属性为visiblestatus/contents。这允许您将所有必需的网格数据存储在一个数据结构中。你知道吗
  • 变量名始终不清楚。wl必须是board size,但这是多余的信息,因为列表已经有了一个len属性,该属性保证了一致性并具有语义意义。你知道吗
  • 对所有变量和函数使用snake_case而不是camelCaseUpperCamelCase类名称。见PEP-8。你知道吗
  • 缩进4个空格;Python没有end关键字或大括号,因此任何小于4个空格的块都很难消除歧义。你知道吗
  • 不要将try/except块用于易于转换为条件的逻辑,尤其是在使用Pokemon exceptions时。您可能无意中抑制了非预期的错误,这是对不是为通用控制流设计的构造的滥用。你知道吗
  • 使用iterables和loops,而不是长序列的手工输入的条件句,这些条件句很难理解,而且容易出错。你知道吗
  • 使用yReal = y-1; xReal = x-1在1索引“人类”坐标和0索引坐标之间进行转换可能会导致混淆,因为它是在每一帧上递归执行的。如果必须这样做,请将逻辑移出算法,并尽可能靠近IO接口进行规范化/反规范化。你知道吗

这里有一个重写建议:

class Minesweeper:
    class Tile:
        def __init__(self, mark, visible=False):
            self.mark = mark
            self.visible = visible

        def __str__(self):
            return self.mark if self.visible else "-"

    neighbors = [(x, y) for x in range(-1, 2) 
                        for y in range(-1, 2) if x or y]

    def __init__(self, field):
        self.field = [[self.Tile(x) for x in row] for row in field]

    def in_bounds(self, x, y):
        return y >= 0 and y < len(self.field) and \
               x >= 0 and x < len(self.field[y])

    def reveal(self, x, y):
        if self.in_bounds(x, y) and not self.field[y][x].visible and \
          self.field[y][x].mark == "0":
            self.field[y][x].visible = True

            for dx, dy in self.neighbors:
                self.reveal(x + dx, y + dy)

    def __str__(self):
        return "".join("".join(map(str, row)) + "\n" for row in self.field)

if __name__ == "__main__":
    field = [
        "00000",
        "12210",
        "1XX10",    
    ]
    board = Minesweeper(field)
    print board
    board.reveal(3, 0)
    print board

输出:

  -
  -
  -

00000
  0
  0

相关问题 更多 >