Back to Home
Conway's Game of Life in Ruby 2D
11/1/2024
Ruby is a language that I wanted to learn. I decided it would be a good project to make Conway's Game of Life to get down the basics of the language.
Conway's Game of Life
If you haven't heard of Conway's Game of Life, it is a basic simulation of life represented by a grid of black and white cells. Each cell can be dead or alive depending on a certain subset of rules. Those rules are as follows:
- If a living cell has less than two living neighbors, it dies.
- If a living cell as more than three living neighbors, it dies.
- If a living cell has exactly two or three living neighbors, it will live on to the next generation.
- If a dead cell has exactly three living neighbors, it will become a living cell.
Creating a Program with Visuals in Ruby
While Ruby would be able to generate visuals inside of a terminal, I wanted to find a library that would allow me to create a window with 2D visuals. I stumbled upon a few different options while doing research, but the one that I decided on was Ruby 2D.
Ruby 2D is a simple graphics library that will allow me to create a window and draw squares to the screen. Creating a window would look like this:
require "ruby2d"
set width: 600, height: 600
show
Then say we want a square to move across the screen. That would be done like so:
require "ruby2d"
set width: 600, height: 600
square = Square.new
square.color = "blue"
update do
square.x += 1
end
show
Putting it Together
Now, in Conway's Game of Life, we aren't going to be having any squares move. Instead, I will create a square for every grid position and then change the color of that cell to represent if it is dead or alive.
I keep track of each cells state in an array of booleans. I then have another array of booleans to keep track of the next generations state. We need to have two arrays here because as I loop through each cell to determine the number of living neighbors, if I change that cells state, it could affect the output of the cells around it once I get to checking those. To avoid this, I keep track of all of the changes in the nextGenCells
array and clone it to the cells
array after every cell has been checked.
Below you can see a screenshot of the result as well as the final script itself. If you have Ruby and Ruby 2D set up on your machine, you can try running it yourself!
require "ruby2d"
WIDTH = 600
HEIGHT = 600
SQUARE_SIDE_LENGTH = 10
GRID_WIDTH = WIDTH / SQUARE_SIDE_LENGTH
GRID_HEIGHT = HEIGHT / SQUARE_SIDE_LENGTH
set width: WIDTH, height: HEIGHT, title: "Conway's Game of Life"
cells = []
nextGenCells = []
allSquares = []
for x in 0...GRID_WIDTH do
for y in 0...GRID_HEIGHT do
isCellOn = rand(2) == 1
cells.push(isCellOn)
nextGenCells.push(isCellOn)
color = isCellOn ? "white" : "black"
allSquares.push(Square.new(x: x * SQUARE_SIDE_LENGTH,
y: y * SQUARE_SIDE_LENGTH,
size: SQUARE_SIDE_LENGTH,
color: color))
end
end
update do
for i in 0...cells.length do
x, y = indexToXY(i)
numberOfOnNeighbours = 0
for j in (x - 1)..(x + 1) do
for k in (y - 1)..(y + 1) do
if j >= 0 && j < WIDTH && k >= 0 && k < HEIGHT
if j != x || k != y
if cells[(k * GRID_WIDTH) + j]
numberOfOnNeighbours += 1
end
end
end
end
end
if numberOfOnNeighbours < 2 || numberOfOnNeighbours > 3
nextGenCells[i] = false
elsif numberOfOnNeighbours == 3
nextGenCells[i] = true
else
nextGenCells[i] = cells[i]
end
end
cells = nextGenCells.clone
for i in 0..(cells.length - 1) do
allSquares[i].color = cells[i] ? "white" : "black"
end
end
def indexToXY(index)
x = index % GRID_WIDTH
y = index / GRID_WIDTH
return [x, y]
end
show
Back to Home