A DOM-Based Snakes Game, Part I: The Code
A DOM-Based Snakes Game, Part I (7)
The Code
<HTML>
<HEAD>
<TITLE>Snakes</TITLE>
<SCRIPT LANGUAGE = "JavaScript">
<!--
var maxRowCount = 13;
var maxColumnCount = 23;
var rightBorder = maxColumnCount - 2;
var leftBorder = 0;
var topBorder = 0;
var bottomBorder = maxRowCount - 1;
var bottomLine = bottomBorder - 1;
var borderGif = "border.gif";
var coreGif = "back.gif";
var snakeGif = "snake.gif";
var targetGif = "apple.gif";
var snakeInitialLength = maxRowCount - 5;
var snakeRowPos = new Array(maxRowCount * maxColumnCount);
var snakeColumnPos = new Array(maxRowCount * maxColumnCount);
var snakeLength;
var headColumnChange;
var headRowChange;
var targetRow;
var tagetColumn;
var afterTailRow, afterTailColumn;
var interval = 50;
var score;
var asciiU = 85;
var asciiu = 117;
var asciiD = 78;
var asciid = 110;
var asciiR = 75;
var asciir = 107;
var asciiL = 72;
var asciil = 104;
var ascii8 = 56;
var ascii2 = 50;
var ascii4 = 52;
var ascii6 = 54;
function Is ()
{ // convert all characters to lowercase to simplify testing
var agt=navigator.userAgent.toLowerCase();
// *** BROWSER VERSION ***
// Note: On IE5, these return 4, so use is.ie5up to detect IE5.
this.major = parseInt(navigator.appVersion);
this.minor = parseFloat(navigator.appVersion);
this.nav = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
&& (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
&& (agt.indexOf('webtv')==-1));
this.nav2 = (this.nav && (this.major == 2));
this.nav3 = (this.nav && (this.major == 3));
this.nav4 = (this.nav && (this.major == 4));
this.nav4up = (this.nav && (this.major >= 4));
this.navonly = (this.nav && ((agt.indexOf(";nav") != -1) ||
(agt.indexOf("; nav") != -1)) );
this.nav5 = (this.nav && (this.major == 5));
this.nav5up = (this.nav && (this.major >= 5));
this.ie = (agt.indexOf("msie") != -1);
this.ie3 = (this.ie && (this.major < 4));
this.ie4 = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.")==-1) );
this.ie4up = (this.ie && (this.major >= 4));
this.ie5 = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.")!=-1) );
this.ie5up = (this.ie && !this.ie3 && !this.ie4);
// KNOWN BUG: On AOL4, returns false if IE3 is embedded browser
// or if this is the first browser window opened. Thus the
// properties is.aol, is.aol3, and is.aol4 aren't 100% reliable.
this.aol = (agt.indexOf("aol") != -1);
this.aol3 = (this.aol && this.ie3);
this.aol4 = (this.aol && this.ie4);
this.opera = (agt.indexOf("opera") != -1);
this.webtv = (agt.indexOf("webtv") != -1);
// *** JAVASCRIPT VERSION CHECK ***
if (this.nav2 || this.ie3) this.js = 1.0
else if (this.nav3 || this.opera) this.js = 1.1
else if ((this.nav4 && (this.minor <= 4.05)) || this.ie4) this.js = 1.2
else if ((this.nav4 && (this.minor > 4.05)) || this.ie5) this.js = 1.3
else if (this.nav5) this.js = 1.4
// NOTE: In the future, update this code when newer versions of JS
// are released. For now, we try to provide some upward compatibility
// so that future versions of Nav and IE will show they are at
// *least* JS 1.x capable. Always check for JS version compatibility
// with > or >=.
else if (this.nav && (this.major > 5)) this.js = 1.4
else if (this.ie && (this.major > 5)) this.js = 1.3
// HACK: no idea for other browsers; always check for JS version with > or >=
else this.js = 0.0;
// *** PLATFORM ***
this.win = ( (agt.indexOf("win")!=-1) || (agt.indexOf("16bit")!=-1) );
// NOTE: On Opera 3.0, the userAgent string includes "Windows 95/NT4" on all
// Win32, so you can't distinguish between Win95 and WinNT.
this.win95 = ((agt.indexOf("win95")!=-1) || (agt.indexOf("windows 95")!=-1));
// is this a 16 bit compiled version?
this.win16 = ((agt.indexOf("win16")!=-1) ||
(agt.indexOf("16bit")!=-1) || (agt.indexOf("windows 3.1")!=-1) ||
(agt.indexOf("windows 16-bit")!=-1) );
this.win31 = ((agt.indexOf("windows 3.1")!=-1) || (agt.indexOf("win16")!=-1) ||
(agt.indexOf("windows 16-bit")!=-1));
// NOTE: Reliable detection of Win98 may not be possible. It appears that:
// - On Nav 4.x and before you'll get plain "Windows" in userAgent.
// - On Mercury client, the 32-bit version will return "Win98", but
// the 16-bit version running on Win98 will still return "Win95".
this.win98 = ((agt.indexOf("win98")!=-1) || (agt.indexOf("windows 98")!=-1));
this.winnt = ((agt.indexOf("winnt")!=-1) || (agt.indexOf("windows nt")!=-1));
this.win32 = ( this.win95 || this.winnt || this.win98 ||
((this.major >= 4) && (navigator.platform == "Win32")) ||
(agt.indexOf("win32")!=-1) || (agt.indexOf("32bit")!=-1) );
this.os2 = ((agt.indexOf("os/2")!=-1) ||
(navigator.appVersion.indexOf("OS/2")!=-1) ||
(agt.indexOf("ibm-webexplorer")!=-1));
this.mac = (agt.indexOf("mac")!=-1);
this.mac68k = (this.mac && ((agt.indexOf("68k")!=-1) ||
(agt.indexOf("68000")!=-1)));
this.macppc = (this.mac && ((agt.indexOf("ppc")!=-1) ||
(agt.indexOf("powerpc")!=-1)));
this.sun = (agt.indexOf("sunos")!=-1);
this.sun4 = (agt.indexOf("sunos 4")!=-1);
this.sun5 = (agt.indexOf("sunos 5")!=-1);
this.suni86= (this.sun && (agt.indexOf("i86")!=-1));
this.irix = (agt.indexOf("irix") !=-1); // SGI
this.irix5 = (agt.indexOf("irix 5") !=-1);
this.irix6 = ((agt.indexOf("irix 6") !=-1) || (agt.indexOf("irix6") !=-1));
this.hpux = (agt.indexOf("hp-ux")!=-1);
this.hpux9 = (this.hpux && (agt.indexOf("09.")!=-1));
this.hpux10= (this.hpux && (agt.indexOf("10.")!=-1));
this.aix = (agt.indexOf("aix") !=-1); // IBM
this.aix1 = (agt.indexOf("aix 1") !=-1);
this.aix2 = (agt.indexOf("aix 2") !=-1);
this.aix3 = (agt.indexOf("aix 3") !=-1);
this.aix4 = (agt.indexOf("aix 4") !=-1);
this.linux = (agt.indexOf("inux")!=-1);
this.sco = (agt.indexOf("sco")!=-1) || (agt.indexOf("unix_sv")!=-1);
this.unixware = (agt.indexOf("unix_system_v")!=-1);
this.mpras = (agt.indexOf("ncr")!=-1);
this.reliant = (agt.indexOf("reliantunix")!=-1);
this.dec = ((agt.indexOf("dec")!=-1) || (agt.indexOf("osf1")!=-1) ||
(agt.indexOf("dec_alpha")!=-1) || (agt.indexOf("alphaserver")!=-1) ||
(agt.indexOf("ultrix")!=-1) || (agt.indexOf("alphastation")!=-1));
this.sinix = (agt.indexOf("sinix")!=-1);
this.freebsd = (agt.indexOf("freebsd")!=-1);
this.bsd = (agt.indexOf("bsd")!=-1);
this.unix = ((agt.indexOf("x11")!=-1) || this.sun || this.irix || this.hpux ||
this.sco ||this.unixware || this.mpras || this.reliant ||
this.dec || this.sinix || this.aix || this.linux || this.bsd ||
this.freebsd);
this.vms = ((agt.indexOf("vax")!=-1) || (agt.indexOf("openvms")!=-1));
}
function loadPicture() {
loadBoardCore();
loadRow(0, borderGif);
loadRow(maxRowCount - 1, borderGif);
loadColumn(0, borderGif);
loadColumn(maxColumnCount - 2, borderGif);
}
function loadRow(rowNum, gif) {
for(var i = 0; i < maxColumnCount - 1; i++) {
divNode.childNodes[domIndex(i, rowNum)].src = gif;
}
}
function loadColumn(colNum, gif) {
for(var i = 0; i < maxRowCount; i++) {
divNode.childNodes[domIndex(colNum, i)].src = gif;
}
}
function loadBoardCore() {
for(var i = 0; i < maxRowCount; i++) {
loadRow(i, coreGif);
}
}
function domIndex(x, y) {
return (y * maxColumnCount + x);
}
function loadSnake() {
var snakeRow = maxRowCount - 2;
for(var i = 1; i <= snakeInitialLength; i++) {
divNode.childNodes[domIndex(i, snakeRow)].src = snakeGif;
snakeRowPos[snakeInitialLength + 1 - i] = snakeRow;
snakeColumnPos[snakeInitialLength + 1 - i] = i;
}
snakeLength = snakeInitialLength;
headColumnChange = 1;
headRowChange = 0;
}
function loadTarget() {
while(true) {
targetColumn = 1 + Math.round(Math.random() * (maxColumnCount - 4));
targetRow = 1 + Math.round(Math.random() * (maxRowCount - 3))
if (!(targetOverlapsSnake())) break;
}
divNode.childNodes[domIndex(targetColumn, targetRow)].src = targetGif;
}
function targetOverlapsSnake() {
for (var i = 1; i <= snakeLength; i++) {
if (targetRow == snakeRowPos[i] && targetColumn == snakeColumnPos[i]) return true;
}
return false;
}
function moveOne() {
if (processBorderCases()) restartGame();
var tailRowPos = snakeRowPos[snakeLength];
var tailColumnPos = snakeColumnPos[snakeLength];
updateSnake();
var headRowPos = snakeRowPos[1];
var headColumnPos = snakeColumnPos[1];
divNode.childNodes[domIndex(headColumnPos, headRowPos)].src = snakeGif;
divNode.childNodes[domIndex(tailColumnPos, tailRowPos)].src = coreGif;
}
function startGame() {
score = 0;
window.status = "Turn with (u, h, n, k) or with NumLock (8, 4, 2, 6); Your score is " + score;
loadSnake();
loadTarget();
}
function restartGame() {
for (var i = 1; i <= snakeLength; i++) {
replaceObj = divNode.childNodes[domIndex(snakeColumnPos[i], snakeRowPos[i])];
replaceObj.src = coreGif;
}
replaceObj = divNode.childNodes[domIndex(targetColumn, targetRow)]
replaceObj.src = coreGif;
startGame();
}
function updateSnake() {
afterTailRow = snakeRowPos[snakeLength];
afterTailColumn = snakeColumnPos[snakeLength];
for(var i = snakeLength; i >= 2; i--) {
snakeRowPos[i] = snakeRowPos[i - 1];
snakeColumnPos[i] = snakeColumnPos[i - 1]
}
snakeRowPos[1] += headRowChange;
snakeColumnPos[1] += headColumnChange;
}
function processBorderCases() {
processTargetCollision();
if (processBorderCollision()) return true;
if (processSelfCollision()) return true;
}
function processTargetCollision() {
if (snakeRowPos[1] == targetRow && snakeColumnPos[1] == targetColumn){
snakeLength++;
snakeRowPos[snakeLength] = afterTailRow;
snakeColumnPos[snakeLength] = afterTailColumn;
loadTarget();
divNode.childNodes[domIndex(afterTailColumn, afterTailRow)].src = snakeGif;
score++;
window.status = "Turn with (u, h, n, k) or with NumLock (8, 4, 2, 6); Your score is " + score;
}
}
function processBorderCollision() {
if (snakeRowPos[1] + headRowChange == topBorder || snakeRowPos[1] + headRowChange == bottomBorder || snakeColumnPos[1] + headColumnChange == leftBorder || snakeColumnPos[1] + headColumnChange == rightBorder) return true;
return false;
}
function processSelfCollision() {
for (var i = 2; i <= snakeLength; i++) {
if (snakeRowPos[1] == snakeRowPos[i] && snakeColumnPos[1] == snakeColumnPos[i]) return true;
}
return false;
}
function handleClick() {
if (window.event.keyCode == asciiU || window.event.keyCode == asciiu || window.event.keyCode == ascii8) {
if (headRowChange == 0) {
headRowChange = -1;
headColumnChange = 0;
}
}
else if (window.event.keyCode == asciiD || window.event.keyCode == asciid || window.event.keyCode == ascii2) {
if (headRowChange == 0) {
headRowChange = 1;
headColumnChange = 0;
}
}
else if (window.event.keyCode == asciiR || window.event.keyCode == asciir || window.event.keyCode == ascii6) {
if (headColumnChange == 0) {
headRowChange = 0;
headColumnChange = 1;
}
}
else if (window.event.keyCode == asciiL || window.event.keyCode == asciil || window.event.keyCode == ascii4) {
if (headColumnChange == 0) {
headRowChange = 0;
headColumnChange = -1;
}
}
}
function addOneRow() {
for (var i = 0; i < maxColumnCount -1; i++) {
tempSquareNode = squareNode.cloneNode();
divNode.appendChild(tempSquareNode);
}
}
function addBr() {
tempBrNode = brNode.cloneNode();
divNode.appendChild(tempBrNode);
}
function buildBoard() {
for(var i = 0; i < maxRowCount; i++) {
addOneRow();
addBr();
}
}
function displayGreeting() {
alert("Welcome to the Snakes game.\n" +
"You get one point every time you eat the target.\n" +
"Watch your score on the status line.\n" +
"Turn up with the u character or with NumLock(8).\n" +
"Turn down with the n character or with NumLock(2).\n" +
"Turn right with the k character or with NumLock(6).\n" +
"Turn left with the h character or with NumLock(4).");
}
// -->
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#ffffff" onkeypress=handleClick() ID="bodyNode">
<SCRIPT LANGUAGE = "JavaScript">
<!--
var is;
is = new Is();
if (!(is.ie5up || is.nav5up)) alert("Snakes is supported by Version 5 and up only")
else {
var divNode = document.createElement("DIV");
divNode.align = "center";
bodyNode.appendChild(divNode);
var squareNode = document.createElement("IMG");
var brNode = document.createElement("BR");
buildBoard();
loadPicture();
displayGreeting();
startGame();
setInterval("moveOne()", interval);
}
// -->
</SCRIPT>
</BODY>
</HTML>
Produced by Yehuda Shiran and Tomer Shiran
Created: August 16, 1999
Revised: March 7, 2000
URL: https://www.webreference.com/js/column46/code1.html