demo-mrlylife.rs

4.4 kB · rust · 152 lines

1use mrlycore::errors::Result;2use mrlycore::tensor::Tensor;3use mrlycore::MrlyError;4use mrlycore::Rng;5use mrlyfig::out::root;6use mrlyfig::{ink, save, Board};7use mrlymath::life::{design_mask, lattice_index, next_grid, Boundary};8use mrlymath::two::Cell2d;9use std::path::PathBuf;1011const NAME: &str = "demo-mrlylife";12const SIDE: usize = 192;13const GENERATIONS: usize = 64;14const SEED: u64 = 1729;15const DENSITY: f64 = 0.05;16const CELL: f64 = 4.0;17const STAMP: f64 = 8.0;18const MASK: usize = 9;19const SITES: usize = 64;2021// LIFE2223fn soup(seed: u64) -> Cell2d {24    let mut rng = Rng::new(seed);25    let mut types = Tensor::new(vec![SIDE, SIDE]);26    for slot in types.bytes_mut().iter_mut() {27        *slot = u8::from(rng.chance(DENSITY));28    }29    Cell2d::new(types)30}3132// DATA3334fn path() -> PathBuf {35    root()36        .join("files")37        .join("figures")38        .join("data")39        .join(format!("{NAME}.json"))40}4142fn write_data(mask: &[u8], cells: &[u8]) -> Result<PathBuf> {43    let file = path();44    let folder = file.parent().unwrap().to_path_buf();45    std::fs::create_dir_all(&folder)46        .map_err(|e| MrlyError::Value(format!("cannot make {folder:?}: {e}")))?;47    let one: Vec<String> = mask.iter().map(|bit| bit.to_string()).collect();48    let two: Vec<String> = cells.iter().map(|bit| bit.to_string()).collect();49    let text = format!(50        "{{\"mask\":[{}],\"cells\":[{}]}}",51        one.join(","),52        two.join(",")53    );54    std::fs::write(&file, text)55        .map_err(|e| MrlyError::Value(format!("cannot write {file:?}: {e}")))?;56    Ok(file)57}5859fn field(text: &str, name: &str) -> Vec<u8> {60    let head = format!("\"{name}\":[");61    let Some(start) = text.find(&head) else {62        return Vec::new();63    };64    let body = &text[start + head.len()..];65    let end = body.find(']').unwrap_or(0);66    body[..end]67        .split(',')68        .filter_map(|token| token.parse().ok())69        .collect()70}7172fn read_data() -> Result<(Vec<u8>, Vec<u8>)> {73    let file = path();74    let raw = std::fs::read_to_string(&file)75        .map_err(|e| MrlyError::Value(format!("cannot read {file:?}: {e}; run -- compute")))?;76    let text: String = raw.chars().filter(|c| !c.is_whitespace()).collect();77    Ok((field(&text, "mask"), field(&text, "cells")))78}7980// PRESS8182fn compute() -> Result<()> {83    let mask = design_mask(2, 7, 3, 2)?;84    assert_eq!(mask.shape, vec![MASK, MASK]);85    assert_eq!((0..mask.size()).filter(|&i| mask.at(i) == 1).count(), SITES);86    assert_eq!(mask.get(&[MASK / 2, MASK / 2]), 0);87    assert_eq!(lattice_index(&mask), 1);8889    let mut cell = soup(SEED);90    for _ in 0..GENERATIONS {91        cell = next_grid(&cell, &[3], &[2, 3], &mask, Boundary::Wrap)?;92    }93    assert_eq!(cell.width(), SIDE);94    assert_eq!(cell.height(), SIDE);95    let types = cell.types();96    let live = (0..SIDE * SIDE).filter(|&i| types.at(i) != 0).count();97    assert!(live > 0);9899    let stamp: Vec<u8> = (0..MASK * MASK).map(|i| mask.at(i) as u8).collect();100    let board: Vec<u8> = (0..SIDE * SIDE)101        .map(|i| u8::from(types.at(i) != 0))102        .collect();103    let file = write_data(&stamp, &board)?;104    println!("{NAME} {SITES} sites {live} live after {GENERATIONS} -> {file:?}");105    Ok(())106}107108fn draw() -> Result<()> {109    let (mask, cells) = read_data()?;110    assert_eq!(mask.len(), MASK * MASK);111    assert_eq!(cells.len(), SIDE * SIDE);112    assert!(cells.iter().any(|&bit| bit != 0));113114    let mut board = Board::square();115    let area = board.frame(0.08);116    let block = SIDE as f64 * CELL;117    let ox = (area.x + area.w - block).round();118    let oy = (area.y + area.h - block).round();119    for row in 0..SIDE {120        for col in 0..SIDE {121            if cells[row * SIDE + col] != 0 {122                let x = ox + col as f64 * CELL;123                let y = oy + row as f64 * CELL;124                board.rect(x, y, CELL, CELL, ink::blue());125            }126        }127    }128129    let sx = area.x.round();130    let sy = area.y.round();131    let mut stamped = 0usize;132    for row in 0..MASK {133        for col in 0..MASK {134            if mask[row * MASK + col] == 1 {135                let x = sx + col as f64 * STAMP;136                let y = sy + row as f64 * STAMP;137                board.rect(x, y, STAMP, STAMP, ink::yellow());138                stamped += 1;139            }140        }141    }142    assert_eq!(stamped, SITES);143    save(NAME, &board)?;144    Ok(())145}146147fn main() -> Result<()> {148    if std::env::args().nth(1).as_deref() == Some("compute") {149        return compute();150    }151    draw()152}