aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoris2020-01-28 09:55:58 +0100
committerJoris2020-01-29 10:12:31 +0100
commit1b6a7e0d00703e3da2e1620b5a2b2cba027161de (patch)
tree5143f784e1529d3b6c04116c84f09c426bb257b0
parent197b6fa7aa810147d63209408c3a378ec552d0f4 (diff)
downloadgame-of-life-1b6a7e0d00703e3da2e1620b5a2b2cba027161de.tar.gz
game-of-life-1b6a7e0d00703e3da2e1620b5a2b2cba027161de.tar.bz2
game-of-life-1b6a7e0d00703e3da2e1620b5a2b2cba027161de.zip
Implement game of life
-rw-r--r--.gitignore2
-rw-r--r--.gitlab-ci.yml10
-rw-r--r--Cargo.lock235
-rw-r--r--Cargo.toml27
-rw-r--r--LICENSE0
-rw-r--r--README.md24
-rwxr-xr-xdeploy16
l---------public/game_of_life.js1
l---------public/game_of_life_bg.wasm1
-rw-r--r--public/index.html28
-rw-r--r--shell.nix17
-rw-r--r--src/canvas.rs64
-rw-r--r--src/console.rs5
-rw-r--r--src/game.rs31
-rw-r--r--src/game_loop.rs31
-rw-r--r--src/lib.rs26
-rw-r--r--src/state.rs72
-rwxr-xr-xwatch13
18 files changed, 603 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..34aa146
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+target/
+pkg/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..a872f80
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,10 @@
+image: alpine:latest
+pages:
+ stage: deploy
+ script:
+ - echo 'Nothing to do...'
+ artifacts:
+ paths:
+ - public
+ only:
+ - pages
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..1c06bd8
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,235 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "anyhow"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bumpalo"
+version = "3.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "game_of_life"
+version = "0.1.0"
+dependencies = [
+ "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+ "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "log"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "memchr"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "nom"
+version = "4.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "sourcefile"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "syn"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "version_check"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "wasm-bindgen-webidl"
+version = "0.2.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+ "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "weedle"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[metadata]
+"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c"
+"checksum bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4"
+"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+"checksum console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
+"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
+"checksum js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9"
+"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223"
+"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
+"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
+"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
+"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
+"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
+"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
+"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c"
+"checksum wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45"
+"checksum wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3"
+"checksum wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668"
+"checksum wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601"
+"checksum wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d"
+"checksum web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b"
+"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..aa18d6f
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "game_of_life"
+version = "0.1.0"
+authors = ["Joris"]
+edition = "2018"
+description = ''
+repository = ''
+license = 'LICENSE'
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+wasm-bindgen = "0.2.58"
+js-sys = "0.3.35"
+console_error_panic_hook = "0.1.6"
+
+[dependencies.web-sys]
+version = "0.3.35"
+features = [
+ 'CanvasRenderingContext2d',
+ 'Document',
+ 'Element',
+ 'HtmlCanvasElement',
+ 'Window',
+ 'console',
+]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/LICENSE
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e9517c3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+# Game of life
+
+The code is written in Rust and is compiled to WebAssembly.
+[wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) is used to interact
+with JavaScript.
+
+See [the wikipedia page](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life)
+to get more information about game of life.
+
+## Live
+
+[https://guyonvarch.gitlab.io/game-of-life](https://guyonvarch.gitlab.io/game-of-life)
+
+## Getting started
+
+```bash
+nix-shell --run ./watch
+```
+
+## Deploy
+
+```bash
+nix-shell --run ./deploy
+```
diff --git a/deploy b/deploy
new file mode 100755
index 0000000..b3c6d61
--- /dev/null
+++ b/deploy
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -e
+git branch -D pages
+git checkout -b pages
+rm -f public/game_of_life.js
+rm -f public/game_of_life_bg.wasm
+rm -f public/game_of_life_bg.wasm.gz
+wasm-pack build --release --target web
+mv pkg/game_of_life.js public
+wasm-opt -O -o public/game_of_life_bg.wasm pkg/game_of_life_bg.wasm
+gzip -9 --keep public/game_of_life_bg.wasm
+git add .
+git commit -m "Deploy pages"
+git push --force origin pages
+git checkout master
+git branch -D pages
diff --git a/public/game_of_life.js b/public/game_of_life.js
new file mode 120000
index 0000000..998a5ea
--- /dev/null
+++ b/public/game_of_life.js
@@ -0,0 +1 @@
+../pkg/game_of_life.js \ No newline at end of file
diff --git a/public/game_of_life_bg.wasm b/public/game_of_life_bg.wasm
new file mode 120000
index 0000000..fe3f9b8
--- /dev/null
+++ b/public/game_of_life_bg.wasm
@@ -0,0 +1 @@
+../pkg/game_of_life_bg.wasm \ No newline at end of file
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..ed2639f
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
+ <style>
+ body {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #333333;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ </style>
+ </head>
+ <body>
+ <canvas id="canvas" height="800" width="800" />
+ <script type="module">
+ import init from './game_of_life.js';
+
+ async function run() {
+ await init();
+ }
+
+ run();
+ </script>
+ </body>
+</html>
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..45261c8
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,17 @@
+with import <nixpkgs> {};
+
+let unstable = import (
+ fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-unstable.tar.gz
+ ) {};
+in
+
+mkShell {
+ buildInputs = [
+ entr
+ gzip
+ python3
+ rustup
+ unstable.binaryen
+ wasm-pack
+ ];
+}
diff --git a/src/canvas.rs b/src/canvas.rs
new file mode 100644
index 0000000..38aef4d
--- /dev/null
+++ b/src/canvas.rs
@@ -0,0 +1,64 @@
+use wasm_bindgen::prelude::JsValue;
+use wasm_bindgen::JsCast;
+use web_sys::CanvasRenderingContext2d;
+
+pub struct Canvas {
+ context: CanvasRenderingContext2d,
+ width: u32,
+ height: u32,
+ scaled_width: u32,
+ scaled_height: u32,
+}
+
+impl Canvas {
+ pub fn new(attr_id: &str, width: u32, height: u32) -> Canvas {
+ let document = web_sys::window().unwrap().document().unwrap();
+ let canvas = document.get_element_by_id(attr_id).unwrap();
+ let canvas: web_sys::HtmlCanvasElement = canvas
+ .dyn_into::<web_sys::HtmlCanvasElement>()
+ .unwrap();
+
+ let context = canvas
+ .get_context("2d")
+ .unwrap()
+ .unwrap()
+ .dyn_into::<web_sys::CanvasRenderingContext2d>()
+ .unwrap();
+
+ let scaled_width = canvas.width() / width;
+ let scaled_height = canvas.height() / height;
+
+ Canvas {
+ context,
+ width,
+ height,
+ scaled_width,
+ scaled_height,
+ }
+ }
+
+ pub fn clear(&self) {
+ self.context.set_fill_style(&JsValue::from_str("white"));
+
+ self.context.fill_rect(
+ f64::from(0),
+ f64::from(0),
+ f64::from(self.width * self.scaled_width),
+ f64::from(self.height * self.scaled_height)
+ );
+ }
+
+ pub fn draw(&self, x: u32, y: u32, color: &str) {
+ assert!(x < self.width);
+ assert!(y < self.height);
+
+ self.context.set_fill_style(&JsValue::from_str(color));
+
+ self.context.fill_rect(
+ f64::from(x * self.scaled_width),
+ f64::from(y * self.scaled_height),
+ f64::from(self.scaled_width),
+ f64::from(self.scaled_height)
+ );
+ }
+}
diff --git a/src/console.rs b/src/console.rs
new file mode 100644
index 0000000..4efbd80
--- /dev/null
+++ b/src/console.rs
@@ -0,0 +1,5 @@
+use wasm_bindgen::prelude::JsValue;
+
+pub fn log(str: &str) {
+ web_sys::console::log_1(&JsValue::from_str(str));
+}
diff --git a/src/game.rs b/src/game.rs
new file mode 100644
index 0000000..4b527d2
--- /dev/null
+++ b/src/game.rs
@@ -0,0 +1,31 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use crate::canvas::Canvas;
+use crate::state::State;
+
+pub struct Game {
+ canvas: Canvas,
+ state: Rc<RefCell<State>>,
+}
+
+impl Game {
+ pub fn new(canvas_id: &str, width: u32, height: u32) -> Game {
+ let canvas = Canvas::new(canvas_id, width, height);
+ let state = Rc::new(RefCell::new(State::new(width, height)));
+
+ Game {
+ canvas,
+ state,
+ }
+ }
+
+ pub fn update(&self) {
+ let next_state = self.state.borrow().next();
+ *self.state.borrow_mut() = next_state;
+ }
+
+ pub fn render(&self) {
+ self.state.borrow().draw(&self.canvas);
+ }
+}
diff --git a/src/game_loop.rs b/src/game_loop.rs
new file mode 100644
index 0000000..34dd477
--- /dev/null
+++ b/src/game_loop.rs
@@ -0,0 +1,31 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use wasm_bindgen::prelude::Closure;
+use wasm_bindgen::JsCast;
+
+use crate::game::Game;
+
+pub fn run(game: Game, update_period: i32) {
+ game.render();
+
+ let f = Rc::new(RefCell::new(None));
+ let g = f.clone();
+
+ *g.borrow_mut() = Some(Closure::wrap(Box::new(move |_| {
+
+ game.update();
+ game.render();
+
+ set_timeout(f.borrow().as_ref().unwrap(), update_period);
+
+ }) as Box<dyn FnMut(i32)>));
+
+ set_timeout(g.borrow().as_ref().unwrap(), update_period);
+}
+
+fn set_timeout(f: &Closure<dyn FnMut(i32)>, timeout: i32) {
+ web_sys::window().unwrap()
+ .set_timeout_with_callback_and_timeout_and_arguments_0(f.as_ref().unchecked_ref(), timeout)
+ .unwrap();
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..f7fa24c
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,26 @@
+use wasm_bindgen::prelude::{wasm_bindgen, JsValue};
+
+mod canvas;
+mod state;
+mod game;
+mod game_loop;
+
+use game::Game;
+
+#[wasm_bindgen(start)]
+pub fn main() -> Result<(), JsValue> {
+ set_panic_hook();
+ game_loop::run(Game::new("canvas", 100, 100), 100);
+ Ok(())
+}
+
+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/src/state.rs b/src/state.rs
new file mode 100644
index 0000000..5d13902
--- /dev/null
+++ b/src/state.rs
@@ -0,0 +1,72 @@
+use crate::canvas::Canvas;
+
+#[derive(Debug, Clone)]
+pub struct State {
+ width: u32,
+ height: u32,
+ cells: Vec::<bool>,
+}
+
+impl State {
+ pub fn new(width: u32, height: u32) -> State {
+ let cells = (0..width * height)
+ .map(|_| js_sys::Math::random() < 0.5)
+ .collect();
+
+ State {
+ width,
+ height,
+ cells,
+ }
+ }
+
+ pub fn neighbor_count(&self, x: u32, y: u32) -> u32 {
+ let mut count = 0;
+ for delta_x in [self.height - 1, 0, 1].iter().clone() {
+ for delta_y in [self.width - 1, 0, 1].iter().clone() {
+ let neighbor_x = (x + delta_x) % self.width;
+ let neighbor_y = (y + delta_y) % self.height;
+
+ if (*delta_x, *delta_y) != (0, 0) && self.is_on(neighbor_x, neighbor_y) {
+ count += 1;
+ }
+ }
+ }
+ count
+ }
+
+ pub fn next(&self) -> State {
+ let cells = (0..self.width * self.height)
+ .map(|i| {
+ let x = i % self.width;
+ let y = i / self.width;
+ let neighbor_count = self.neighbor_count(x, y);
+ neighbor_count == 3 || self.is_on(x, y) && neighbor_count == 2
+ })
+ .collect();
+
+ State {
+ width: self.width,
+ height: self.height,
+ cells,
+ }
+ }
+
+ fn is_on(&self, x: u32, y: u32) -> bool {
+ let inside_x = (x + self.width) % self.width;
+ let inside_y = (y + self.height) % self.height;
+ self.cells[(inside_x + inside_y * self.width) as usize]
+ }
+
+ pub fn draw(&self, canvas: &Canvas) {
+ canvas.clear();
+
+ for y in 0..self.height {
+ for x in 0..self.width {
+ if self.is_on(x, y) {
+ canvas.draw(x, y, "green");
+ }
+ }
+ }
+ }
+}
diff --git a/watch b/watch
new file mode 100755
index 0000000..802c503
--- /dev/null
+++ b/watch
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+python -m http.server --directory public 8000 &
+
+trap "fuser -k 8000/tcp" EXIT
+
+CMD="clear && wasm-pack build --target web"
+
+while true; do
+
+ find src | entr -d -s "$CMD"
+
+done