replace play checkbox with button, added description
This commit is contained in:
parent
de8ef49de3
commit
07d2e0bb68
@ -94,7 +94,7 @@ impl Universe {
|
|||||||
impl Universe {
|
impl Universe {
|
||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self) {
|
||||||
// log!("ticking");
|
// log!("ticking");
|
||||||
let _timer = time::Timer::new("Universe::tick"); // will stop when dropped
|
// let _timer = time::Timer::new("Universe::tick"); // will stop when dropped
|
||||||
let mut next = self.cells.clone();
|
let mut next = self.cells.clone();
|
||||||
|
|
||||||
for row in 0..self.height {
|
for row in 0..self.height {
|
||||||
|
@ -30,19 +30,24 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="card container-xs text-center p-4 m-3">
|
<div class="card container-xs text-center p-4 m-3">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h1>Game of Life</h1>
|
<h1>Game of Life 🚀</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
<div class="row p-1">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<p class="text-muted">An implementation of the standard <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Conway Game of Life</a>. The game logic is written in WASM-targeted Rust with a light Js frontend. Read through the source code <a href="https://github.com/Sarsoo/game-of-life">here</a>. It was written following the guides in the <a href="https://rustwasm.github.io/docs/book">Rust & WebAssembly book</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row p-3">
|
<div class="row p-3">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="range"
|
<input type="range"
|
||||||
id="frameRate"
|
id="frameRate"
|
||||||
name="frameRate"
|
name="frameRate"
|
||||||
min="1" max="500" value="50"
|
min="1" max="500" value="100"
|
||||||
class="form-range">
|
class="form-range">
|
||||||
<label for="frameRate"
|
<label for="frameRate"
|
||||||
id="frameRate-label"
|
id="frameRate-label"
|
||||||
class="form-label">Frame Interval: 50</label>
|
class="form-label">Frame Interval: 100ms</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="range"
|
<input type="range"
|
||||||
@ -52,7 +57,7 @@
|
|||||||
class="form-range">
|
class="form-range">
|
||||||
<label for="randThreshold"
|
<label for="randThreshold"
|
||||||
id="randThreshold-label"
|
id="randThreshold-label"
|
||||||
class="form-label">Rand Threshold: 50</label>
|
class="form-label">Random Threshold: 50%</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row p-3">
|
<div class="row p-3">
|
||||||
@ -75,18 +80,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row p-3 align-items-center">
|
<div class="row p-3 align-items-center">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="checkbox"
|
<!-- <input type="checkbox"
|
||||||
id="play-check"
|
id="play-check"
|
||||||
name="play-check"
|
name="play-check"
|
||||||
class="form-check-input">
|
class="form-check-input">
|
||||||
<label for="play-check">Play</label>
|
<label for="play-check">Play</label> -->
|
||||||
|
<button id="play" class="btn btn-success">Play</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="row p-1">
|
<div class="row p-1">
|
||||||
<button id="step" class="btn btn-primary">Step</button>
|
<button id="step" class="btn btn-secondary">Step</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="row p-1">
|
<div class="row p-1">
|
||||||
<button id="reset" class="btn btn-primary">Reset</button>
|
<button id="reset" class="btn btn-secondary">Reset</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -98,9 +104,7 @@
|
|||||||
|
|
||||||
<script src="./bootstrap.js"></script>
|
<script src="./bootstrap.js"></script>
|
||||||
|
|
||||||
<img src="https://storage.googleapis.com/sarsooxyzstatic/andy.png" class=" pb-2" style="width: 150px" />
|
<a href="https://github.com/sarsoo"><img src="https://storage.googleapis.com/sarsooxyzstatic/andy.png" class=" pb-2" style="width: 150px" /></a>
|
||||||
|
|
||||||
<!-- <div id="fps"></div> -->
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
134
www/index.js
134
www/index.js
@ -15,6 +15,7 @@ const ALIVE_COLOR = "#FF55AA";
|
|||||||
let universe = Universe.new(100, 100, randSlider.value, new Date().getTime() / 1000);
|
let universe = Universe.new(100, 100, randSlider.value, new Date().getTime() / 1000);
|
||||||
let width = universe.width();
|
let width = universe.width();
|
||||||
let height = universe.height();
|
let height = universe.height();
|
||||||
|
let play = false;
|
||||||
|
|
||||||
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;
|
||||||
@ -22,6 +23,9 @@ canvas.width = (CELL_SIZE + 1) * width + 1;
|
|||||||
|
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw grid onto canvas prior to painting cells
|
||||||
|
*/
|
||||||
const drawGrid = () => {
|
const drawGrid = () => {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.strokeStyle = GRID_COLOR;
|
ctx.strokeStyle = GRID_COLOR;
|
||||||
@ -41,10 +45,19 @@ const drawGrid = () => {
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get linear index from row and column indices
|
||||||
|
* @param {*} row Row index
|
||||||
|
* @param {*} column Column index
|
||||||
|
* @returns Linear index
|
||||||
|
*/
|
||||||
const getIndex = (row, column) => {
|
const getIndex = (row, column) => {
|
||||||
return row * width + column;
|
return row * width + column;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint alive cells onto grid
|
||||||
|
*/
|
||||||
const drawCells = () => {
|
const drawCells = () => {
|
||||||
const cellsPtr = universe.cells();
|
const cellsPtr = universe.cells();
|
||||||
const cells = new Uint8Array(memory.buffer, cellsPtr, width * height);
|
const cells = new Uint8Array(memory.buffer, cellsPtr, width * height);
|
||||||
@ -71,6 +84,9 @@ const drawCells = () => {
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single frame/step of game, tick universe, refresh UI
|
||||||
|
*/
|
||||||
const renderSingle = () => {
|
const renderSingle = () => {
|
||||||
// fps.render(); //new
|
// fps.render(); //new
|
||||||
universe.tick();
|
universe.tick();
|
||||||
@ -79,53 +95,62 @@ const renderSingle = () => {
|
|||||||
drawCells();
|
drawCells();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start interval timer to periodically iterate frames
|
||||||
|
*/
|
||||||
const start = () => {
|
const start = () => {
|
||||||
if(loop != null) clearInterval(loop);
|
if(loop != null) clearInterval(loop);
|
||||||
loop = setInterval(renderSingle, frameInterval);
|
loop = setInterval(renderSingle, frameInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear interval timer to stop animation loop
|
||||||
|
*/
|
||||||
const stop = () => {
|
const stop = () => {
|
||||||
if(loop != null) clearInterval(loop);
|
if(loop != null) clearInterval(loop);
|
||||||
loop = null;
|
loop = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// const renderLoop = () => {
|
|
||||||
// if(PLAY){
|
|
||||||
// renderSingle();
|
|
||||||
// requestAnimationFrame(renderLoop);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// renderSingle();
|
|
||||||
// requestAnimationFrame(renderLoop);
|
|
||||||
|
|
||||||
var frameInterval = 50;
|
var frameInterval = 50;
|
||||||
// var loop = setInterval(renderSingle, frameInterval);
|
// var loop = setInterval(renderSingle, frameInterval);
|
||||||
var loop = null;
|
var loop = null;
|
||||||
|
|
||||||
|
// SLIDERS
|
||||||
|
|
||||||
const frameSlider = document.getElementById("frameRate");
|
const frameSlider = document.getElementById("frameRate");
|
||||||
const frameSliderLabel = document.getElementById("frameRate-label");
|
const frameSliderLabel = document.getElementById("frameRate-label");
|
||||||
|
/**
|
||||||
|
* Handler for frame interval slider change, stop, change interval, start
|
||||||
|
*/
|
||||||
const onFrameSlider = () => {
|
const onFrameSlider = () => {
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
frameInterval = frameSlider.value;
|
frameInterval = frameSlider.value;
|
||||||
frameSliderLabel.innerHTML = `Frame Interval: ${frameSlider.value}`;
|
frameSliderLabel.innerHTML = `Frame Interval: ${frameSlider.value}ms`;
|
||||||
|
|
||||||
if(playCheck.checked) start();
|
if(play) start();
|
||||||
}
|
}
|
||||||
frameSlider.onchange = onFrameSlider;
|
frameSlider.onchange = onFrameSlider;
|
||||||
|
frameSlider.value = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for random threshold slider change, get a new universe with new threshold
|
||||||
|
*/
|
||||||
const onRandSlider = () => {
|
const onRandSlider = () => {
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
||||||
refreshCanvas();
|
refreshCanvas();
|
||||||
randSliderLabel.innerHTML = `Rand Threshold: ${randSlider.value}`;
|
randSliderLabel.innerHTML = `Random Threshold: ${randSlider.value}%`;
|
||||||
|
|
||||||
if(playCheck.checked) start();
|
if(play) start();
|
||||||
}
|
}
|
||||||
randSlider.onchange = onRandSlider;
|
randSlider.onchange = onRandSlider;
|
||||||
|
randSlider.value = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh existing canvas, calculate dimensions and draw
|
||||||
|
*/
|
||||||
const refreshCanvas = () => {
|
const refreshCanvas = () => {
|
||||||
canvas.width = (CELL_SIZE + 1) * width + 1;
|
canvas.width = (CELL_SIZE + 1) * width + 1;
|
||||||
canvas.height = (CELL_SIZE + 1) * height + 1;
|
canvas.height = (CELL_SIZE + 1) * height + 1;
|
||||||
@ -133,7 +158,12 @@ const refreshCanvas = () => {
|
|||||||
drawCells();
|
drawCells();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// INPUT BOXES
|
||||||
|
|
||||||
const widthBox = document.getElementById("width");
|
const widthBox = document.getElementById("width");
|
||||||
|
/**
|
||||||
|
* Handler for width input box change, get a new universe of given size
|
||||||
|
*/
|
||||||
const onWidth = () => {
|
const onWidth = () => {
|
||||||
// PLAY = false;
|
// PLAY = false;
|
||||||
width = widthBox.value;
|
width = widthBox.value;
|
||||||
@ -145,6 +175,9 @@ const onWidth = () => {
|
|||||||
widthBox.onchange = onWidth;
|
widthBox.onchange = onWidth;
|
||||||
|
|
||||||
const heightBox = document.getElementById("height");
|
const heightBox = document.getElementById("height");
|
||||||
|
/**
|
||||||
|
* Handler for height input box change, get a new universe of given size
|
||||||
|
*/
|
||||||
const onHeight = () => {
|
const onHeight = () => {
|
||||||
// PLAY = false;
|
// PLAY = false;
|
||||||
height = heightBox.value;
|
height = heightBox.value;
|
||||||
@ -155,26 +188,42 @@ const onHeight = () => {
|
|||||||
}
|
}
|
||||||
heightBox.onchange = onHeight;
|
heightBox.onchange = onHeight;
|
||||||
|
|
||||||
const playCheck = document.getElementById("play-check");
|
// BUTTONS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click handler for step button, make single move
|
||||||
|
*/
|
||||||
const onPlay = () => {
|
const onPlay = () => {
|
||||||
console.log("play: " + playCheck.checked);
|
play = !play;
|
||||||
if(playCheck.checked) {
|
|
||||||
|
// console.log("play: " + play);
|
||||||
|
if(play) {
|
||||||
|
playButton.classList.remove("btn-success");
|
||||||
|
playButton.classList.add("btn-danger");
|
||||||
|
playButton.innerText = "Stop";
|
||||||
start();
|
start();
|
||||||
}else {
|
}else {
|
||||||
|
playButton.classList.add("btn-success");
|
||||||
|
playButton.classList.remove("btn-danger");
|
||||||
|
playButton.innerText = "Play";
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
// PLAY = playCheck.checked;
|
|
||||||
// requestAnimationFrame(renderLoop);
|
|
||||||
}
|
}
|
||||||
playCheck.onchange = onPlay;
|
const playButton = document.getElementById("play");
|
||||||
|
playButton.onclick = onPlay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click handler for step button, make single move
|
||||||
|
*/
|
||||||
const onStep = () => {
|
const onStep = () => {
|
||||||
console.log("stepping");
|
console.log("stepping");
|
||||||
renderSingle();
|
renderSingle();
|
||||||
}
|
}
|
||||||
document.getElementById("step").onclick = onStep;
|
document.getElementById("step").onclick = onStep;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click handler for reset button, generate a new universe and refresh the canvas
|
||||||
|
*/
|
||||||
const onReset = () => {
|
const onReset = () => {
|
||||||
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
universe = Universe.new(width, height, randSlider.value, new Date().getTime() / 1000);
|
||||||
refreshCanvas();
|
refreshCanvas();
|
||||||
@ -183,46 +232,3 @@ document.getElementById("reset").onclick = onReset;
|
|||||||
|
|
||||||
drawGrid();
|
drawGrid();
|
||||||
drawCells();
|
drawCells();
|
||||||
|
|
||||||
const fps = new class {
|
|
||||||
constructor() {
|
|
||||||
this.fps = document.getElementById("fps");
|
|
||||||
this.frames = [];
|
|
||||||
this.lastFrameTimeStamp = performance.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
// Convert the delta time since the last frame render into a measure
|
|
||||||
// of frames per second.
|
|
||||||
const now = performance.now();
|
|
||||||
const delta = now - this.lastFrameTimeStamp;
|
|
||||||
this.lastFrameTimeStamp = now;
|
|
||||||
const fps = 1 / delta * 1000;
|
|
||||||
|
|
||||||
// Save only the latest 100 timings.
|
|
||||||
this.frames.push(fps);
|
|
||||||
if (this.frames.length > 100) {
|
|
||||||
this.frames.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the max, min, and mean of our 100 latest timings.
|
|
||||||
let min = Infinity;
|
|
||||||
let max = -Infinity;
|
|
||||||
let sum = 0;
|
|
||||||
for (let i = 0; i < this.frames.length; i++) {
|
|
||||||
sum += this.frames[i];
|
|
||||||
min = Math.min(this.frames[i], min);
|
|
||||||
max = Math.max(this.frames[i], max);
|
|
||||||
}
|
|
||||||
let mean = sum / this.frames.length;
|
|
||||||
|
|
||||||
// Render the statistics.
|
|
||||||
this.fps.textContent = `
|
|
||||||
Frames per Second:
|
|
||||||
latest = ${Math.round(fps)} //
|
|
||||||
avg of last 100 = ${Math.round(mean)} //
|
|
||||||
min of last 100 = ${Math.round(min)} //
|
|
||||||
max of last 100 = ${Math.round(max)}
|
|
||||||
`.trim();
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user