mod utils; extern crate js_sys; use wasm_bindgen::prelude::*; // 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] // Alert export to JS extern { pub fn alert(s: &str); } // Universe is represented as a linear Array of Cells // If we want to access row 5 column 2 we apply : // cell_pos = 5(row) * width(universe) + 2(column) #[wasm_bindgen] // Export to JS #[repr(u8)] // each cell is represented as a single byte #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Cell { Dead = 0, Alive = 1, } #[wasm_bindgen] // Export to JS pub struct Universe { width: u32, height: u32, cells: Vec, } // Not exported to JS impl Universe { fn live_neighbor_count(&self, row: u32, column: u32) -> u8 { 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.get_index(neighbor_row, neighbor_col); count += self.cells[idx] as u8; } } count } } use std::fmt; impl fmt::Display for Universe { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for line in self.cells.as_slice().chunks(self.width as usize) { for &cell in line { let symbol = if cell == Cell::Dead { '◻' } else { '◼' }; write!(f, "{}", symbol)?; } write!(f, "\n")?; } Ok(()) } } // Exported to JS #[wasm_bindgen] impl Universe { pub fn get_index(&self, row: u32, column: u32) -> usize { (row * self.width + column) as usize } pub fn width(&self) -> u32 { self.width } pub fn height(&self) -> u32 { self.height } // Return mutable pointer to Cell pub fn cells(&mut self) -> *mut Cell { self.cells.as_mut_ptr() } // Will be called by JS pub fn tick(&mut self) { let mut next = self.cells.clone(); for row in 0..self.height { for col in 0..self.width { let idx = self.get_index(row, col); let cell = self.cells[idx]; let live_neighbors = self.live_neighbor_count(row, col); // BM: Match + tuple c'est cool let next_cell = match (cell, live_neighbors) { // Rule 1: Any live cell with fewer than two live neighbours // dies, as if caused by underpopulation. (Cell::Alive, x) if x < 2 => Cell::Dead, // Rule 2: Any live cell with two or three live neighbours // lives on to the next generation. (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive, // Rule 3: Any live cell with more than three live // neighbours dies, as if by overpopulation. (Cell::Alive, x) if x > 3 => Cell::Dead, // Rule 4: Any dead cell with exactly three live neighbours // becomes a live cell, as if by reproduction. (Cell::Dead, 3) => Cell::Alive, // All other cells remain in the same state. (otherwise, _) => otherwise, }; next[idx] = next_cell; } } self.cells = next; } pub fn new(w: u32, h: u32) -> Universe { let width = w; let height = h; let cells = (0..width * height) .map(|_| { if js_sys::Math::random() < 0.5 { Cell::Alive } else { Cell::Dead } }) .collect(); Universe { width, height, cells, } } pub fn render(&self) -> String { self.to_string() } }