红宝石扫雷游戏堆栈级别内的递归方法太深

时间:2014-06-12 05:11:15

标签: ruby recursion minesweeper

我试图制作一个方法,当它们为零时拉出瓷砖时,我会触发无休止的递归。我在irb中输入以下内容进行测试:

class Board
  attr_accessor :size, :board

  def initialize(size = gets.chomp.to_i)
    @size = size
    @board = (1..@size).map { |x| ["L"] * @size }
  end

  def print_board
    @board.map { |row| puts row.join }
  end
end

class Mine
  attr_accessor :proxi, :row, :col

  def initialize(proxi)
    @proxi = proxi
    @row = 0
    @col = 0
    @random = Random.new
    check_position
  end

  def check_position
    if @proxi.board[@row - 1][@col - 1] != "L"
      @row = @random.rand(1..@proxi.board.length)
      @col = @random.rand(1..@proxi.board[0].length)
      check_position
    else
      map_position
    end
  end

  def map_position
    @proxi.board[@row - 1][@col - 1] = "*"
  end
end

b = Board.new(20)
m = (1..b.size * 2).map { |i| i = Mine.new(b) }

class Detector
  attr_accessor :board, :proxi, :row, :col, :value

  def initialize(board, proxi)
    @board = board
    @proxi = proxi
    @row = 0
    @col = 0
    @value = 0
  end

  def mine?
    if @proxi.board[@row - 1][@col - 1] == "*"
      true
    else
      false
    end
  end

  def detect
    (@row - 1..@row + 1).each do |r|
      (@col - 1..@col + 1).each do |c|
        unless (r - 1 < 0 || r - 1 > @proxi.size - 1) || (c - 1 < 0 || c - 1 > @proxi.size - 1)
          @value += 1 if @proxi.board[r - 1][c - 1] == "*"
        end
      end
    end
  end

  def map_position
    @proxi.board[@row - 1][@col - 1] = @value
    @board.board[@row - 1][@col - 1] = @value
  end

  def recursion
    if @proxi.board[@row - 1][@col - 1] == 0
      (@row - 1..@row + 1).each do |r|
        (@col - 1..@col + 1).each do |c|
          unless (r - 1 < 0 || r - 1 > @proxi.size - 1) || (c - 1 < 0 || c - 1 > @proxi.size - 1)
            @row, @col = r, c 
            detect
            map_position
            recursion
          end
        end
      end
    end
  end

  def reset
    @row, @col, @value = 0, 0, 0
  end
end

d = Detector.new(b, b)
b.print_board

如果输出在右上角有足够的空间,则继续粘贴下一部分,否则重新进行。

d.row = 1
d.col = 1
d.mine?
d.detect
d.map_position
d.recursion
b.print_board

在递归方法中,如果堆栈级别太深,则会出错。我知道这是因为它有结束递归模式的问题。我认为我的两个除非声明阻止它从电路板上搜索,否则会限制它在电路板上的区域。此外,地雷会迫使它限制在它可能暴露的零点。也许是以某种方式在董事会上写下空格或覆盖董事会上的东西?

2 个答案:

答案 0 :(得分:0)

这里不需要递归。只需检查每个位置周围的地雷:

请始终使用基于0的数组来消除大量@blah - 1

detect中,如果有我的话需要立即返回,否则设置@value

def detect
  return if @proxi.board[@row][@col] == '*'
  value = 0 # no need to be global anymore
  (@row - 1..@row + 1).each do |r| 
    (@col - 1..@col + 1).each do |c| 
      unless r < 0 || r >= @proxi.size || c < 0 || c >= @proxi.size
        value += 1 if @proxi.board[r][c] == "*" 
      end
    end 
  end
  @proxi.board[@row][@col] = value
end 

现在你根本不需要map_position方法。只需检查所有单元格:

def check
  (0..@proxi.size - 1).each do |r|
    (0..@proxi.size - 1).each do |c|
      @row, @col = r, c
      detect
    end
  end
end

希望它有所帮助。

答案 1 :(得分:0)

超过堆栈大小通常表示递归没有正确的终止条件。在您的情况下,有哪种机制可以阻止recursion使用相同的@row @col对多次调用(@row - 1..@row + 1)?请注意,在(@col - 1..@col + 1) @row生成的9对中,其中一对是@col revealed本身。该函数将无限次地调用自己!

解决此问题的一种简单方法是使用recursion数组来跟踪被访问的单元格。然后recursion会将其访问的每个单元格标记为已访问,如果在已访问的单元格上调用它,则会立即返回。

此外,您在此处使用实例变量非常脆弱。递归依赖于每个函数调用都有自己的作用域这一事实,但{{1}}的每次调用都共享相同的实例变量 - 您用它来传递参数!您应该使用方法参数来保持范围不同。