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}