(This post was generated by an LLM with direction from a human.)
This project has been updated!
Today, I’m excited to share a simple Rust implementation of Conway’s Game of Life, which I’ve been working on. You can check out the full code on my GitHub repository.
Prerequisites
Before diving in, ensure Rust is installed on your system. If not, head over to rust-lang.org and follow the installation instructions.
Cloning the Repository
Start by cloning the repository to your local machine:
git clone https://github.com/drewwalton19216801/gameoflife
cd gameoflife
Running the Game
Execute the game with:
cargo run
Project Structure
Cargo.toml
The Cargo.toml
file specifies the dependencies for the project:
[dependencies]
rand = "0.8.5"
termsize = "0.1.8"
Source Code
The main logic is contained within the src
directory. Let’s break down some key parts of the implementation.
main.rs
This file is the entry point of the program. It initializes the game grid and controls the game loop.
Grid Initialization
A random grid is generated using the rand
crate:
// Create a random number generator.
let mut rng = rand::thread_rng();
// Create a 2D vector with the correct dimensions
// and initialize all cells to `false`.
let mut grid = vec![vec![false; size.cols as usize]; size.rows as usize];
// Set randomly generated live cells in the grid.
for i in 0..size.cols as usize {
for j in 0..size.rows as usize {
grid[j][i] = rng.gen_bool(0.5);
}
}
Game Loop
The game loop is responsible for updating the grid based on Conway’s rules:
loop {
// Clear the screen before displaying the next frame.
print!("\x1B[2J\x1B[1;1H");
// Display the current state of the grid to the console.
display_grid(&grid);
// Update the grid by applying the Game of Life rules.
grid = update_grid(&mut grid, &console_size);
// Sleep for a short duration to control the speed of the simulation.
thread::sleep(Duration::from_millis(100));
}
Grid Update Logic
The update_grid
function calculates the next state for each cell:
for i in 0..size.rows {
for j in 0..size.cols {
// Calculate the number of live neighbors of the cell.
let live_neighbors = live_neighbors(grid, j, i);
// Apply the Game of Life rules to determine the next state of the cell.
if grid[i][j] {
// If the cell is alive:
// - If it has 2 or 3 live neighbors, it remains alive.
// - Otherwise, it dies.
new_grid[i][j] = live_neighbors == 2 || live_neighbors == 3;
} else {
// If the cell is dead:
// - If it has exactly 3 live neighbors, it becomes alive.
// - Otherwise, it remains dead.
new_grid[i][j] = live_neighbors == 3;
}
}
}
Counting Live Neighbors
The live_neighbors
function is crucial for implementing the rules of the Game of Life. It counts the number of live neighbors around a given cell:
for i in -1..=1 {
for j in -1..=1 {
// Skip the cell itself.
if i == 0 && j == 0 {
continue;
}
// Check if the neighbor is within the grid bounds.
if let Some(&cell) = grid.get((y as i32 + i) as usize)
.and_then(|row| row.get((x as i32 + j) as usize))
{
// Increment the count if the neighbor is live.
count += cell as usize;
}
}
}
Displaying the Grid
The grid is printed to the terminal using the termsize
crate to fit the terminal size dynamically.
Conclusion
Implementing Conway’s Game of Life in Rust was a fun and educational experience. Rust’s performance and safety make it an excellent choice for this kind of simulation. I hope you find this project as enjoyable to explore and extend as I did. Feel free to visit the GitHub repository for the full source code and further details.
Happy coding!
Pingback: Implementing Conway's Game of Life in Rust: A Deep Dive Update - [email protected]