Вот моя попытка написать «Игру жизни» Конвея (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