initial release 1.0.0

This commit is contained in:
2024-07-25 23:42:48 +02:00
parent fef486043f
commit 3e3008f843
9 changed files with 333 additions and 59 deletions

0
.gitignore vendored Normal file → Executable file
View File

182
Cargo.lock generated
View File

@@ -3,10 +3,59 @@
version = 3
[[package]]
name = "bitflags"
version = "2.5.0"
name = "anstream"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "cfg-if"
@@ -20,6 +69,46 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "clap"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "clearscreen"
version = "3.0.0"
@@ -33,6 +122,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "colorchoice"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "dirs"
version = "4.0.0"
@@ -55,9 +150,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "errno"
@@ -77,8 +172,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "game-of-life"
version = "0.1.0"
version = "1.0.0"
dependencies = [
"clap",
"clearscreen",
"rand",
]
@@ -94,6 +190,12 @@ dependencies = [
"wasi",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "home"
version = "0.5.9"
@@ -103,6 +205,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "libc"
version = "0.2.155"
@@ -282,10 +390,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "syn"
version = "2.0.68"
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
@@ -307,18 +421,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
@@ -331,6 +445,12 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -382,9 +502,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -398,51 +518,51 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winsafe"

View File

@@ -1,8 +1,17 @@
[package]
name = "game-of-life"
version = "0.1.0"
version = "1.0.0"
authors = ["Michał Czyż <mike@c2yz.com>"]
edition = "2021"
description = "Rust implementation of the Game of Life"
homepage = "https://github.com/eRgo35/game-of-life"
repository = "https://github.com/eRgo35/game-of-life"
documentation = "https://github.com/eRgo35/game-of-life"
license = "MIT"
readme = "README.md"
[dependencies]
clap = { version = "4.5.10", features = ["derive"] }
clearscreen = "3.0.0"
rand = "0.8.5"

0
README.md Normal file → Executable file
View File

5
examples/custom_game.sav Normal file
View File

@@ -0,0 +1,5 @@
X X X
X X
X X X
X X
X X X

25
examples/random_game.sav Normal file
View File

@@ -0,0 +1,25 @@
XX X X XX X
XX X XX XXX X X XX XXX
X XXXXXX XXXX XXXX XXX
XXX XXX XXX XX X
X XXX X XX X XXXX XX X
XXXXXX XX X X X X
XX XXXX XXX X X X
X XXX XX X XXX XXX
XX XXX XX X X XXXXXX
XX X XXX XX XXXX XXX X
XXXX XXXX XXX XX X XX
XX XXX X X X XX
XX X XXXXXXXX X X X
X X XXXXX X XXX X
XX XX X X X XX X
XX XXX X X XX X X
XXX XXXX X XXX X X
X XXX XX X X X
X X X XX XX X X X
XXX X XX XXX XXXXXXXX
XXXX XX X X X X XX X
X XX XX X XXX X X XXX
XX X X X X XX XXXXXXXX
XXX X X X X X X
XXX XXX X XXX X X

41
src/args.rs Normal file
View File

@@ -0,0 +1,41 @@
use clap::Parser;
#[derive(Parser)]
#[command(
name = "Game of Life",
author = "Michał Czyż",
version = "1.0.0",
about = "Rust implementation of Conway's Game of Life",
long_about = None
)]
pub struct Args {
#[arg(long, default_value_t = 25)]
pub width: usize,
#[arg(long, default_value_t = 25)]
pub height: usize,
#[arg(short, long, default_value_t = 0.5)]
pub probability: f64,
#[arg(short, long, default_value_t = 0)]
pub seed: u64,
#[arg(short, long, default_value_t = 15)]
pub tickrate: u64,
#[arg(long, default_value_t = String::from(""))]
pub load: String,
#[arg(long, default_value_t = String::from(""))]
pub save: String,
#[arg(short, long, default_value_t = 3)]
pub repopulation: usize,
#[arg(short, long, default_value_t = 2)]
pub overpopulation: usize,
#[arg(short, long, default_value_t = 3)]
pub underpopulation: usize,
}

103
src/board.rs Normal file → Executable file
View File

@@ -1,4 +1,6 @@
use rand::Rng;
use std::{io::{Read, Write}, path::PathBuf};
use rand::{Rng, SeedableRng};
#[derive(Clone, PartialEq)]
enum State {
@@ -18,18 +20,16 @@ pub struct Board {
}
impl Board {
// fn dead_state() -> State {
// State::Dead
// }
fn random_state(probability: f64, seed: u64, row: usize, col: usize) -> State {
let mut rng: rand::rngs::StdRng;
// fn alive_state() -> State {
// State::Alive
// }
if seed == 0 {
rng = rand::rngs::StdRng::from_entropy();
} else {
rng = rand::rngs::StdRng::seed_from_u64(seed * row as u64 + col as u64);
}
fn random_state() -> State {
let mut rng = rand::thread_rng();
match rng.gen_bool(0.5) {
match rng.gen_bool(probability) {
true => State::Alive,
false => State::Dead,
}
@@ -63,13 +63,13 @@ impl Board {
alive_neightbors
}
fn generate_cells(width: usize, height: usize, state: &dyn Fn() -> State) -> Vec<Vec<Cell>> {
fn generate_cells(width: usize, height: usize, probability: f64, seed: u64) -> Vec<Vec<Cell>> {
let mut cells = Vec::new();
for _ in 0..height {
for i in 0..height {
let mut row = Vec::new();
for _ in 0..width {
row.push(Cell { state: state() });
for j in 0..width {
row.push(Cell { state: Self::random_state(probability, seed, i, j) });
}
cells.push(row);
}
@@ -77,8 +77,8 @@ impl Board {
cells
}
pub fn init(width: usize, height: usize) -> Self {
let cells = Self::generate_cells(width, height, &Self::random_state);
pub fn init(width: usize, height: usize, probability: f64, seed: u64) -> Self {
let cells = Self::generate_cells(width, height, probability, seed);
Self {
cells,
@@ -87,6 +87,65 @@ impl Board {
}
}
pub fn load(path: String) -> Self {
let path = PathBuf::from(path);
let mut file = match std::fs::File::open(path) {
Ok(file) => file,
Err(err) => panic!("{}", err),
};
let mut contents = String::new();
if let Err(err) = file.read_to_string(&mut contents) {
panic!("{}", err);
}
let width = contents.lines().next().unwrap().len();
let height = contents.lines().count();
let mut cells = vec![vec![Cell { state: State::Dead }; width]; height];
println!("{}x{}", width, height);
for (i, line) in contents.lines().enumerate() {
for (j, c) in line.chars().enumerate() {
match c {
'X' => cells[i][j].state = State::Alive,
' ' => cells[i][j].state = State::Dead,
_ => panic!("Invalid character in file: {}. Use X to mark alive cells and space to mark dead cells.", c),
}
}
}
Self {
cells,
width,
height,
}
}
pub fn save(&self, path: String) {
let path = PathBuf::from(path);
let mut file = match std::fs::File::create(path) {
Ok(file) => file,
Err(err) => panic!("{}", err),
};
let mut contents = String::new();
for row in &self.cells {
for cell in row {
match cell.state {
State::Alive => contents.push('X'),
State::Dead => contents.push(' '),
}
}
contents.push('\n');
}
if let Err(err) = file.write(contents.as_bytes()) {
panic!("{}", err);
}
}
pub fn render(&self) {
for row in &self.cells {
for cell in row {
@@ -99,7 +158,7 @@ impl Board {
}
}
pub fn next_generation(&mut self) {
pub fn next_generation(&mut self, overpopulation: usize, underpopulation: usize, repopulation: usize) {
let width = self.width;
let height = self.height;
let cells = self.cells.clone();
@@ -111,18 +170,17 @@ impl Board {
let current_cell = &cells[i][j];
let alive_neightbors = Self::count_alive_neightbors(&cells, i, j);
// Main Gmae Logic
// TODO: Optimize and remove magic numbers
// Main Game of Life Logic
match current_cell.state {
State::Alive => {
// Any live cell with less than two or more than three live neighbours survives.
if !(2..=3).contains(&alive_neightbors) {
if !(underpopulation..=overpopulation).contains(&alive_neightbors) {
new_cells[i][j].state = State::Dead;
}
},
State::Dead => {
// Any dead cell with three live neighbours becomes a live cell.
if alive_neightbors == 3 {
if alive_neightbors == repopulation {
new_cells[i][j].state = State::Alive;
}
},
@@ -134,3 +192,4 @@ impl Board {
}
}

25
src/main.rs Normal file → Executable file
View File

@@ -1,15 +1,30 @@
mod args;
mod board;
use args::Args;
use board::Board;
use clap::Parser;
use std::{thread, time};
fn main() {
let one_second = time::Duration::from_millis(1000);
let mut board = Board::init(32, 32);
let args = Args::parse();
let mut board: Board;
if !args.load.is_empty() {
board = Board::load(args.load);
} else {
board = Board::init(args.width, args.height, args.probability, args.seed);
}
if !args.save.is_empty() {
board.save(args.save);
}
loop {
board.render();
thread::sleep(one_second/15);
board.next_generation();
clearscreen::clear().expect("Failed to clear screen");
board.render();
thread::sleep(time::Duration::from_millis(1000 / args.tickrate));
board.next_generation(args.overpopulation, args.underpopulation, args.repopulation);
}
}