Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- mod utils;
- // extern crate js_sys;
- // extern crate web_sys;
- use wasm_bindgen::prelude::*;
- use std::fmt;
- use wasm_bindgen::__rt::core::fmt::Formatter;
- use web_sys;
- use web_sys::console;
- macro_rules! log {
- ($( $t:tt)* ) => {
- web_sys::console::log_1(&format!($($t)*).into());
- };
- }
- pub struct Timer<'a> {
- name: &'a str,
- }
- impl <'a> Timer<'a> {
- pub fn new(name: &'a str) -> Timer<'a> {
- console::time_with_label(name);
- Timer { name }
- }
- }
- impl <'a> Drop for Timer<'a> {
- fn drop(&mut self) {
- console::time_end_with_label(self.name);
- }
- }
- fn now() -> f64 {
- web_sys::window()
- .expect("should have a Window")
- .performance()
- .expect("should have a Performance")
- .now()
- }
- // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
- // allocator.
- #[cfg(feature = "wee_alloc")]
- #[global_allocator]
- static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
- #[wasm_bindgen]
- #[repr(u8)]
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- pub enum Cell {
- Dead = 0,
- Alive = 1,
- }
- pub enum BufActive {
- One,
- Two
- }
- pub struct DoubleBuffer {
- cells_buf_one: Vec<Cell>,
- cells_buf_two: Vec<Cell>,
- active_buf: BufActive
- }
- pub struct Dimension {
- width: u32,
- height: u32
- }
- #[wasm_bindgen]
- pub struct Universe {
- dimension : Dimension,
- buffer: DoubleBuffer
- }
- impl DoubleBuffer {
- pub fn new(cells: Vec<Cell>) -> Self {
- DoubleBuffer {
- cells_buf_one : Clone::clone(&cells),
- cells_buf_two : cells,
- active_buf : BufActive::One
- }
- }
- pub fn switch_active_buffer(&mut self) {
- self.active_buf = match &self.active_buf {
- One => BufActive::Two,
- Two => BufActive::One
- }
- }
- pub fn get_write_buf_mut(&mut self) -> & mut Vec<Cell> {
- match &self.active_buf {
- One => &mut self.cells_buf_one,
- Two => &mut self.cells_buf_two
- }
- }
- pub fn get_read_buf(& self) -> & Vec<Cell> {
- match &self.active_buf {
- One => & self.cells_buf_one,
- Two => & self.cells_buf_two
- }
- }
- pub fn get_buf_mut(&mut self) -> (& Vec<Cell>, &mut Vec<Cell>) {
- match &self.active_buf {
- One => (& self.cells_buf_two, &mut self.cells_buf_one),
- Two => (& self.cells_buf_one, &mut self.cells_buf_two)
- }
- }
- pub fn init(&mut self, cells: Vec<Cell>) {
- self.cells_buf_one = Clone::clone(&cells);
- self.cells_buf_two = cells;
- }
- }
- impl Dimension {
- fn get_index(&self, row: u32, column: u32) -> usize {
- (row * self.width + column) as usize
- }
- }
- impl Cell {
- fn toggle(&mut self) {
- *self = match self {
- Cell::Dead => Cell::Alive,
- Cell::Alive => Cell::Dead
- }
- }
- }
- /// Public methods, exported to JavaScript.
- #[wasm_bindgen]
- impl Universe {
- pub fn toggle_cell(&mut self, row: u32, column: u32) {
- let idx = self.dimension.get_index(row, column);
- let writeBuf = self.buffer.get_write_buf_mut();
- writeBuf[idx].toggle();
- }
- pub fn width(&self) -> u32 {
- self.dimension.width
- }
- pub fn height(&self) -> u32 {
- self.dimension.height
- }
- pub fn cells(&self) -> *const Cell {
- self.buffer.get_read_buf().as_ptr()
- }
- pub fn set_height(&mut self, height: u32) {
- self.dimension.height = height;
- self.initCells();
- }
- pub fn set_width(&mut self, width: u32) {
- self.dimension.width = width;
- self.initCells();
- }
- fn initCells(&mut self) {
- self.buffer.init(
- (0..self.dimension.width * self.dimension.height)
- .map(|_i| Cell::Dead)
- .collect());
- }
- pub fn new() -> Universe {
- utils::set_panic_hook();
- // panic!("Attention , you life is broken beyond repair!");
- let width = 256;
- let height = 128;
- let cells = (0..width * height).map(|i| {
- if js_sys::Math::random() < 0.3 || i%2 == 0 {
- Cell::Alive
- } else {
- Cell::Dead
- }
- }).collect();
- Universe {
- dimension: Dimension { height, width },
- buffer: DoubleBuffer::new(cells)
- }
- }
- pub fn init_random(&mut self) {
- let cells = (0..self.dimension.width * self.dimension.height).map(|i| {
- if js_sys::Math::random() < 0.5 {
- Cell::Alive
- } else {
- Cell::Dead
- }
- }).collect();
- self.buffer.init(cells);
- }
- pub fn init_clear(&mut self) {
- let cells = (0..self.dimension.width * self.dimension.height).map(|i| { Cell::Dead }).collect();
- self.buffer.init(cells);
- }
- pub fn render(&self) -> String {
- self.to_string()
- }
- fn live_neighbor_count(dim: &Dimension, buf: &Vec<Cell>, row: u32, column: u32) -> u8 {
- // less efficient algorithm
- // let mut count = 0;
- //
- // for delta_row in [self.height -1, 0, 1].iter().cloned() {
- // for delta_col in [self.width -1, 0, 1].iter().cloned() {
- //
- // if delta_row == 0 && delta_col == 0 {
- // continue;
- // }
- //
- // let neighbor_row = (row + delta_row) % self.height;
- // let neighbor_col = (column + delta_col) % self.width;
- // let idx = self.dimension.get_index(neighbor_row, neighbor_col);
- // count += self.cells[idx] as u8;
- // }
- // }
- // count
- let mut count = 0;
- let north = if row == 0 {
- dim.height - 1
- } else {
- row - 1
- };
- let south = if row == dim.height - 1 {
- 0
- } else {
- row + 1
- };
- let west = if column == 0 {
- dim.width - 1
- } else {
- column - 1
- };
- let east = if column == dim.width - 1 {
- 0
- } else {
- column + 1
- };
- let nw = dim.get_index(north, west);
- count += buf[nw] as u8;
- let n = dim.get_index(north, column);
- count += buf[n] as u8;
- let ne = dim.get_index(north, east);
- count += buf[ne] as u8;
- let w = dim.get_index(row, west);
- count += buf[w] as u8;
- let e = dim.get_index(row, east);
- count += buf[e] as u8;
- let sw = dim.get_index(south, west);
- count += buf[sw] as u8;
- let s = dim.get_index(south, column);
- count += buf[s] as u8;
- let se = dim.get_index(south, east);
- count += buf[se] as u8;
- count
- }
- pub fn tick(&mut self) {
- let _timer = Timer::new("Universe::tick");
- let (readBuf,writeBuf) = self.buffer.get_buf_mut();
- {
- let _timer = Timer::new("new generation");
- for row in 0..self.dimension.height {
- for col in 0..self.dimension.width {
- let idx = self.dimension.get_index(row, col);
- let cell = readBuf[idx];
- let live_neighbors = Universe::live_neighbor_count(&self.dimension, readBuf, row, col);
- //
- // log!("cell[{}, {}] is initially {:?} and has {} live neighbors",
- // row, col, cell, live_neighbors);
- let next_cell = match (cell, live_neighbors) {
- (Cell::Alive, x) if x < 2 => Cell::Dead,
- (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
- (Cell::Alive, x) => Cell::Dead,
- (Cell::Dead, 3) => Cell::Alive,
- (otherwise,_) => otherwise,
- };
- writeBuf[idx] = next_cell;
- }
- }
- }
- let _timer = Timer::new("free old cells");
- self.buffer.switch_active_buffer();
- }
- }
- impl Universe {
- /// Get the dead and alive values of the entire universe.
- pub fn get_cells(& self) -> &[Cell] {
- self.buffer.get_read_buf()
- }
- /// Set cells to be alive in a universe by passing the row and column
- /// of each cell as an array
- pub fn set_cells(&mut self, cells: &[(u32,u32)]) {
- let writebuf = self.buffer.get_write_buf_mut();
- for (row, col) in cells.iter().cloned() {
- let idx = self.dimension.get_index(row, col);
- writebuf[idx] = Cell::Alive;
- }
- self.buffer.switch_active_buffer();
- }
- }
- impl fmt::Display for Universe {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- let readBuf = self.buffer.get_read_buf();
- for line in readBuf.as_slice().chunks(self.dimension.width as usize) {
- for &cell in line {
- let symbol = if cell == Cell::Dead { '◻' } else { '◼' };
- write!(f, "{}", symbol)?;
- }
- write!(f, "\n")?;
- }
- Ok(())
- }
- }
Add Comment
Please, Sign In to add comment