解析康威的游戏网格

时间:2014-04-13 07:27:24

标签: ruby arrays parsing conways-game-of-life

这是我尝试用Ruby编写康威的生命游戏(http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules)。

我对“count_neighbours”方法有一个非常具体的问题。基本上当我到达网格的边缘时,我会得到一些奇怪的行为。当我解析第0行并到达最后一列(Cloumn 4)时,它会执行以下操作:

评估细胞:R:0 C:4

  • 评估邻居R:-1 C:3。状态:0
  • 评估邻居R:-1 C:4。状态:1
  • 评估邻居R:-1 C:5。状态:
  • 评估邻居R:0 C:3。状态:0
  • 评估邻居R:0 C:5。状态:
  • 评估邻居R:1 C:3。状态:1
  • 评估邻居R:1 C:4。状态:0
  • 评估邻居R:1 C:5。状态:

一个好处是“R:-1”基本上将评估包装到网格的底部,就好像网格的边缘是真正连接的一样。我喜欢无边网格的想法。

这里的坏处是该方法试图评估不存在的“C:5”(第5列),因为在此示例中网格是列0-4。所以这是我寻求帮助解决的第一个问题。理想情况下,我想评估第0列。

当方法尝试评估网格上的最后一行时,下一个问题是,第4行。当方法尝试评估“R:5”时,会抛出一个错误。错误是“未定义的方法`[]'为nil:NilClass(NoMethodError)”因为该行不存在。为了避免抛出错误,我在注释“#This line is a hack”下添加了一行,但我希望能够在没有错误的情况下评估此行。

我最后一个问题是,为什么超出最后一行到第5行抛出并且超出最后一列时出错并不会抛出错误?

    #Dimensions for the game grid
    WIDTH = 5
    HEIGHT = 5

    def rand_cell
      rand(2)
    end

    def starting_grid
      #Initialise the playing grid
      @start_grid = Array.new(WIDTH){Array.new(HEIGHT)}
      #Randomly generate starting state for each cell on the grid
      @start_grid.each_with_index do |row, rindex|
        row.each_with_index do |col, cindex|
          @start_grid[rindex][cindex] = rand_cell
        end
      end
    end

    def next_grid
      #build the next generation's grid to load values into
      @next_gen_grid = Array.new(WIDTH){Array.new(HEIGHT)}

      #parse each cell in the start grid to see if it lives in the next round
      @start_grid.each_with_index do |row, rindex|
        row.each_with_index do |col, cindex|
          puts "\n\nEvaluating cell: R: #{rindex} C: #{cindex}"
          @next_gen_grid[rindex][cindex] = will_cell_survive(rindex, cindex)
        end
      end   
      #move the newly generated grid to the start grid as a sterting point for the next round
      @start_grid = @next_gen_grid
    end

    def show_grid(grid)
      #Display the evolving cell structures in the console
      grid.each_with_index do |row, rindex|
        row.each_with_index do |col, cindex|
          if grid[rindex][cindex] == 1
            print "️⬛️ "
          else
            print "⬜️ ️"
      end  
    end
    puts "\n"
  end
end

def count_neighbours(row, col)
  cell_count = 0
  rows = [-1, 0, 1]
  cols = [-1, 0, 1]

  rows.each do |r|
    cols.each do |c|
      #ingnore the cell being evaluated
      unless c == 0 && r == 0

        #This line is a hack to stop an error when evaluating beyond the last row
        if  row != HEIGHT-1
          puts "Evaluating neighbor R: #{row+r} C: #{col+c}. State: #{@start_grid[(row+r)][(col+c)]}" 
          if @start_grid[(row+r)][(col+c)] == 1
            cell_count += 1
          end
        end

      end
    end
  end
  puts "Neighbour count is #{cell_count}"
  return cell_count
end

def will_cell_survive(rindex, cindex)
  count = count_neighbours(rindex, cindex)
  #If the cell being evaluated is currently alive
  if @start_grid[rindex][cindex] == 1
       #test rule 1 
    if alive_rule1(count)
      puts "Rule 1"
      return 0
          #test rule 2
    elsif alive_rule2(count)
      puts "Rule 2"
      return 1
    elsif
      #test rule 3
      puts "Rule 3"
      return 0
    end

  #If the cell being evaluated is currently dead      
  else
    #test rule 4
    alive_rule4(count)
      puts "Rule 4"
      return 1
  end
end

def alive_rule1(neighbour_count)
    neighbour_count < 2
end

def alive_rule2(neighbour_count)
  neighbour_count == 2 || neighbour_count == 3
end

def alive_rule3(neighbour_count)
  neighbour_count > 3
end

def alive_rule4(neighbour_count)
  neighbour_count == 3
end


#Run just one round of the game
system "clear"
starting_grid
show_grid(@start_grid)
puts "\n\n" 
next_grid
show_grid(@next_gen_grid)


#Initiate the game grid
#   system "clear"
#   starting_grid

#Run the game
# 200.times do |t|
#   system "clear"
#   puts "\n\n" 
#   next_grid
#   puts "Grid #{t}"
#   show_grid(@next_gen_grid)
#   sleep(0.25)
# end

[编辑]:实施答案的代码位于https://github.com/AxleMaxGit/ruby-conways-game

1 个答案:

答案 0 :(得分:3)

如果你想将边缘彼此连接起来(顺便说一下,它会形成一个&#34;圆环&#34;形状,或者你喜欢的是&#34;小行星&#34;世界模型你永远不能离开屏幕)然后最简单的调整是模块化算术:

更改:

if @start_grid[(row+r)][(col+c)] == 1

要:

if @start_grid[(row+r) % HEIGHT][(col+c) % WIDTH] == 1

运算符符号%是模运算,并根据需要精确地执行环绕逻辑。

超越最后一行的原因与超越最后一列的行为不同的原因是:

@start_grid[ 3 ][ 5 ] == nil

在您检查邻居时返回false,其他所有内容都正常工作。

然而,

@start_grid[ 5 ][ 3 ]

是一个问题,因为@start_grid[ 5 ]nil,所以它实际上是

nil[ 3 ]

抛出错误是因为Ruby没有解析[]nil的含义的逻辑。