@@ -0,0 +1,216 @@
1+use dodrio::bumpalo::{self, Bump};
2+use dodrio::{Node, Render, Vdom};
3+use futures::prelude::*;
4+use std::cell::RefCell;
6+use wasm_bindgen::{prelude::*, JsCast};
9+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15+/// The universe containing all of the cells for our Game of Life.
23+ /// Construct a new universe.
24+ pub fn new() -> Universe {
28+ let cells = (0..width * height)
30+ if i % 2 == 0 || i % 7 == 0 {
45+ /// Compute one tick of the universe.
46+ pub fn tick(&mut self) {
47+ let mut next = self.cells.clone();
49+ for row in 0..self.height {
50+ for col in 0..self.width {
51+ let idx = self.get_index(row, col);
52+ let cell = self.cells[idx];
53+ let live_neighbors = self.live_neighbor_count(row, col);
55+ let next_cell = match (cell, live_neighbors) {
56+ // Rule 1: Any live cell with fewer than two live neighbours
57+ // dies, as if caused by underpopulation.
58+ (Cell::Alive, x) if x < 2 => Cell::Dead,
59+ // Rule 2: Any live cell with two or three live neighbours
60+ // lives on to the next generation.
61+ (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
62+ // Rule 3: Any live cell with more than three live
63+ // neighbours dies, as if by overpopulation.
64+ (Cell::Alive, x) if x > 3 => Cell::Dead,
65+ // Rule 4: Any dead cell with exactly three live neighbours
66+ // becomes a live cell, as if by reproduction.
67+ (Cell::Dead, 3) => Cell::Alive,
68+ // All other cells remain in the same state.
69+ (otherwise, _) => otherwise,
72+ next[idx] = next_cell;
79+ fn get_index(&self, row: u32, column: u32) -> usize {
80+ (row * self.width + column) as usize
83+ fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
86+ let north = if row == 0 { self.height - 1 } else { row - 1 };
88+ let south = if row == self.height - 1 { 0 } else { row + 1 };
90+ let west = if column == 0 {
96+ let east = if column == self.width - 1 {
102+ let nw = self.get_index(north, west);
103+ count += self.cells[nw] as u8;
105+ let n = self.get_index(north, column);
106+ count += self.cells[n] as u8;
108+ let ne = self.get_index(north, east);
109+ count += self.cells[ne] as u8;
111+ let w = self.get_index(row, west);
112+ count += self.cells[w] as u8;
114+ let e = self.get_index(row, east);
115+ count += self.cells[e] as u8;
117+ let sw = self.get_index(south, west);
118+ count += self.cells[sw] as u8;
120+ let s = self.get_index(south, column);
121+ count += self.cells[s] as u8;
123+ let se = self.get_index(south, east);
124+ count += self.cells[se] as u8;
130+/// The rendering implementation for our Game of Life.
131+impl Render for Universe {
132+ fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
136+ use dodrio::builder::*;
138+ let mut rows = bumpalo::collections::Vec::with_capacity_in(self.height as usize, bump);
140+ for row in self.cells.chunks(self.width as usize) {
141+ let mut cells = bumpalo::collections::Vec::with_capacity_in(self.width as usize, bump);
146+ .attr("class", "cell")
150+ Cell::Alive => "background-color: black",
151+ Cell::Dead => "background-color: white",
158+ rows.push(div(bump).attr("class", "row").children(cells).finish());
161+ div(bump).attr("class", "universe").children(rows).finish()
165+#[wasm_bindgen(start)]
167+ // Set up the panic hook for debugging when things go wrong.
168+ console_error_panic_hook::set_once();
170+ // Grab the document's `<body>`.
171+ let window = web_sys::window().unwrap_throw();
172+ let document = window.document().unwrap_throw();
173+ let body = document.body().unwrap_throw();
175+ // Create a new Game of Life `Universe` render component.
176+ let universe = Universe::new();
178+ // Create a virtual DOM and mount it and the `Hello` render component to the
180+ let vdom = Vdom::new(body.as_ref(), universe);
182+ // Kick off a loop that keeps computing a tick of the universe and then
183+ // re-rendering on every animation frame.
184+ let rc: Rc<RefCell<Option<Closure<FnMut()>>>> = Rc::new(RefCell::new(None));
185+ let rc2 = rc.clone();
186+ let window2 = window.clone();
187+ let weak = vdom.weak();
188+ let f = Closure::wrap(Box::new(move || {
189+ weak.schedule_render();
191+ wasm_bindgen_futures::spawn_local(
192+ weak.with_component(|root| {
193+ let universe = root.unwrap_mut::<Universe>();
196+ .map_err(|_| wasm_bindgen::throw_str("impossible, we always keep the vdom alive")),
200+ .request_animation_frame(
205+ .unchecked_ref::<js_sys::Function>(),
208+ }) as Box<FnMut()>);
210+ .request_animation_frame(f.as_ref().unchecked_ref::<js_sys::Function>())
212+ *rc2.borrow_mut() = Some(f);
214+ // Run the virtual DOM forever and don't unmount it.