Analyse de la grille de jeu de Conway
-
21-12-2019 - |
Question
Voici ma tentative d'écrire Game of Life de Conway (http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules) en Ruby.
J'ai une question très précise sur la méthode "count_neighbours".Fondamentalement, lorsque j'arrive au bord de la grille, j'obtiens un comportement étrange.Lorsque j'analyse la ligne 0 et que j'atteins la dernière colonne (Cloumn 4), cela ressemble à ceci :
Cellule d'évaluation :R :0C :4
- Évaluation du voisin R :-1°C :3.État:0
- Évaluation du voisin R :-1°C :4.État:1
- Évaluation du voisin R :-1°C :5.État:
- Évaluation du voisin R :0C :3.État:0
- Évaluation du voisin R :0C :5.État:
- Évaluation du voisin R :1C :3.État:1
- Évaluation du voisin R :1C :4.État:0
- Évaluation du voisin R :1C :5.État:
Une bonne chose est que "R :-1" enveloppe essentiellement l'évaluation jusqu'au bas de la grille comme si les bords de la grille étaient réellement connectés.J'aime l'idée d'une grille sans bord.
Le problème ici est que la méthode essaie d'évaluer "C :5" (Colonne 5) qui n'existe pas car dans cet exemple la grille est constituée des colonnes 0 à 4.Donc c'est le premier problème que je cherche à résoudre.Idéalement, je veux ici évaluer la colonne 0.
Le le prochain problème est lorsque la méthode tente d'évaluer la dernière ligne de la grille, la ligne 4.Lorsque la méthode tente d'évaluer "R :5", une erreur est générée.L'erreur est « méthode non définie `[ ]' pour nil:NilClass (NoMethodError) » car cette ligne n'existe pas.Pour éviter que l'erreur ne soit générée, j'ai ajouté la ligne sous le commentaire "#Cette ligne est un hack" mais je veux pouvoir évaluer cette ligne sans erreur.
Le dernière question que j'ai est-ce pourquoi aller au-delà de la dernière ligne jusqu'à la ligne 5 génère une erreur alors que dépasser la dernière colonne ne génère pas d'erreur ?
#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
[MODIFIER]:Le code avec la réponse implémentée se trouve à https://github.com/AxleMaxGit/ruby-conways-game
La solution
Si vous souhaitez relier les bords les uns aux autres (ce qui crée d'ailleurs une forme de "tore", ou si vous préférez le modèle mondial "astéroïdes" où vous ne pouvez jamais quitter l'écran), alors le réglage le plus simple est de travailler en mode modulaire. arithmétique:
Changement:
if @start_grid[(row+r)][(col+c)] == 1
À:
if @start_grid[(row+r) % HEIGHT][(col+c) % WIDTH] == 1
Le symbole de l'opérateur %
est une arithmétique modulaire et exécute la logique de bouclage précisément selon vos besoins.
La raison pour laquelle le dépassement de la dernière ligne se comporte différemment du dépassement de la dernière colonne est la suivante :
@start_grid[ 3 ][ 5 ] == nil
qui renvoie false dans votre chèque pour le voisin, et tout le reste fonctionne normalement.
Cependant,
@start_grid[ 5 ][ 3 ]
est un problème, parce que @start_grid[ 5 ]
est nil
, c'est donc effectivement
nil[ 3 ]
l'erreur est générée car Ruby n'a aucune logique pour résoudre ce qui []
signifie sur un nil
.