random board gen, UI input
This commit is contained in:
parent
b6e9ebd18d
commit
ac57886578
@ -9,10 +9,16 @@ repository = "https://github.com/Sarsoo/game-of-life"
|
|||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["console_error_panic_hook"]
|
default = ["console_error_panic_hook",
|
||||||
|
"random_init"
|
||||||
|
]
|
||||||
|
|
||||||
|
random_init = ["rand", "rand_pcg"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasm-bindgen = "0.2.63"
|
wasm-bindgen = "0.2.63"
|
||||||
|
rand = {version = "0.7.3", optional = true }
|
||||||
|
rand_pcg = {version = "0.2.1", optional = true }
|
||||||
|
|
||||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||||
# logging them with `console.error`. This is great for development, but requires
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
|
51
src/lib.rs
51
src/lib.rs
@ -1,15 +1,19 @@
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
// use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
|
use rand_pcg::Pcg64Mcg;
|
||||||
|
|
||||||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||||||
// allocator.
|
// allocator.
|
||||||
#[cfg(feature = "wee_alloc")]
|
// #[cfg(feature = "wee_alloc")]
|
||||||
#[global_allocator]
|
// #[global_allocator]
|
||||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
// static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
macro_rules! log {
|
macro_rules! log {
|
||||||
( $( $t:tt )* ) => {
|
( $( $t:tt )* ) => {
|
||||||
@ -17,6 +21,15 @@ macro_rules! log {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn init() {
|
||||||
|
log!("initialising wasm");
|
||||||
|
utils::set_panic_hook();
|
||||||
|
|
||||||
|
#[cfg(feature = "random_init")]
|
||||||
|
log!("random layout enabled");
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@ -30,6 +43,9 @@ pub struct Universe {
|
|||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
cells: Vec<Cell>,
|
cells: Vec<Cell>,
|
||||||
|
|
||||||
|
rng: Pcg64Mcg,
|
||||||
|
rand_threshold: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Universe {
|
impl Universe {
|
||||||
@ -54,6 +70,7 @@ impl Universe {
|
|||||||
count
|
count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "random_init"))]
|
||||||
fn populate_cell(i: u32) -> Cell {
|
fn populate_cell(i: u32) -> Cell {
|
||||||
if i % 2 == 0 || i % 7 == 0 {
|
if i % 2 == 0 || i % 7 == 0 {
|
||||||
Cell::Alive
|
Cell::Alive
|
||||||
@ -61,6 +78,15 @@ impl Universe {
|
|||||||
Cell::Dead
|
Cell::Dead
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "random_init")]
|
||||||
|
fn populate_cell(i: u32, rng: &mut Pcg64Mcg, threshold: u32) -> Cell {
|
||||||
|
match rng.gen_range(0, 101).cmp(&threshold) {
|
||||||
|
Ordering::Less => Cell::Alive,
|
||||||
|
Ordering::Greater => Cell::Dead,
|
||||||
|
Ordering::Equal => Cell::Dead,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@ -99,12 +125,17 @@ impl Universe {
|
|||||||
self.cells = next;
|
self.cells = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(width: u32, height: u32) -> Universe {
|
pub fn new(width: u32, height: u32, rand_threshold: u32, seed: f64) -> Universe {
|
||||||
log!("Generating new board {}x{}", width, height);
|
log!("Generating new board {}x{}", width, height);
|
||||||
|
|
||||||
|
let mut rng = Pcg64Mcg::seed_from_u64(seed as u64);
|
||||||
|
|
||||||
let cells = (0..width * height)
|
let cells = (0..width * height)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
Universe::populate_cell(i)
|
#[cfg(not(feature = "random_init"))]
|
||||||
|
return Universe::populate_cell(i);
|
||||||
|
#[cfg(feature = "random_init")]
|
||||||
|
return Universe::populate_cell(i, &mut rng, rand_threshold);
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -112,6 +143,9 @@ impl Universe {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
cells,
|
cells,
|
||||||
|
|
||||||
|
rng,
|
||||||
|
rand_threshold,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +168,10 @@ impl Universe {
|
|||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.cells = (0..self.width * self.height)
|
self.cells = (0..self.width * self.height)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
Universe::populate_cell(i)
|
#[cfg(not(feature = "random_init"))]
|
||||||
|
return Universe::populate_cell(i);
|
||||||
|
#[cfg(feature = "random_init")]
|
||||||
|
return Universe::populate_cell(i, &mut self.rng, self.rand_threshold);
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
<div align="center">
|
|
||||||
|
|
||||||
<h1><code>create-wasm-app</code></h1>
|
|
||||||
|
|
||||||
<strong>An <code>npm init</code> template for kick starting a project that uses NPM packages containing Rust-generated WebAssembly and bundles them with Webpack.</strong>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://travis-ci.org/rustwasm/create-wasm-app"><img src="https://img.shields.io/travis/rustwasm/create-wasm-app.svg?style=flat-square" alt="Build Status" /></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>
|
|
||||||
<a href="#usage">Usage</a>
|
|
||||||
<span> | </span>
|
|
||||||
<a href="https://discordapp.com/channels/442252698964721669/443151097398296587">Chat</a>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<sub>Built with 🦀🕸 by <a href="https://rustwasm.github.io/">The Rust and WebAssembly Working Group</a></sub>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## About
|
|
||||||
|
|
||||||
This template is designed for depending on NPM packages that contain
|
|
||||||
Rust-generated WebAssembly and using them to create a Website.
|
|
||||||
|
|
||||||
* Want to create an NPM package with Rust and WebAssembly? [Check out
|
|
||||||
`wasm-pack-template`.](https://github.com/rustwasm/wasm-pack-template)
|
|
||||||
* Want to make a monorepo-style Website without publishing to NPM? Check out
|
|
||||||
[`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template)
|
|
||||||
and/or
|
|
||||||
[`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template).
|
|
||||||
|
|
||||||
## 🚴 Usage
|
|
||||||
|
|
||||||
```
|
|
||||||
npm init wasm-app
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔋 Batteries Included
|
|
||||||
|
|
||||||
- `.gitignore`: ignores `node_modules`
|
|
||||||
- `LICENSE-APACHE` and `LICENSE-MIT`: most Rust projects are licensed this way, so these are included for you
|
|
||||||
- `README.md`: the file you are reading now!
|
|
||||||
- `index.html`: a bare bones html document that includes the webpack bundle
|
|
||||||
- `index.js`: example js file with a comment showing how to import and use a wasm pkg
|
|
||||||
- `package.json` and `package-lock.json`:
|
|
||||||
- pulls in devDependencies for using webpack:
|
|
||||||
- [`webpack`](https://www.npmjs.com/package/webpack)
|
|
||||||
- [`webpack-cli`](https://www.npmjs.com/package/webpack-cli)
|
|
||||||
- [`webpack-dev-server`](https://www.npmjs.com/package/webpack-dev-server)
|
|
||||||
- defines a `start` script to run `webpack-dev-server`
|
|
||||||
- `webpack.config.js`: configuration file for bundling your js with webpack
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Licensed under either of
|
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
at your option.
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally
|
|
||||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
|
||||||
license, shall be dual licensed as above, without any additional terms or
|
|
||||||
conditions.
|
|
@ -18,8 +18,20 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<input type="range" id="frameRate" name="frameRate" min="1" max="500" value="50">
|
||||||
|
<label for="frameRate" id="frameRate-label">Frame Interval: 50</label>
|
||||||
|
<input type="range" id="randThreshold" name="randThreshold" min="0" max="100" value="50">
|
||||||
|
<label for="randThreshold" id="randThreshold-label">Rand Threshold: 50</label>
|
||||||
|
<input type="number" id="width" name="width" min="1" max="1000" value="100">
|
||||||
|
<label for="width">width</label>
|
||||||
|
<input type="number" id="height" name="height" min="1" max="1000" value="100">
|
||||||
|
<label for="height">height</label>
|
||||||
<!-- <pre id="game-of-life-canvas"></pre> -->
|
<!-- <pre id="game-of-life-canvas"></pre> -->
|
||||||
<canvas id="game-of-life-canvas"></canvas>
|
<canvas id="game-of-life-canvas"></canvas>
|
||||||
|
<input type="checkbox" id="play-check" name="play-check">
|
||||||
|
<label for="play-check">Play</label>
|
||||||
|
<button id="step">Step</button>
|
||||||
|
<button id="reset">Reset</button>
|
||||||
<script src="./bootstrap.js"></script>
|
<script src="./bootstrap.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
121
www/index.js
121
www/index.js
@ -1,18 +1,20 @@
|
|||||||
import { Universe, Cell } from "game-of-life";
|
import { Universe, Cell, init } from "game-of-life";
|
||||||
import { memory } from "game-of-life/game_of_life_bg";
|
import { memory } from "game-of-life/game_of_life_bg";
|
||||||
|
|
||||||
let PLAY = true;
|
// let PLAY = true;
|
||||||
// let PLAY = false;
|
// let PLAY = false;
|
||||||
|
init();
|
||||||
|
const randSlider = document.getElementById("randThreshold");
|
||||||
|
const randSliderLabel = document.getElementById("randThreshold-label");
|
||||||
|
|
||||||
const CELL_SIZE = 5; // px
|
const CELL_SIZE = 5; // px
|
||||||
const GRID_COLOR = "#BBBBBB";
|
const GRID_COLOR = "#BBBBBB";
|
||||||
const DEAD_COLOR = "#FFFFFF";
|
const DEAD_COLOR = "#FFFFFF";
|
||||||
const ALIVE_COLOR = "#FF55AA";
|
const ALIVE_COLOR = "#FF55AA";
|
||||||
|
|
||||||
const universe = Universe.new(200, 109);
|
let universe = Universe.new(100, 100, randSlider.value, new Date().getTime() / 1000);
|
||||||
const width = universe.width();
|
let width = universe.width();
|
||||||
const height = universe.height();
|
let height = universe.height();
|
||||||
// console.log(universe);
|
|
||||||
|
|
||||||
const canvas = document.getElementById("game-of-life-canvas");
|
const canvas = document.getElementById("game-of-life-canvas");
|
||||||
canvas.height = (CELL_SIZE + 1) * height + 1;
|
canvas.height = (CELL_SIZE + 1) * height + 1;
|
||||||
@ -76,12 +78,107 @@ const renderSingle = () => {
|
|||||||
drawCells();
|
drawCells();
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderLoop = () => {
|
const start = () => {
|
||||||
if(PLAY){
|
if(loop != null) clearInterval(loop);
|
||||||
renderSingle();
|
loop = setInterval(renderSingle, frameInterval);
|
||||||
requestAnimationFrame(renderLoop);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
const stop = () => {
|
||||||
|
if(loop != null) clearInterval(loop);
|
||||||
|
loop = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const renderLoop = () => {
|
||||||
|
// if(PLAY){
|
||||||
|
// renderSingle();
|
||||||
|
// requestAnimationFrame(renderLoop);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// renderSingle();
|
||||||
|
// requestAnimationFrame(renderLoop);
|
||||||
|
|
||||||
|
var frameInterval = 50;
|
||||||
|
// var loop = setInterval(renderSingle, frameInterval);
|
||||||
|
var loop = null;
|
||||||
|
|
||||||
|
const frameSlider = document.getElementById("frameRate");
|
||||||
|
const frameSliderLabel = document.getElementById("frameRate-label");
|
||||||
|
const onFrameSlider = () => {
|
||||||
|
stop();
|
||||||
|
|
||||||
|
frameInterval = frameSlider.value;
|
||||||
|
frameSliderLabel.innerHTML = `Frame Interval: ${frameSlider.value}`;
|
||||||
|
|
||||||
|
if(playCheck.checked) start();
|
||||||
|
}
|
||||||
|
frameSlider.onchange = onFrameSlider;
|
||||||
|
|
||||||
|
const onRandSlider = () => {
|
||||||
|
stop();
|
||||||
|
|
||||||
|
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
||||||
|
refreshCanvas();
|
||||||
|
randSliderLabel.innerHTML = `Rand Threshold: ${randSlider.value}`;
|
||||||
|
|
||||||
|
if(playCheck.checked) start();
|
||||||
|
}
|
||||||
|
randSlider.onchange = onRandSlider;
|
||||||
|
|
||||||
|
const refreshCanvas = () => {
|
||||||
|
canvas.width = (CELL_SIZE + 1) * width + 1;
|
||||||
|
canvas.height = (CELL_SIZE + 1) * height + 1;
|
||||||
|
drawGrid();
|
||||||
|
drawCells();
|
||||||
|
}
|
||||||
|
|
||||||
|
const widthBox = document.getElementById("width");
|
||||||
|
const onWidth = () => {
|
||||||
|
// PLAY = false;
|
||||||
|
width = widthBox.value;
|
||||||
|
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
||||||
|
refreshCanvas();
|
||||||
|
// PLAY = true;
|
||||||
|
// requestAnimationFrame(renderLoop);
|
||||||
|
}
|
||||||
|
widthBox.onchange = onWidth;
|
||||||
|
|
||||||
|
const heightBox = document.getElementById("height");
|
||||||
|
const onHeight = () => {
|
||||||
|
// PLAY = false;
|
||||||
|
height = heightBox.value;
|
||||||
|
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
||||||
|
refreshCanvas();
|
||||||
|
// PLAY = true;
|
||||||
|
// requestAnimationFrame(renderLoop);
|
||||||
|
}
|
||||||
|
heightBox.onchange = onHeight;
|
||||||
|
|
||||||
|
const playCheck = document.getElementById("play-check");
|
||||||
|
const onPlay = () => {
|
||||||
|
console.log("play: " + playCheck.checked);
|
||||||
|
if(playCheck.checked) {
|
||||||
|
start();
|
||||||
|
}else {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
// PLAY = playCheck.checked;
|
||||||
|
// requestAnimationFrame(renderLoop);
|
||||||
|
}
|
||||||
|
playCheck.onchange = onPlay;
|
||||||
|
|
||||||
|
const onStep = () => {
|
||||||
|
console.log("stepping");
|
||||||
renderSingle();
|
renderSingle();
|
||||||
requestAnimationFrame(renderLoop);
|
}
|
||||||
|
document.getElementById("step").onclick = onStep;
|
||||||
|
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
||||||
|
refreshCanvas();
|
||||||
|
}
|
||||||
|
document.getElementById("reset").onclick = onReset;
|
||||||
|
|
||||||
|
drawGrid();
|
||||||
|
drawCells();
|
Loading…
Reference in New Issue
Block a user