Compare commits
5 Commits
d8153408e1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87998aed6b | ||
|
|
dd557aa679 | ||
|
|
54ca7f797f | ||
|
|
3a288d27a5 | ||
|
|
7bec5e58fa |
@@ -6,7 +6,10 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# add https://github.com/Magnus167/rustframe as a dependency
|
# add https://github.com/Magnus167/rustframe as a dependency
|
||||||
# rustframe = { git = "https://github.com/Magnus167/rustframe.git" , rev = "refs/pull/19/head" }
|
# rustframe = { git = "https://github.com/Magnus167/rustframe.git" , rev = "refs/pull/19/head" }
|
||||||
rustframe = { git = "https://gitea.nulltech.uk/Magnus167/rustframe.git" , branch = "matr_eq" }
|
# rustframe = { git = "https://gitea.nulltech.uk/Magnus167/rustframe.git" , branch = "matr_eq" }
|
||||||
|
# ~/Code/rustframe
|
||||||
|
rustframe = { path = "/home/palash/Code/rustframe" }
|
||||||
|
|
||||||
|
|
||||||
chrono = { version = "*" }
|
chrono = { version = "*" }
|
||||||
rand = { version = "*" }
|
rand = { version = "*" }
|
||||||
468
src/gol.rs
Normal file
468
src/gol.rs
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
// src/game.rs
|
||||||
|
|
||||||
|
use rustframe::matrix::{BoolMatrix, BoolOps, IntMatrix, Matrix}; // Matrix for ::from_vec
|
||||||
|
|
||||||
|
/// Helper function to create a shifted version of the game board.
|
||||||
|
/// (Using the version provided by the user)
|
||||||
|
///
|
||||||
|
/// - `game`: The current state of the Game of Life as a `BoolMatrix`.
|
||||||
|
/// - `dr`: The row shift (delta row). Positive shifts down, negative shifts up.
|
||||||
|
/// - `dc`: The column shift (delta column). Positive shifts right, negative shifts left.
|
||||||
|
///
|
||||||
|
/// Returns an `IntMatrix` of the same dimensions as `game`.
|
||||||
|
/// - Cells in the shifted matrix get value `1` if the corresponding source cell in `game` was `true` (alive).
|
||||||
|
/// - Cells that would source from outside `game`'s bounds (due to the shift) get value `0`.
|
||||||
|
fn get_shifted_neighbor_layer(game: &BoolMatrix, dr: isize, dc: isize) -> IntMatrix {
|
||||||
|
let rows = game.rows();
|
||||||
|
let cols = game.cols();
|
||||||
|
|
||||||
|
if rows == 0 || cols == 0 {
|
||||||
|
// Handle 0x0 case, other 0-dim cases panic in Matrix::from_vec
|
||||||
|
return IntMatrix::from_vec(vec![], 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize with a matrix of 0s using from_vec.
|
||||||
|
// This demonstrates creating an IntMatrix and then populating it.
|
||||||
|
let mut shifted_layer = IntMatrix::from_vec(vec![0i32; rows * cols], rows, cols);
|
||||||
|
|
||||||
|
for r_target in 0..rows {
|
||||||
|
// Iterate over cells in the *new* (target) shifted matrix
|
||||||
|
for c_target in 0..cols {
|
||||||
|
// Calculate where this target cell would have come from in the *original* game matrix
|
||||||
|
let r_source = r_target as isize - dr;
|
||||||
|
let c_source = c_target as isize - dc;
|
||||||
|
|
||||||
|
// Check if the source coordinates are within the bounds of the original game matrix
|
||||||
|
if r_source >= 0
|
||||||
|
&& r_source < rows as isize
|
||||||
|
&& c_source >= 0
|
||||||
|
&& c_source < cols as isize
|
||||||
|
{
|
||||||
|
// If the source cell in the original game was alive...
|
||||||
|
if game[(r_source as usize, c_source as usize)] {
|
||||||
|
// Demonstrates Index access on BoolMatrix
|
||||||
|
// ...then this cell in the shifted layer is 1.
|
||||||
|
shifted_layer[(r_target, c_target)] = 1; // Demonstrates IndexMut access on IntMatrix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Else (source is out of bounds): it remains 0, as initialized.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shifted_layer // Return the constructed IntMatrix
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the next generation of Conway's Game of Life.
|
||||||
|
///
|
||||||
|
/// This implementation uses a broadcast-like approach by creating shifted layers
|
||||||
|
/// for each neighbor and summing them up, then applying rules element-wise.
|
||||||
|
///
|
||||||
|
/// - `current_game`: A `&BoolMatrix` representing the current state (true=alive).
|
||||||
|
///
|
||||||
|
/// Returns: A new `BoolMatrix` for the next generation.
|
||||||
|
pub fn game_of_life_next_frame(current_game: &BoolMatrix) -> BoolMatrix {
|
||||||
|
let rows = current_game.rows();
|
||||||
|
let cols = current_game.cols();
|
||||||
|
|
||||||
|
if rows == 0 && cols == 0 {
|
||||||
|
return BoolMatrix::from_vec(vec![], 0, 0); // Return an empty BoolMatrix
|
||||||
|
}
|
||||||
|
// Assuming valid non-empty dimensions (e.g., 25x25) as per typical GOL.
|
||||||
|
// Your Matrix::from_vec would panic for other invalid 0-dim cases.
|
||||||
|
|
||||||
|
// Define the 8 neighbor offsets (row_delta, col_delta)
|
||||||
|
let neighbor_offsets: [(isize, isize); 8] = [
|
||||||
|
(-1, -1),
|
||||||
|
(-1, 0),
|
||||||
|
(-1, 1), // Top row (NW, N, NE)
|
||||||
|
(0, -1),
|
||||||
|
(0, 1), // Middle row (W, E)
|
||||||
|
(1, -1),
|
||||||
|
(1, 0),
|
||||||
|
(1, 1), // Bottom row (SW, S, SE)
|
||||||
|
];
|
||||||
|
|
||||||
|
// 1. Initialize `neighbor_counts` with the first shifted layer.
|
||||||
|
// This demonstrates creating an IntMatrix from a function and using it as a base.
|
||||||
|
let (first_dr, first_dc) = neighbor_offsets[0];
|
||||||
|
let mut neighbor_counts = get_shifted_neighbor_layer(current_game, first_dr, first_dc);
|
||||||
|
|
||||||
|
// 2. Add the remaining 7 neighbor layers.
|
||||||
|
// This demonstrates element-wise addition of matrices (`Matrix + Matrix`).
|
||||||
|
for i in 1..neighbor_offsets.len() {
|
||||||
|
let (dr, dc) = neighbor_offsets[i];
|
||||||
|
let next_neighbor_layer = get_shifted_neighbor_layer(current_game, dr, dc);
|
||||||
|
// `neighbor_counts` (owned IntMatrix) + `next_neighbor_layer` (owned IntMatrix)
|
||||||
|
// uses `impl Add for Matrix`, consumes both, returns new owned `IntMatrix`.
|
||||||
|
neighbor_counts = neighbor_counts + next_neighbor_layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Apply Game of Life rules using element-wise operations.
|
||||||
|
|
||||||
|
// Rule: Survival or Birth based on neighbor counts.
|
||||||
|
// A cell is alive in the next generation if:
|
||||||
|
// (it's currently alive AND has 2 or 3 neighbors) OR
|
||||||
|
// (it's currently dead AND has exactly 3 neighbors)
|
||||||
|
|
||||||
|
// `neighbor_counts.eq_elem(scalar)`:
|
||||||
|
// Demonstrates element-wise comparison of a Matrix with a scalar (broadcast).
|
||||||
|
// Returns an owned `BoolMatrix`.
|
||||||
|
let has_2_neighbors = neighbor_counts.eq_elem(2);
|
||||||
|
let has_3_neighbors = neighbor_counts.eq_elem(3); // This will be reused
|
||||||
|
|
||||||
|
// `has_2_neighbors | has_3_neighbors`:
|
||||||
|
// Demonstrates element-wise OR (`Matrix<bool> | Matrix<bool>`).
|
||||||
|
// Consumes both operands, returns an owned `BoolMatrix`.
|
||||||
|
let has_2_or_3_neighbors = has_2_neighbors | has_3_neighbors.clone(); // Clone has_3_neighbors as it's used again
|
||||||
|
|
||||||
|
// `current_game & &has_2_or_3_neighbors`:
|
||||||
|
// `current_game` is `&BoolMatrix`. `has_2_or_3_neighbors` is owned.
|
||||||
|
// Demonstrates element-wise AND (`&Matrix<bool> & &Matrix<bool>`).
|
||||||
|
// Borrows both operands, returns an owned `BoolMatrix`.
|
||||||
|
let survives = current_game & &has_2_or_3_neighbors;
|
||||||
|
|
||||||
|
// `!current_game`:
|
||||||
|
// Demonstrates element-wise NOT (`!&Matrix<bool>`).
|
||||||
|
// Borrows operand, returns an owned `BoolMatrix`.
|
||||||
|
let is_dead = !current_game;
|
||||||
|
|
||||||
|
// `is_dead & &has_3_neighbors`:
|
||||||
|
// `is_dead` is owned. `has_3_neighbors` is owned.
|
||||||
|
// Demonstrates element-wise AND (`Matrix<bool> & &Matrix<bool>`).
|
||||||
|
// Consumes `is_dead`, borrows `has_3_neighbors`, returns an owned `BoolMatrix`.
|
||||||
|
let births = is_dead & &has_3_neighbors;
|
||||||
|
|
||||||
|
// `survives | births`:
|
||||||
|
// Demonstrates element-wise OR (`Matrix<bool> | Matrix<bool>`).
|
||||||
|
// Consumes both operands, returns an owned `BoolMatrix`.
|
||||||
|
let next_frame_game = survives | births;
|
||||||
|
|
||||||
|
next_frame_game
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Tests from previous example (can be kept or adapted) ---
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rustframe::matrix::{BoolMatrix, BoolOps}; // Assuming BoolOps is available for .count()
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blinker_oscillator() {
|
||||||
|
let initial_data = vec![false, true, false, false, true, false, false, true, false];
|
||||||
|
let game1 = BoolMatrix::from_vec(initial_data.clone(), 3, 3);
|
||||||
|
let expected_frame2_data = vec![false, false, false, true, true, true, false, false, false];
|
||||||
|
let expected_game2 = BoolMatrix::from_vec(expected_frame2_data, 3, 3);
|
||||||
|
let game2 = game_of_life_next_frame(&game1);
|
||||||
|
assert_eq!(
|
||||||
|
game2.data(),
|
||||||
|
expected_game2.data(),
|
||||||
|
"Frame 1 to Frame 2 failed for blinker"
|
||||||
|
);
|
||||||
|
let expected_game3 = BoolMatrix::from_vec(initial_data, 3, 3);
|
||||||
|
let game3 = game_of_life_next_frame(&game2);
|
||||||
|
assert_eq!(
|
||||||
|
game3.data(),
|
||||||
|
expected_game3.data(),
|
||||||
|
"Frame 2 to Frame 3 failed for blinker"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_board_remains_empty() {
|
||||||
|
let board_3x3_all_false = BoolMatrix::from_vec(vec![false; 9], 3, 3);
|
||||||
|
let next_frame = game_of_life_next_frame(&board_3x3_all_false);
|
||||||
|
assert_eq!(
|
||||||
|
next_frame.count(),
|
||||||
|
0,
|
||||||
|
"All-false board should result in all-false"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_size_board() {
|
||||||
|
let board_0x0 = BoolMatrix::from_vec(vec![], 0, 0);
|
||||||
|
let next_frame = game_of_life_next_frame(&board_0x0);
|
||||||
|
assert_eq!(next_frame.rows(), 0);
|
||||||
|
assert_eq!(next_frame.cols(), 0);
|
||||||
|
assert!(
|
||||||
|
next_frame.data().is_empty(),
|
||||||
|
"0x0 board should result in 0x0 board"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_still_life_block() {
|
||||||
|
let block_data = vec![
|
||||||
|
true, true, false, false, true, true, false, false, false, false, false, false, false,
|
||||||
|
false, false, false,
|
||||||
|
];
|
||||||
|
let game_block = BoolMatrix::from_vec(block_data.clone(), 4, 4);
|
||||||
|
let next_frame_block = game_of_life_next_frame(&game_block);
|
||||||
|
assert_eq!(
|
||||||
|
next_frame_block.data(),
|
||||||
|
game_block.data(),
|
||||||
|
"Block still life should remain unchanged"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/main.rs
|
||||||
|
|
||||||
|
use rand::{self, Rng};
|
||||||
|
use std::{str::MatchIndices, thread, time};
|
||||||
|
|
||||||
|
const BOARD_ROWS: usize = 100;
|
||||||
|
const BOARD_COLS: usize = 100; // Made wider for better viewing of patterns like gliders
|
||||||
|
const TICK_DURATION_MS: u64 = 10; // Milliseconds per frame
|
||||||
|
|
||||||
|
/// Prints the Game of Life board to the console.
|
||||||
|
///
|
||||||
|
/// - `board`: A reference to the `BoolMatrix` representing the current game state.
|
||||||
|
/// This function demonstrates `board.rows()`, `board.cols()`, and `board[(r, c)]` (Index trait).
|
||||||
|
fn print_board(board: &BoolMatrix) {
|
||||||
|
let mut print_str = String::new();
|
||||||
|
print_str.push_str("+");
|
||||||
|
for _ in 0..board.cols() {
|
||||||
|
print_str.push_str("--");
|
||||||
|
}
|
||||||
|
print_str.push_str("+\n");
|
||||||
|
for r in 0..board.rows() {
|
||||||
|
print_str.push_str("| ");
|
||||||
|
for c in 0..board.cols() {
|
||||||
|
if board[(r, c)] {
|
||||||
|
// Using Index trait for Matrix<bool>
|
||||||
|
print_str.push_str("██");
|
||||||
|
} else {
|
||||||
|
print_str.push_str(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print_str.push_str(" |\n");
|
||||||
|
}
|
||||||
|
print_str.push_str("+");
|
||||||
|
for _ in 0..board.cols() {
|
||||||
|
print_str.push_str("--");
|
||||||
|
}
|
||||||
|
print_str.push_str("+\n\n");
|
||||||
|
print!("{}", print_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_glider(board: &mut BoolMatrix) {
|
||||||
|
// Initialize with a Glider pattern.
|
||||||
|
// It demonstrates how to set specific cells in the matrix.
|
||||||
|
// This demonstrates `IndexMut` for `current_board[(r, c)] = true;`.
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
let r_offset = rng.random_range(0..(BOARD_ROWS - 3));
|
||||||
|
let c_offset = rng.random_range(0..(BOARD_COLS - 3));
|
||||||
|
if board.rows() >= r_offset + 3 && board.cols() >= c_offset + 3 {
|
||||||
|
board[(r_offset + 0, c_offset + 1)] = true;
|
||||||
|
board[(r_offset + 1, c_offset + 2)] = true;
|
||||||
|
board[(r_offset + 2, c_offset + 0)] = true;
|
||||||
|
board[(r_offset + 2, c_offset + 1)] = true;
|
||||||
|
board[(r_offset + 2, c_offset + 2)] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_pulsar(board: &mut BoolMatrix) {
|
||||||
|
// Initialize with a Pulsar pattern.
|
||||||
|
// This demonstrates how to set specific cells in the matrix.
|
||||||
|
// This demonstrates `IndexMut` for `current_board[(r, c)] = true;`.
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
let r_offset = rng.random_range(0..(BOARD_ROWS - 17));
|
||||||
|
let c_offset = rng.random_range(0..(BOARD_COLS - 17));
|
||||||
|
if board.rows() >= r_offset + 17 && board.cols() >= c_offset + 17 {
|
||||||
|
let pulsar_coords = [
|
||||||
|
(2, 4),
|
||||||
|
(2, 5),
|
||||||
|
(2, 6),
|
||||||
|
(2, 10),
|
||||||
|
(2, 11),
|
||||||
|
(2, 12),
|
||||||
|
(4, 2),
|
||||||
|
(4, 7),
|
||||||
|
(4, 9),
|
||||||
|
(4, 14),
|
||||||
|
(5, 2),
|
||||||
|
(5, 7),
|
||||||
|
(5, 9),
|
||||||
|
(5, 14),
|
||||||
|
(6, 2),
|
||||||
|
(6, 7),
|
||||||
|
(6, 9),
|
||||||
|
(6, 14),
|
||||||
|
(7, 4),
|
||||||
|
(7, 5),
|
||||||
|
(7, 6),
|
||||||
|
(7, 10),
|
||||||
|
(7, 11),
|
||||||
|
(7, 12),
|
||||||
|
];
|
||||||
|
for &(dr, dc) in pulsar_coords.iter() {
|
||||||
|
board[(r_offset + dr, c_offset + dc)] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// (2,4),(2,5),(2,6), (2,10),(2,11),(2,12), // row 1
|
||||||
|
// (4,2),(4,7),(4,9),(4,14), // row 2
|
||||||
|
// (5,2),(5,7),(5,9),(5,14), // row 3
|
||||||
|
// (6,2),(6,7),(6,9),(6,14), // row 4
|
||||||
|
// (7,4),(7,5),(7,6), (7,10),(7,11),(7,12), // row 5
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_stable_state(
|
||||||
|
current_board: &BoolMatrix,
|
||||||
|
previous_board_state: &Option<BoolMatrix>,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(ref prev_board) = previous_board_state {
|
||||||
|
// `*prev_board == current_board` demonstrates `PartialEq` for `Matrix`.
|
||||||
|
return *prev_board == *current_board;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_board(board: &BoolMatrix, primes: Vec<i32>) -> usize {
|
||||||
|
let board_ints_vec = board
|
||||||
|
.data()
|
||||||
|
.iter()
|
||||||
|
.map(|&cell| if cell { 1 } else { 0 })
|
||||||
|
.collect::<Vec<i32>>();
|
||||||
|
|
||||||
|
let ints_board = Matrix::from_vec(board_ints_vec, board.rows(), board.cols());
|
||||||
|
|
||||||
|
let primes_board = Matrix::from_vec(primes, ints_board.rows(), ints_board.cols());
|
||||||
|
|
||||||
|
let result = ints_board * primes_board;
|
||||||
|
let result: i32 = result.data().iter().sum();
|
||||||
|
result as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_repeating_state(board_hashes: &mut Vec<usize>) -> bool {
|
||||||
|
// so - detect alternating states. if 0==2, 1==3, 2==4, 3==5, 4==6, 5==7
|
||||||
|
if board_hashes.len() < 4 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut result = false;
|
||||||
|
if (board_hashes[0] == board_hashes[2]) && (board_hashes[1] == board_hashes[3]) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
// remove the 0th item
|
||||||
|
board_hashes.remove(0);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Initialize the game board.
|
||||||
|
// This demonstrates `BoolMatrix::from_vec`.
|
||||||
|
let mut current_board =
|
||||||
|
BoolMatrix::from_vec(vec![false; BOARD_ROWS * BOARD_COLS], BOARD_ROWS, BOARD_COLS);
|
||||||
|
|
||||||
|
let primes = generate_primes((BOARD_ROWS * BOARD_COLS) as i32);
|
||||||
|
|
||||||
|
// let r_offset = 3;
|
||||||
|
// let c_offset = 3;
|
||||||
|
// if BOARD_ROWS >= r_offset + 3 && BOARD_COLS >= c_offset + 3 {
|
||||||
|
// current_board[(r_offset + 0, c_offset + 1)] = true;
|
||||||
|
// current_board[(r_offset + 1, c_offset + 2)] = true;
|
||||||
|
// current_board[(r_offset + 2, c_offset + 0)] = true;
|
||||||
|
// current_board[(r_offset + 2, c_offset + 1)] = true;
|
||||||
|
// current_board[(r_offset + 2, c_offset + 2)] = true;
|
||||||
|
// } else {
|
||||||
|
// println!("Board is too small for the default Glider pattern. Starting empty.");
|
||||||
|
// }
|
||||||
|
// gerate a random glider
|
||||||
|
|
||||||
|
add_simulated_activity(&mut current_board);
|
||||||
|
|
||||||
|
let mut generation_count: u32 = 0;
|
||||||
|
// `previous_board_state` will store a clone of the board.
|
||||||
|
// This demonstrates `Matrix::clone()` and later `PartialEq` for `Matrix`.
|
||||||
|
let mut previous_board_state: Option<BoolMatrix> = None;
|
||||||
|
let mut board_hashes = Vec::new();
|
||||||
|
let mut print_board_bool = true;
|
||||||
|
let mut print_bool_int = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// print!("{}[2J", 27 as char); // Clear screen and move cursor to top-left
|
||||||
|
|
||||||
|
// if print_board_bool {
|
||||||
|
if print_bool_int % 10 == 0 {
|
||||||
|
print!("{}[2J", 27 as char);
|
||||||
|
println!("Conway's Game of Life - Generation: {}", generation_count);
|
||||||
|
|
||||||
|
print_board(¤t_board);
|
||||||
|
println!("Alive cells: {}", ¤t_board.count());
|
||||||
|
|
||||||
|
// print_board_bool = false;
|
||||||
|
print_bool_int = 0;
|
||||||
|
} else {
|
||||||
|
// print_board_bool = true;
|
||||||
|
print_bool_int += 1;
|
||||||
|
}
|
||||||
|
// `current_board.count()` demonstrates a method from `BoolOps`.
|
||||||
|
board_hashes.push(hash_board(¤t_board, primes.clone()));
|
||||||
|
if detect_stable_state(¤t_board, &previous_board_state) {
|
||||||
|
println!(
|
||||||
|
"\nStable state detected at generation {}.",
|
||||||
|
generation_count
|
||||||
|
);
|
||||||
|
add_simulated_activity(&mut current_board);
|
||||||
|
}
|
||||||
|
if detect_repeating_state(&mut board_hashes) {
|
||||||
|
println!(
|
||||||
|
"\nRepeating state detected at generation {}.",
|
||||||
|
generation_count
|
||||||
|
);
|
||||||
|
add_simulated_activity(&mut current_board);
|
||||||
|
}
|
||||||
|
if !¤t_board.any() {
|
||||||
|
println!("\nExtinction at generation {}.", generation_count);
|
||||||
|
add_simulated_activity(&mut current_board);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `current_board.clone()` demonstrates `Clone` for `Matrix`.
|
||||||
|
previous_board_state = Some(current_board.clone());
|
||||||
|
|
||||||
|
// This is the core call to your game logic.
|
||||||
|
let next_board = game_of_life_next_frame(¤t_board);
|
||||||
|
current_board = next_board;
|
||||||
|
|
||||||
|
generation_count += 1;
|
||||||
|
thread::sleep(time::Duration::from_millis(TICK_DURATION_MS));
|
||||||
|
|
||||||
|
// if generation_count > 500 { // Optional limit
|
||||||
|
// println!("\nReached generation limit.");
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_simulated_activity(current_board: &mut Matrix<bool>) {
|
||||||
|
for _ in 0..20 {
|
||||||
|
generate_glider(current_board);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a Pulsar pattern
|
||||||
|
for _ in 0..10 {
|
||||||
|
generate_pulsar(current_board);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate prime numbers
|
||||||
|
fn generate_primes(n: i32) -> Vec<i32> {
|
||||||
|
// I want to generate the first n primes
|
||||||
|
let mut primes = Vec::new();
|
||||||
|
let mut count = 0;
|
||||||
|
let mut num = 2; // Start checking for primes from 2
|
||||||
|
while count < n {
|
||||||
|
let mut is_prime = true;
|
||||||
|
for i in 2..=((num as f64).sqrt() as i32) {
|
||||||
|
if num % i == 0 {
|
||||||
|
is_prime = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_prime {
|
||||||
|
primes.push(num);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
num += 1;
|
||||||
|
}
|
||||||
|
primes
|
||||||
|
}
|
||||||
196
src/main.rs
196
src/main.rs
@@ -1,149 +1,73 @@
|
|||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use rand::Rng; // Import thread_rng for random number generation
|
use rustframe::{
|
||||||
use rustframe::frame::{Frame, RowIndex};
|
frame::{Frame, RowIndex},
|
||||||
use rustframe::matrix::{BoolMatrix, BoolOps, Matrix, SeriesOps}; // Explicitly list used items
|
matrix::{BoolOps, Matrix, SeriesOps},
|
||||||
use rustframe::utils::{BDateFreq, BDatesList};
|
utils::{BDateFreq, BDatesList},
|
||||||
use std::time::Instant; // Use Instant for timing
|
};
|
||||||
|
|
||||||
// Helper function to generate a random f64 between 0.0 and 1.0
|
fn generate_array(n_cols: usize, n_rows: usize) -> Matrix<f64> {
|
||||||
fn generate_random_float() -> f64 {
|
let matrix = Matrix::from_vec(
|
||||||
let mut rng = rand::rng(); // Get the thread-local random number generator
|
(0..n_cols * n_rows).map(|x| x as f64).collect::<Vec<f64>>(),
|
||||||
let uniform = rand::distr::Uniform::new(0.0, 1.0).unwrap(); // Define a uniform distribution range and unwrap the result
|
n_cols,
|
||||||
rng.sample(&uniform) // Sample a value from the distribution
|
n_rows,
|
||||||
|
);
|
||||||
|
matrix
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to generate column labels
|
fn test_a() {
|
||||||
fn generate_column_labels(num_cols: usize) -> Vec<String> {
|
let n_periods = 4;
|
||||||
(0..num_cols).map(|i| format!("col_{}", i)).collect() // Use "col" prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to generate a Frame with random data
|
// Four business days starting 2024‑01‑02
|
||||||
fn generate_random_frame(column_labels: Vec<String>, dates_vec: Vec<NaiveDate>) -> Frame<f64> {
|
let dates: Vec<NaiveDate> =
|
||||||
let num_cols = column_labels.len();
|
BDatesList::from_n_periods("2024-01-02".to_string(), BDateFreq::Daily, n_periods)
|
||||||
let num_rows = dates_vec.len();
|
.unwrap()
|
||||||
|
.list()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut data: Vec<Vec<f64>> = Vec::with_capacity(num_cols);
|
let col_names: Vec<String> = vec!["a".to_string(), "b".to_string()];
|
||||||
for _ in 0..num_cols {
|
|
||||||
let col: Vec<f64> = (0..num_rows).map(|_| generate_random_float()).collect();
|
|
||||||
data.push(col);
|
|
||||||
}
|
|
||||||
|
|
||||||
let matrix = Matrix::from_cols(data); // Create Matrix from columns
|
let ma: Matrix<f64> =
|
||||||
|
Matrix::from_cols(vec![vec![1.0, 2.0, 3.0, 4.0], vec![5.0, 6.0, 7.0, 8.0]]);
|
||||||
|
let mb: Matrix<f64> =
|
||||||
|
Matrix::from_cols(vec![vec![4.0, 3.0, 2.0, 1.0], vec![8.0, 7.0, 6.0, 5.0]]);
|
||||||
|
|
||||||
Frame::new(
|
let fa: Frame<f64> = Frame::new(
|
||||||
matrix,
|
ma.clone(),
|
||||||
column_labels, // Consume the vector of labels
|
col_names.clone(),
|
||||||
Some(RowIndex::Date(dates_vec)), // Consume the vector of dates for the index
|
Some(RowIndex::Date(dates.clone())),
|
||||||
)
|
);
|
||||||
|
let fb: Frame<f64> = Frame::new(mb, col_names, Some(RowIndex::Date(dates)));
|
||||||
|
|
||||||
|
// Math that reads like math
|
||||||
|
let result: Frame<f64> = &fa * &fb; // element‑wise multiply
|
||||||
|
let total: f64 = result.sum_vertical().iter().sum::<f64>();
|
||||||
|
assert_eq!(total, 184.0);
|
||||||
|
|
||||||
|
// broadcast & reduce
|
||||||
|
let result: Matrix<f64> = &ma + 1.0; // add scalar
|
||||||
|
let result: Matrix<f64> = &result - 1.0; // subtract scalar
|
||||||
|
let result: Matrix<f64> = &result * 2.0; // multiply by scalar
|
||||||
|
let result: Matrix<f64> = &result / 2.0; // divide by scalar
|
||||||
|
|
||||||
|
let check: bool = result.eq_elem(ma.clone()).all();
|
||||||
|
assert!(check);
|
||||||
|
|
||||||
|
// The above math can also be written as:
|
||||||
|
let check: bool = (&(&(&(&ma + 1.0) - 1.0) * 2.0) / 2.0).eq_elem(ma).all();
|
||||||
|
assert!(check);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Start overall timing
|
// test with 1000x1000
|
||||||
let total_start_time = Instant::now();
|
let n_cols = 1000;
|
||||||
|
let n_rows = 1000;
|
||||||
|
let ma = generate_array(n_cols, n_rows);
|
||||||
|
// time the operation
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
// let result = Matrix::from_vec((&ma * &ma).sum_vertical(), n_rows, 1).sum_vertical();
|
||||||
|
let result = (&ma * &ma).sum_vertical();
|
||||||
|
let duration = start.elapsed();
|
||||||
|
|
||||||
// --- Configuration ---
|
// println!("Result: {:?}", result);
|
||||||
let start_date_str = "2000-01-01".to_string();
|
println!("Duration: {:?}", duration);
|
||||||
let num_periods = 1_000;
|
|
||||||
let num_columns = 1_000;
|
|
||||||
let frequency = BDateFreq::Daily;
|
|
||||||
|
|
||||||
println!("--- Demo Parameters ---");
|
|
||||||
println!("Start Date: {}", start_date_str);
|
|
||||||
println!("Number of Periods (Dates): {}", num_periods);
|
|
||||||
println!("Number of Columns: {}", num_columns);
|
|
||||||
println!("Frequency: {:?}", frequency);
|
|
||||||
println!("-----------------------");
|
|
||||||
println!("--- Timing Results ---");
|
|
||||||
|
|
||||||
// --- Setup Data (Dates and Labels) ---
|
|
||||||
let setup_data_start_time = Instant::now();
|
|
||||||
|
|
||||||
let dates_list = BDatesList::from_n_periods(start_date_str, frequency, num_periods).unwrap();
|
|
||||||
let dates_vec: Vec<NaiveDate> = dates_list.list().unwrap().to_vec();
|
|
||||||
let column_labels: Vec<String> = generate_column_labels(num_columns);
|
|
||||||
|
|
||||||
let setup_data_duration = setup_data_start_time.elapsed();
|
|
||||||
println!(
|
|
||||||
"Setup data (dates/labels) duration: {:?}",
|
|
||||||
setup_data_duration
|
|
||||||
);
|
|
||||||
|
|
||||||
// --- Frame Creation ---
|
|
||||||
let frame_creation_start_time = Instant::now();
|
|
||||||
|
|
||||||
// Create two frames with the same structure but different random data
|
|
||||||
// Clone labels and dates because generate_random_frame consumes them
|
|
||||||
let frame_a = generate_random_frame(column_labels.clone(), dates_vec.clone());
|
|
||||||
let frame_b = generate_random_frame(column_labels.clone(), dates_vec.clone());
|
|
||||||
|
|
||||||
let frame_creation_duration = frame_creation_start_time.elapsed();
|
|
||||||
println!(
|
|
||||||
"Frame size: dates: {}, cols: {}",
|
|
||||||
frame_a.rows(),
|
|
||||||
frame_a.cols()
|
|
||||||
);
|
|
||||||
println!("Frame creation duration: {:?}", frame_creation_duration);
|
|
||||||
|
|
||||||
// --- Arithmetic Operations and Timing ---
|
|
||||||
|
|
||||||
// Multiplication
|
|
||||||
let mul_start_time = Instant::now();
|
|
||||||
let _result_mul = &frame_a * &frame_b; // Store result, even if unused, as the operation happens
|
|
||||||
let mul_duration = mul_start_time.elapsed();
|
|
||||||
println!("Multiplication duration: {:?}", mul_duration);
|
|
||||||
|
|
||||||
// Addition
|
|
||||||
let add_start_time = Instant::now();
|
|
||||||
let frame_r = &frame_a + &frame_b;
|
|
||||||
let add_duration = add_start_time.elapsed();
|
|
||||||
println!("Addition duration: {:?}", add_duration);
|
|
||||||
|
|
||||||
// Division (using the result of addition)
|
|
||||||
let div_start_time = Instant::now();
|
|
||||||
let frame_r = &frame_r / &frame_b;
|
|
||||||
let div_duration = div_start_time.elapsed();
|
|
||||||
println!("Division duration: {:?}", div_duration);
|
|
||||||
|
|
||||||
// Subtraction (using the result of division)
|
|
||||||
let sub_start_time = Instant::now();
|
|
||||||
let frame_r = &frame_r - &frame_a;
|
|
||||||
let sub_duration = sub_start_time.elapsed();
|
|
||||||
println!("Subtraction duration: {:?}", sub_duration);
|
|
||||||
|
|
||||||
// --- Boolean Operations and Timing ---
|
|
||||||
|
|
||||||
// Element-wise comparison (e.g., less than)
|
|
||||||
let bool_mat_start_time = Instant::now();
|
|
||||||
// Check elements in the subtraction result that are less than a small value
|
|
||||||
let frame_r = frame_r.matrix().lt_elementwise(0.001);
|
|
||||||
let bool_mat_duration = bool_mat_start_time.elapsed();
|
|
||||||
println!("LT operation duration: {:?}", bool_mat_duration);
|
|
||||||
|
|
||||||
// Reduction operation (e.g., check if 'any' element is true)
|
|
||||||
let any_start_time = Instant::now();
|
|
||||||
let any_result = frame_r.any();
|
|
||||||
let any_duration = any_start_time.elapsed();
|
|
||||||
println!("Any operation duration: {:?}", any_duration);
|
|
||||||
println!("Any operation result: {:?}", any_result);
|
|
||||||
|
|
||||||
// Complex operation
|
|
||||||
let complex_start_time = Instant::now();
|
|
||||||
let frame_r = &(&(&(&(&frame_a * &frame_b) / &frame_b) - &frame_a) + &frame_a) - &frame_a;
|
|
||||||
|
|
||||||
let frame_r = frame_r.matrix().lt_elementwise(0.0000001);
|
|
||||||
let complex_result = frame_r.all();
|
|
||||||
let complex_duration = complex_start_time.elapsed();
|
|
||||||
println!("Complex operation duration: {:?}", complex_duration);
|
|
||||||
println!(
|
|
||||||
"Complex operation result (expected true): {:?}",
|
|
||||||
complex_result
|
|
||||||
);
|
|
||||||
println!("-----------------------");
|
|
||||||
|
|
||||||
// End overall timing
|
|
||||||
let total_duration = total_start_time.elapsed();
|
|
||||||
println!(
|
|
||||||
"Total execution duration (including setup and ops): {:?}",
|
|
||||||
total_duration
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user