diff options
Diffstat (limited to 'wasm-game-of-life')
-rw-r--r-- | wasm-game-of-life/.gitignore | 7 | ||||
-rw-r--r-- | wasm-game-of-life/Cargo.toml | 37 | ||||
-rw-r--r-- | wasm-game-of-life/src/lib.rs | 132 | ||||
-rw-r--r-- | wasm-game-of-life/src/utils.rs | 10 | ||||
-rw-r--r-- | wasm-game-of-life/tests/web.rs | 13 |
5 files changed, 199 insertions, 0 deletions
diff --git a/wasm-game-of-life/.gitignore b/wasm-game-of-life/.gitignore new file mode 100644 index 0000000..c50a344 --- /dev/null +++ b/wasm-game-of-life/.gitignore @@ -0,0 +1,7 @@ +/target +**/*.rs.bk +Cargo.lock +bin/ +pkg/ +wasm-pack.log +www/ diff --git a/wasm-game-of-life/Cargo.toml b/wasm-game-of-life/Cargo.toml new file mode 100644 index 0000000..8fc6c9a --- /dev/null +++ b/wasm-game-of-life/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "wasm-game-of-life" +version = "0.1.0" +authors = ["Jordan Gong <jordan.gong@protonmail.com>"] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +wasm-bindgen = "0.2.63" + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.6", optional = true } + +# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size +# compared to the default allocator's ~10K. It is slower than the default +# allocator, however. +# +# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. +wee_alloc = { version = "0.4.5", optional = true } + +[dev-dependencies] +wasm-bindgen-test = "0.3.13" + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" + +[package.metadata.wasm-pack.profile.release] +wasm-opt = ["-Oz", "--enable-mutable-globals"]
\ No newline at end of file diff --git a/wasm-game-of-life/src/lib.rs b/wasm-game-of-life/src/lib.rs new file mode 100644 index 0000000..fe82d2c --- /dev/null +++ b/wasm-game-of-life/src/lib.rs @@ -0,0 +1,132 @@ +mod utils; + +use wasm_bindgen::prelude::*; +use wasm_bindgen::__rt::core::fmt::Alignment::Center; +use wasm_bindgen::__rt::core::fmt::Formatter; + +// 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; + +fn main() { + #[wasm_bindgen] + #[repr(u8)] + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum Cell { + Dead = 0, + Alive = 1, + } + + #[wasm_bindgen] + pub struct Universe { + width: u32, + height: u32, + cells: Vec<Cell>, + } + + #[wasm_bindgen] + impl Universe { + fn get_index(&self, row: u32, column: u32) -> usize { + (row * self.width + column) as usize + } + + 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_column in [self.width - 1, 0, 1].iter().cloned() { + if delta_row == 0 && delta_column == 0 { + continue; + } + + let neighbor_row = (row + delta_row) % self.height; + let neighbor_column = (column + delta_column) % self.width; + let idx = self.get_index(neighbor_row, neighbor_column); + count += self.cells[idx] as u8; + } + } + count + } + pub fn new() -> Universe { + let width = 64; + let height = 64; + + let cells = (0..width * height) + .map(|x| { + if x % 2 == 0 || x % 7 == 0 { + Cell::Alive + } else { + Cell::Dead + } + }) + .collect(); + + Universe { + width, + height, + cells, + } + } + + pub fn render(&self) -> String { + self.to_string() + } + + 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); + + let next_cell = match (cell, live_neighbors) { + // 1. Underpopulation + (Cell::Alive, x) if x < 2 => Cell::Dead, + // 2. OK + (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive, + // 3. Overpopulation + (Cell::Alive, x) if x > 3 => Cell::Dead, + // 4. Reproduction + (Cell::Dead, 3) => Cell::Alive, + (otherwise, _) => otherwise, + }; + + next[idx] = next_cell; + } + } + + self.cells = next; + } + + pub fn width(&self) -> u32 { + self.width + } + + pub fn height(&self) -> u32 { + self.height + } + + pub fn cells(&self) -> *const Cell { + self.cells.as_ptr() + } + } + + 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(()) + } + } +} diff --git a/wasm-game-of-life/src/utils.rs b/wasm-game-of-life/src/utils.rs new file mode 100644 index 0000000..b1d7929 --- /dev/null +++ b/wasm-game-of-life/src/utils.rs @@ -0,0 +1,10 @@ +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} diff --git a/wasm-game-of-life/tests/web.rs b/wasm-game-of-life/tests/web.rs new file mode 100644 index 0000000..de5c1da --- /dev/null +++ b/wasm-game-of-life/tests/web.rs @@ -0,0 +1,13 @@ +//! Test suite for the Web and headless browsers. + +#![cfg(target_arch = "wasm32")] + +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn pass() { + assert_eq!(1 + 1, 2); +} |