Разбор сетки игры Конвея

Вот моя попытка написать «Игру жизни» Конвея (http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules) на Ruby.

У меня очень конкретный вопрос о методе count_neighbours. В основном, когда я добираюсь до края сетки, я получаю странное поведение. Когда я анализирую строку 0 и дохожу до последнего столбца (столбец 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)", поскольку такой строки не существует. Чтобы избежать возникновения ошибки, я добавил строку под комментарием «#Эта строка является взломом», но я хочу иметь возможность оценить эту строку без ошибки.

Последний вопрос, который у меня есть, заключается в том, почему переход от последней строки к строке 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

[EDIT]: код с реализованным ответом находится по адресу https://github.com/AxleMaxGit/ruby-conways-game


person AxleMax    schedule 13.04.2014    source источник


Ответы (1)


Если вы хотите соединить ребра друг с другом (что, кстати, создает форму «тора», или, если вы предпочитаете, модель мира «астероиды», где вы никогда не можете покинуть экран), то самая простая настройка - работать в модульном режиме. арифметика:

Изменять:

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

To:

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

Символ оператора % является модульной арифметикой и выполняет циклическую логику именно так, как вам нужно.

Причина, по которой выход за пределы последней строки ведет себя иначе, чем выход за пределы последнего столбца, заключается в том, что:

@start_grid[ 3 ][ 5 ] == nil

который возвращает ложь в вашей проверке для соседа, и все остальное работает как обычно.

Однако,

@start_grid[ 5 ][ 3 ]

это проблема, потому что @start_grid[ 5 ] это nil, так что это эффективно

nil[ 3 ]

возникает ошибка, потому что у Ruby нет логики для разрешения того, что означает [] для nil.

person Neil Slater    schedule 13.04.2014
comment
Нил, Спасибо за ответ. Очень умный. :-) - person AxleMax; 14.04.2014