random board gen, UI input

This commit is contained in:
aj 2020-10-12 00:10:54 +01:00
parent b6e9ebd18d
commit ac57886578
5 changed files with 174 additions and 89 deletions

View File

@ -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

View File

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

View File

@ -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.

View File

@ -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>

View File

@ -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); }
}
};
renderSingle(); const stop = () => {
requestAnimationFrame(renderLoop); 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();
}
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();