Last active
August 10, 2020 18:50
-
-
Save fredrikdev/90409cea932f628c70733e7b7b17e0bd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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