Gold Collector KoTH
BaitBot - JavaScript Node.JS
Why bother chasing or running if you can never catch? Instead, BaitBot finds the nearest coin and waits for a weaker bot to also approach it. When they're both adjacent, baitBot goes for the coin assuming the weaker bot will as well. If baitBot is waiting and a stronger bot approaches, he just grabs the coin and skedaddles. Try Me!
function baitBot(me, others, coins) {
let directions = ['none','east','south','west','north']
function distanceTo(a) {
return (Math.abs(a[0] - me.locationX) + Math.abs(a[1] - me.locationY))
}
function distanceBetween(a, b){
return (Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]))
}
function adjacentDir(a) {
//0 = no, 1,2,3,4 = ESWN
if(distanceTo(a) == 1) {
if(a[0] > me.locationX){ return 1}
else if(a[0] < me.locationX) {return 3}
else if(a[1] > me.locationY) {return 2}
else{ return 4}
}
else {return 0}
}
function edibility(a) {
return me.coins - a[2]
}
//Find nearest coin and get next to it
let closestCoin = coins.sort((a,b) => distanceTo(a) - distanceTo(b))[0]
if(distanceTo(closestCoin) > 1) {
if(closestCoin[0] > me.locationX){ return 'east'}
else if(closestCoin[0] < me.locationX){ return 'west'}
else if(closestCoin[1] < me.locationY){ return 'north'}
else if(closestCoin[1] > me.locationY){ return 'south'}
}
//If we're next to a coin and there's a threat close, just grab it
let nearestThreat = others.filter(a => edibility(a) < 0).sort((a,b) => distanceBetween(a, closestCoin) - distanceBetween(b, closestCoin))[0]
if(nearestThreat && distanceBetween(nearestThreat, closestCoin) <= 2) {
return directions[adjacentDir(closestCoin)]
}
//Otherwise, wait until there's a target also next to the coin. If none are close, just take it
let targets = others.filter(a => edibility(a) > 0 && distanceBetween(closestCoin, a) <= 3)
targets.sort((a,b) => distanceBetween(a, closestCoin) - distanceBetween(b, closestCoin))
if(targets.length > 0 && distanceBetween(targets[0], closestCoin) > 1){
return directions[0]
}
return directions[adjacentDir(closestCoin)]
}
Big King Little Hill | JavaScript
function BigKingLittleHill(me, enemies, coins) {
// Is a move safe to execute?
function isItSafe(x){
let loc = [x[0] + me.locationX,x[1] + me.locationY];
return loc[0] >= 0 && loc[0] < me.arenaLength
&& loc[1] >= 0 && loc[1] < me.arenaLength
&& enemies
.filter(enemy => me.coins <= enemy[2])
.filter(enemy => getDist(enemy,loc) == 1).length === 0;
}
// Dumb conversion of relative coord to direction string
function coordToString(coord){
if (coord[0] == 0 && coord[1] == 0) return 'none';
if (Math.abs(coord[0]) > Math.abs(coord[1]))
return coord[0] < 0 ? 'west' : 'east';
return coord[1] < 0 ? 'north' : 'south';
}
// Calculate a square's zone of control
function getZOC(x) {
let n = 0;
for(let i = 0; i < me.arenaLength;i++){
for(let j = 0; j < me.arenaLength;j++){
if (doesAControlB(x, [i,j])) n++;
}
}
return n;
}
function doesAControlB(a, b) {
return getEnemyDist(b) > getDist(a, b);
}
// Distance to nearest enemy
function getEnemyDist(x) {
return enemies.filter(enemy => enemy[2] >= me.coins/50).map(enemy => getWeightedDist(enemy, x)).reduce((accumulator, current) => Math.min(accumulator, current));
}
// Weights distance by whether enemy is weaker or stronger
function getWeightedDist(enemy, pos) {
return getDist(enemy, pos) + (enemy[2] < me.coins ? 1 : 0);
}
function getDist(a, b){
return (Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]))
}
//check whether there are coins in our Zone of Control, if yes move towards the closest one
let loc = [me.locationX,me.locationY];
let sortedCoins = coins.sort((a,b) => getDist(loc,a) - getDist(loc,b));
for (let coin of sortedCoins) {
if (doesAControlB(loc,coin)){
return coordToString([coin[0] - loc[0],coin[1] - loc[1]]);
}
}
//sort moves by how they increase our Zone of Control
northZOC = [[0,-1], getZOC([loc[0],loc[1]-1])];
southZOC = [[0,1], getZOC([loc[0],loc[1]+1])];
westZOC = [[-1,0], getZOC([loc[0]-1,loc[1]])];
eastZOC = [[1,0], getZOC([loc[0]+1,loc[1]])];
noneZOC = [[0,0], getZOC([loc[0],loc[1]])];
let moves = [northZOC,southZOC,westZOC,eastZOC,noneZOC].sort((a,b) => b[1] - a[1]);
//check whether these moves are safe and make the highest priority safe move
for (let move of moves) {
if (isItSafe(move[0])) {
return coordToString(move[0]);
}
}
//no moves are safe (uh oh!), return the highest priority
return coordToString(moves[0][0])
}
Try it online!
Big King Little Hill makes decisions based on “zones of control”. It will only pursue coins that are in its zone of control meaning it can reach the coin before any other bot can. When there are no coins in its zone of control it instead moves to maximize the size of its zone of control. Big King Little Hill calculates the zone of control of each of its 5 possible moves and favors the moves that maximize the size of its zone of control. In this way, Big King Little Hill eventually reaches a local maximum (little hill) of control and waits for a coin to be generated within its zone. Additionally, Big King Little Hill rejects any move that could result in its death unless there are no alternatives.
Big King Little Hill is a pessimist (it prefers the term realist) because it does not bother to contest any coin it isn't assured to get. It is also a pacifist in that it does not pursue weaker bots in any sense (although it might step on one if they get in the way). Lastly, Big King Little Hill is a coward that will not jeopardize its own life for any reward unless it absolutely has to.
First Gen Learning Algorithm | JavaScript (Node.js)
function run() {
return ['north','east','south','west'][(Math.random()*4)|0];
}
Try it online!
Have you ever seen those timelapses of learning algorithms learning to play a game? They often move almost randomly in the first few generations...