Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add examples of correct code at major points throughout the docs #284

Open
CLEckhardt opened this issue Dec 11, 2022 · 2 comments
Open

Add examples of correct code at major points throughout the docs #284

CLEckhardt opened this issue Dec 11, 2022 · 2 comments

Comments

@CLEckhardt
Copy link

Where in the docs did you come across this?
I could not get past section 4.4 > Rendering with JavaScript

Describe what about it does not make sense
My code runs fine (without warnings or errors) but nothing renders in the browser. I have checked it against the docs multiple times, but without a clear example of what the code should look like at this point, it's very difficult to debug what's going on.

Why does it not make sense?
There is not a complete picture of what the code should be at this point in the docs.

How could we improve it?
Add clear examples of what the code should look like at major points in the book.

This idea is mentioned in other issues, like #169 and #210, but I'm creating a new issue because I think it is worth calling out on its own. I also think having working examples after each major step would help folks (beginners especially) debug their own code. Perhaps this would help reduce confusion around the #[wasm-bindgen] macro, implementing Display or other issues I've seen where the user would likely be able to debug their code if they had something to compare it to. So, I think this would not only be beneficial to users but could perhaps reduce the number of simple questions asked of others.

For me, https://github.com/rustwasm/wasm_game_of_life is too far along to be helpful for debugging section 4.4 > Rendering with JavaScript. I can get the linked example to work just fine, but I still can't tell what's wrong with my current code.

To be clear, I'm not suggesting that the examples be put in the docs themselves - likely a link to a repo would be best.

@suchapalaver
Copy link

suchapalaver commented Jan 6, 2023

I was also stuck at this same point for a while. Including code that finally worked as a workaround for now:

src/lib.rs:

mod utils;

use std::fmt;

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]
#[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>,
}

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(())
    }
}

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_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
    }
}

/// Public methods, exported to JavaScript.
#[wasm_bindgen]
impl Universe {
    pub fn new() -> Universe {
        let width = 64;
        let height = 64;

        let cells = (0..width * height)
            .map(|i| {
                if i % 2 == 0 || i % 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) {
                    // 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;
    }

    // ...
}

www/index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello wasm-pack!</title>
    <style>
      body {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
      }
    </style>
  </head>
  <body>
    <pre id="game-of-life-canvas"></pre>
    <script src="./bootstrap.js"></script>
  </body>
</html>

www/index.js:

import { Universe } from "wasm-game-of-life";

const pre = document.getElementById("game-of-life-canvas");
const universe = Universe.new();
const renderLoop = () => {
    pre.textContent = universe.render();
    universe.tick();
  
    requestAnimationFrame(renderLoop);
  };

requestAnimationFrame(renderLoop);

@chinoto
Copy link

chinoto commented Mar 1, 2023

You probably made the same mistake I did and missed the code under

To start the rendering process, we'll use the same code as above to start the first iteration of the rendering loop:

I came up with this because I didn't like the code duplication and, since async-await exists, I replaced the recursion with looping to make it clearer, then I realized that the Universe.tick() is tied to the refresh rate (mine is 300Hz...), so I tried setTimeout, but the spinners looked a little glitchy, so I went back to requestAnimationFrame with extra code to skip if the frame time is too early:

(async function renderLoop() {
  let lastTime;
  while (true) {
    // await new Promise((res) => setTimeout(res, 1000 / 60));
    let time = await new Promise(requestAnimationFrame);
    if (lastTime && time - lastTime < 1000 / 59) {
      continue;
    }
    lastTime = time;

    drawCells();
    universe.tick();
  }
})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants