Created
July 12, 2014 06:46
-
-
Save apsillers/eacf6101cc829e36138b to your computer and use it in GitHub Desktop.
JavaScript BftPD sample cell
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
/* | |
Sample code for a "Battle for the Petri Dish" cell | |
Released under the terms of the WTF Public License, | |
No warranty express or implied is granted, etc, etc. | |
I just hacked this together very quickly; improvements are welcome, so please fork the Gist if you like. | |
*/ | |
// used in defining cell spec | |
var MAX_HP = 2; | |
var MAX_ENERGY = 10; | |
var ACIDITY = 0; | |
/* | |
The decide function takes an Arena object (see below for prototype methods), a cell object, | |
and an outputCallback, which accepts a command string to output | |
*/ | |
function decide(arena, cell, outputCallback) { | |
var nearbyEmpties = arena.getAdjacentMatches(cell.point, [".", "c"]); | |
var nearbyEnemies = arena.getAdjacentMatches(cell.point, ["x"]); | |
var nearbyCorpses = arena.getAdjacentMatches(cell.point, ["c"]); | |
// if you have the energy and space to divide, do it | |
if(cell.energy >= 5 && nearbyEmpties.length > 0) { | |
outputCallback("DIVIDE " + arena.getDirection(cell, nearbyEmpties[(nearbyEmpties.length*Math.random())|0])); | |
return; | |
} | |
// if you have two or more nearby enemies, explode if possible | |
if(nearbyEnemies.length > 1 && cell.energy >= cell.hp && cell.hp <= 3) { | |
outputCallback("EXPLODE"); | |
return; | |
} | |
// if at least one adjacent enemy, attack if possible | |
if(cell.energy > 0 && nearbyEnemies.length > 0) { | |
outputCallback("ATTACK " + arena.getDirection(cell, nearbyEnemies[(nearbyEnemies.length*Math.random())|0]) + " " + Math.min(cell.energy, 3)); | |
return; | |
} | |
// if there's a nearby corpse, eat it if your energy is below max | |
if(nearbyCorpses.length > 0) { | |
outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length*Math.random())|0])); | |
return; | |
} | |
for(var i=0; i<nearbyEmpties.length; ++i) { | |
var space = nearbyEmpties[i]; | |
if(arena.getAdjacentMatches(space, ["x"]).length) { | |
outputCallback("MOVE " + arena.getDirection(cell,space)); | |
return; | |
} | |
} | |
outputCallback("REST"); | |
return; | |
} | |
var input = ""; | |
// quiet stdin EPIPE errors | |
process.stdin.on("error", function(err) { | |
//console.log("slight error: " + err); | |
}); | |
process.stdin.on("data", function(data) { | |
input += data; | |
}); | |
process.stdin.on("end", function() { | |
if(input == "BEGIN") { | |
// output space-separated attributes | |
process.stdout.write([MAX_HP, MAX_ENERGY, ACIDITY].join(" ")); | |
} else { | |
// read in arena and decide on an action | |
var arena = new Arena(); | |
var lines = input.split("\n"); | |
var dimensions = lines[0].split(" ").map(function(d) { return parseInt(d); }); | |
arena.width = dimensions[0]; | |
arena.height = dimensions[1]; | |
for(var y=1; y<=dimensions[1]; ++y) { | |
for(var x=0; x<lines[y].length; ++x) { | |
arena.set(x, y-1, lines[y][x]); | |
} | |
} | |
var stats = lines[dimensions[1]+2].split(" "); | |
var cell = { x: stats[0], y: stats[1], hp: stats[2], energy: stats[3], point: arena.get(stats[0], stats[1]) }; | |
// decide on an action and write the action to stdout | |
decide(arena, cell, function(output) { process.stdout.write(output); }) | |
} | |
}); | |
var Arena = function() { | |
this.dict = {}; | |
}; | |
Arena.prototype = { | |
// get Point object | |
get: function(x,y) { | |
return this.dict[x+","+y]; | |
}, | |
// store Point object | |
set: function(x,y,d) { | |
this.dict[x+","+y] = new Point(x,y,d); | |
}, | |
// get an array of all Points adjacent to this one whose symbol is contained in matchList | |
// if matchList is omitted, return all Points | |
getAdjacentMatches: function(point, matchList) { | |
var result = []; | |
for(var i=-1; i<=1; ++i) { | |
for(var j=-1; j<=1; ++j) { | |
var inspectedPoint = this.get(point.x+i, point.y+j); | |
if(inspectedPoint && | |
(i!=0 || j!=0) && | |
(!matchList || matchList.indexOf(inspectedPoint.symbol) != -1)) { | |
result.push(inspectedPoint); | |
} | |
} | |
} | |
return result; | |
}, | |
// return the direction from point1 to point2 | |
getDirection: function(point1, point2) { | |
var dx = point2.x - point1.x; | |
var dy = point2.y - point1.y; | |
dx = Math.abs(dx) / (dx || 1); | |
dy = Math.abs(dy) / (dy || 1); | |
c2d = { "0,0":"-", | |
"0,-1":"N", "0,1":"S", "1,0":"E", "-1,0":"W", | |
"-1,-1":"NW", "1,-1":"NE", "1,1":"SE", "-1,1":"SW" }; | |
return c2d[dx + "," + dy]; | |
} | |
} | |
var Point = function(x,y,d) { | |
this.x = x; | |
this.y = y; | |
this.symbol = d; | |
} | |
Point.prototype.toString = function() { | |
return "(" + this.x + ", " + this.y + ")"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment