summaryrefslogtreecommitdiff
path: root/wasm-game-of-life
diff options
context:
space:
mode:
Diffstat (limited to 'wasm-game-of-life')
-rw-r--r--wasm-game-of-life/.gitignore7
-rw-r--r--wasm-game-of-life/Cargo.toml37
-rw-r--r--wasm-game-of-life/src/lib.rs132
-rw-r--r--wasm-game-of-life/src/utils.rs10
-rw-r--r--wasm-game-of-life/tests/web.rs13
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);
+}