Сапер: функция обнаружения окружающих блоков зависает

Я пытаюсь создать функцию для игры про саперов, которую делаю. Цель этой функции - выявить элемент с учетом x и y (где он расположен). Это, вероятно, не самый элегантный способ добиться этого, но я составляю список из ['-'] для каждой плитки под названием newField. '-' представляет собой скрытый блок (где вы не знаете, бомба это или сколько бомб окружает его). Затем я изменяю один элемент из newField на соответствующий ему блок из listField (который представляет собой список списков. Каждый список в нем представляет собой строку). X обозначает бомбу, а числа обозначают, сколько бомб находится внутри.

В «Сапере», когда обнаруживается блок, вокруг которого нет бомб, также обнаруживаются и окружающие его блоки. Я создал функцию под названием revealSurroundings, которая выполняет это. Когда я запускаю revealSurroundings в функции revealElement, как показано ниже, мой компьютер зависает. Однако, если я запускаю revealSurroundings вне функции revealElement, он работает нормально.

Если у кого-нибудь есть совет, как решить эту проблему и / или сделать ее более эффективной (потому что я знаю, что метод, который я использую, очень дорогостоящий), дайте мне знать.

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)

person Ariya Reddy    schedule 30.12.2019    source источник
comment
При отладке кода заключать каждый вызов функции в except: pass - ужасная идея, потому что это подавляет все ошибки. Удалите их, и я подозреваю, что вы увидите какую-то ошибку рекурсии.   -  person Seb    schedule 30.12.2019


Ответы (2)


Проблема, похоже, в том, что когда вы запускаете revealSurroundings в revealElement, вы создаете бесконечный цикл.

Когда вы запускаете revealElement, если элемент равен 0, запускается функция revealSurroundings. Внутри revealSurroundings вы запускаете revealElement. Если обнаруженный новый элемент также является нулем, он снова запускает revealSurroundings и обнаруживает ноль с первой итерации revealElement.

Это начинает бесконечный цикл, который никогда не заканчивается. Я предлагаю добавить еще одно условное выражение в revealSurroundings, например, проверить, не раскрыли ли вы уже символы рядом с ним, с помощью простого оператора if. Я также рекомендовал бы полностью переписать код из-за того, что заявил @gorlen.

person ZeOnlyOne    schedule 30.12.2019

Эта логика кажется излишней для базового алгоритма обнаружения Сапера. Несколько мыслей:

Вот предложение переписать:

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
person ggorlen    schedule 30.12.2019