Skip to content

Instantly share code, notes, and snippets.

@fredrikdev
Last active August 10, 2020 18:50
Show Gist options
  • Save fredrikdev/90409cea932f628c70733e7b7b17e0bd to your computer and use it in GitHub Desktop.
Save fredrikdev/90409cea932f628c70733e7b7b17e0bd to your computer and use it in GitHub Desktop.
// Non-canvas Worms Game. Copyright (c) by Fredrik Johansson 2020. fredrik@johanssonrobotics.com.
class WormGame {
constructor(el, grid) {
let t = this;
t.el = el;
t.config = {
grid: { ...{
size: 18,
spacing: 0,
width: 30,
height: 20
}, ...grid
}
}
t.config.bounds = {
w: (t.config.grid.spacing+t.config.grid.size) * t.config.grid.width + t.config.grid.spacing,
h: (t.config.grid.spacing+t.config.grid.size) * t.config.grid.height + t.config.grid.spacing
}
}
run() {
// init
let t = this;
t.log("Loading. Copyright (c) by Fredrik Johansson 2019 (fredrik@johanssonrobotics.com)");
// add bounds
let boundsDiv = document.createElement("div");
boundsDiv.setAttribute("style", `position: relative; background: #eee; border: 0px; width: ${t.config.bounds.w}px; height: ${t.config.bounds.h}px; padding: 0; margin: 0; box-sizing: border-box;`);
t.el.appendChild(boundsDiv);
// draw feedback row
let feedbackDiv = document.createElement("div");
feedbackDiv.setAttribute("style", `position: relative; background: #eee; border: 0px; width: ${t.config.bounds.w}px; text-align: center; border-top: 1px solid #fff; padding: 4px; margin: 0; box-sizing: border-box;`);
feedbackDiv.innerHTML = "Click window, then use arrow keys ← ↑ → ↓ to play!";
t.el.appendChild(feedbackDiv);
// dots, and the current length
// and the current goal to grab
let dots = [], dotsmax, goal;
// current position, moved in direction by factor of speed
// speed: msec between each new dot. for dots/second use e.g. 1000/n
// direction that we want to go and last direction drawn
let x, y, speed;
let direction, directionLast, ispaused;
// time when the last dot was painted
let timeSinceLastDraw;
function updateFeedback() {
if (ispaused) {
feedbackDiv.innerHTML = "Paused! Press SPACE to continue, or R to restart.";
} else if (direction == null) {
feedbackDiv.innerHTML = "Game Over. " + (goal.value > 1 ? "Score: " + (goal.value-1) + ". " : "") + "Press any key to restart!";
} else {
feedbackDiv.innerHTML = (goal.value > 1 ? "Score: " + (goal.value-1) + ". " : "Play! ") + "Press SPACE to pause, or R to restart.";
}
}
function reset() {
t.log("Reset");
// reset worm
while (dots.length > 0)
boundsDiv.removeChild(dots.shift().element);
// reset goal
if (goal)
boundsDiv.removeChild(goal.element);
goal = null;
drawgoal();
// reset variables
dotsmax = 10;
speed = 1000/(dotsmax/1.5);
x = (t.config.grid.spacing);
y = (t.config.grid.spacing)+(t.config.grid.spacing+t.config.grid.size)*Math.floor((t.config.grid.height-1)/2);
timeSinceLastDraw = null;
direction = directionLast = "ArrowRight";
ispaused = false;
drawdot();
}
function drawgoal() {
function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
// get goal x and goal y; but not on worm
let gx, gy;
while (true) {
gx = t.config.grid.spacing+(t.config.grid.spacing+t.config.grid.size)*randomIntFromInterval(0,t.config.grid.width-1);
gy = t.config.grid.spacing+(t.config.grid.spacing+t.config.grid.size)*randomIntFromInterval(0,t.config.grid.height-1);
if (dots.filter(i => i.x == gx && i.y == gy).length == 0)
break;
}
let d = document.createElement("div");
if (!goal) {
// create initial
goal = { element: d, x: gx, y: gy, value: 1 };
} else {
// create next
boundsDiv.removeChild(goal.element);
goal = { element: d, x: gx, y: gy, value: goal.value + 1 };
dotsmax++;
speed = 1000/(dotsmax/2);
}
d.setAttribute("style", `position: absolute; font-family: monospace; font-size: 14px; text-align: center; background: white; width: ${t.config.grid.size}px; height: ${t.config.grid.size}px; left: ${gx}px; top: ${gy}px; padding: 0; margin: 0`);
d.innerText = goal.value;
boundsDiv.appendChild(d);
}
// draw dot
function drawdot() {
let result = true;
// remove any dots that overflow our maximum number of dots
while (dots.length >= dotsmax)
boundsDiv.removeChild(dots.shift().element);
// draw new dot
let d = document.createElement("div");
d.setAttribute("style", `position: absolute; background: rgba(27,44,45,1); width: ${t.config.grid.size}px; height: ${t.config.grid.size}px; left: ${x}px; top: ${y}px; padding: 0; margin: 0`);
boundsDiv.appendChild(d);
if (goal.x == x && goal.y == y) {
// drawing on goal - great!
t.log("Got #" + goal.value);
drawgoal();
updateFeedback();
}
if (dots.filter(i => i.x == x && i.y == y).length > 0) {
// check if we already have dot at the position
result = false;
d.style.background = "red";
} else {
// change color of the dots
dots.forEach(function(item, index) {
item.element.style.backgroundColor = "rgba(27,44,45," + ((index + 1) / dots.length) + ")"
});
}
dots.push({ element: d, x, y });
return result;
}
// hook keys
document.addEventListener('keydown', function(e) {
e.stopPropagation();
e.preventDefault();
if (direction == null) {
reset();
updateFeedback();
return;
}
if (e.code == 'KeyR') {
reset();
}
if (e.code == 'Space') {
if (!ispaused) {
ispaused = true;
updateFeedback();
t.log("Paused");
} else {
ispaused = false;
timeSinceLastDraw = null;
updateFeedback();
t.log("Resumed");
}
}
if (ispaused)
return;
if (e.code != "ArrowUp" && e.code != "ArrowDown" && e.code != "ArrowLeft" && e.code != "ArrowRight")
return;
if ((e.code == "ArrowUp" && directionLast == "ArrowDown") || (e.code == "ArrowDown" && directionLast == "ArrowUp") || (e.code == "ArrowLeft" && directionLast == "ArrowRight") || (e.code == "ArrowRight" && directionLast == "ArrowLeft"))
return;
updateFeedback();
direction = e.code;
});
// start
reset();
// setup interval to 32fps
setInterval(function() {
if (direction == null || ispaused)
return;
if (timeSinceLastDraw == null)
timeSinceLastDraw = new Date();
// draw dots
while (true) {
let timeElapsedMsec = new Date() - timeSinceLastDraw;
if (timeElapsedMsec < speed) {
break;
}
if (direction == "ArrowUp") {
y -= t.config.grid.size + t.config.grid.spacing;
if (y < t.config.grid.spacing) y = t.config.bounds.h - t.config.grid.size - t.config.grid.spacing;
} else if (direction == "ArrowRight") {
x += t.config.grid.size + t.config.grid.spacing;
if (x >= t.config.bounds.w) x = t.config.grid.spacing;
} else if (direction == "ArrowDown") {
y += t.config.grid.size + t.config.grid.spacing;
if (y >= t.config.bounds.h) y = t.config.grid.spacing;
} else if (direction == "ArrowLeft") {
x -= t.config.grid.size + t.config.grid.spacing;
if (x < t.config.grid.spacing) x = t.config.bounds.w - t.config.grid.size - t.config.grid.spacing;
}
directionLast = direction;
if (!drawdot()) {
// game over
t.log("Game Over")
direction = null;
directionLast = null;
updateFeedback();
break;
} else {
// set timeSinceLastDraw to now - remainder
timeSinceLastDraw = new Date(new Date() - (timeElapsedMsec - speed));
}
}
}, 1000/32);
}
log(message) {
console.log("Fredrik's Worm: " + message);
}
}
new WormGame(
document.getElementsByTagName('body')[0],
{ spacing: 0, width: Math.floor(400/18), height: Math.floor(300/18), size: 18 }
).run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment