Last active
August 22, 2018 13:09
-
-
Save beta-decay/10f026b15c3babd63c004db1f937eb14 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
botData = [ | |
{ | |
"name": "Jack", | |
"func": function (myself, grid, bots, gameInfo) { | |
var col = myself[0]; | |
var myX = myself[1]; | |
var myY = myself[2]; | |
var notPreferred = []; | |
var move = "wait"; | |
if(grid[myX][myY] != col){ | |
var go = checkMove(move, grid[myX][myY]); | |
if(go) { | |
if(go == "notPreferred") { | |
//notPreferred.push(move); | |
} else { | |
nextMove(move, "standard"); | |
return move; | |
} | |
} | |
} | |
move = "left"; | |
if(myX > 0 && grid[myX-1][myY] != col){ | |
var go = checkMove(move, grid[myX-1][myY]); | |
if(go) { | |
if(go == "notPreferred") { | |
notPreferred.push(move); | |
} else { | |
nextMove(move, "standard"); | |
return move; | |
} | |
} | |
} | |
move = "right"; | |
if(myX < grid.length-1 && grid[myX+1][myY] != col){ | |
var go = checkMove(move, grid[myX+1][myY]); | |
if(go) { | |
if(go == "notPreferred") { | |
notPreferred.push(move); | |
} else { | |
nextMove(move, "standard"); | |
return move; | |
} | |
} | |
} | |
move = "up"; | |
if(myY > 0 && grid[myX][myY-1] != col){ | |
var go = checkMove(move, grid[myX][myY-1]); | |
if(go) { | |
if(go == "notPreferred") { | |
notPreferred.push(move); | |
} else { | |
nextMove(move, "standard"); | |
return move; | |
} | |
} | |
} | |
move = "down"; | |
if(myY < grid[0].length && grid[myX][myY+1] != col){ | |
var go = checkMove(move, grid[myX][myY+1]); | |
if(go) { | |
if(go == "notPreferred") { | |
notPreferred.push(move); | |
} else { | |
nextMove(move, "standard"); | |
return move; | |
} | |
} | |
} | |
if(notPreferred[0]) { | |
nextMove(notPreferred[0], "notPreferred"); | |
return notPreferred[0]; | |
} | |
var r = ["up","down","left","right"][Math.random() *4|0]; | |
nextMove(r, "random"); | |
return r; | |
function checkMove(move, currentColor) { | |
var go = false; | |
if(currentColor === 0) { | |
go = true; | |
} else { | |
var z = [col, 0, currentColor][Math.abs(col - currentColor)%3] | |
go = z == 0 ? "notPreferred" : z != currentColor; | |
} | |
if(go) { | |
if(localStorage.nextMoveShouldNotBe && localStorage.nextMoveShouldNotBe == move) { | |
return false; | |
} | |
} | |
return go; | |
} | |
function nextMove(move, message) { | |
var oppositeMove = "wait"; | |
if(move == "left") { | |
oppositeMove = "right"; | |
} else if(move == "right") { | |
oppositeMove = "left"; | |
} else if(move == "up") { | |
oppositeMove = "down"; | |
} else if(move == "down") { | |
oppositeMove = "up"; | |
} | |
localStorage.nextMoveShouldNotBe = oppositeMove; | |
} | |
} | |
}, | |
{ | |
"name": "Fuzzy Guy", | |
"func": function(myself, grid, bots, gameInfo) { | |
var i,j,x,y = 0; | |
this.answerToLifeTheUniverseAndEverything = 42; | |
this.round = gameInfo[0]; | |
this.coloringStruggle = []; | |
this.myColor = myself[0]; | |
this.botCount = bots.length; | |
this.sizeOfGrid = grid.length; | |
this.storageName = 'm53kp1of6igcnpsq'; | |
this.distances = {up: 0, right: 0, down: 0, left: 0}; | |
this.foodSmell = {up: 0, right: 0, down: 0, left: 0}; | |
this.botSmell = {up: 0, right: 0, down: 0, left: 0}; | |
this.botPredictedSmell = {up: 0, right: 0, down: 0, left: 0}; | |
this.directionPoints = {up: 0, right: 0, down: 0, left: 0}; | |
this.blockedMoves = function() { | |
var blocked = []; | |
if(myself[1] == 0) { | |
blocked.push('left'); | |
} | |
if(myself[2] == 0) { | |
blocked.push('up'); | |
} | |
if(myself[1] == this.sizeOfGrid - 1) { | |
blocked.push('right'); | |
} | |
if(myself[2] == this.sizeOfGrid - 1) { | |
blocked.push('down'); | |
} | |
return blocked; | |
} | |
this.getDistance = function(x1,y1) { | |
return [Math.abs(myself[1]-x1), Math.abs(myself[2]-y1)]; | |
} | |
this.finddeliciousDirection = function() { | |
for (x = 0; x < this.sizeOfGrid; x++) { | |
for (y = 0; y < this.sizeOfGrid; y++) { | |
if (y < myself[2]) { | |
this.foodSmell.up+= ((1.9 - this.coloringStruggle[x][y]) / this.getDistance(x, y).reduce((a, b) => a + b, 0)) / 4; | |
} | |
if (y > myself[2]) { | |
this.foodSmell.down+= ((1.9 - this.coloringStruggle[x][y]) / this.getDistance(x, y).reduce((a, b) => a + b, 0)) / 4; | |
} | |
if (x < myself[1]) { | |
this.foodSmell.left+= ((1.9 - this.coloringStruggle[x][y]) / this.getDistance(x, y).reduce((a, b) => a + b, 0)) / 4; | |
} | |
if (x > myself[1]) { | |
this.foodSmell.right+= ((1.9 - this.coloringStruggle[x][y]) / this.getDistance(x, y).reduce((a, b) => a + b, 0)) / 4; | |
} | |
} | |
} | |
} | |
this.predictFuture = function(x0,y0,x1,y1) { | |
var xMovement = x1-x0; | |
var yMovement = y1-y0; | |
var xAfter2Turns = x1 + xMovement * 2; | |
var yAfter2Turns = y1 + yMovement * 2; | |
var hitsWall = [1, 1]; | |
if (xMovement == 1) { | |
hitsWall = [2, 1] | |
} else if (xMovement == -1) { | |
hitsWall = [0, 1] | |
} else if (yMovement == 1) { | |
hitsWall = [1, 2] | |
} else if (yMovement == -1) { | |
hitsWall = [1, 0] | |
} else { | |
hitsWall = [1, 1] | |
} | |
if (xAfter2Turns < 0) { | |
xAfter2Turns = 0; | |
} else if (xAfter2Turns >= this.sizeOfGrid) { | |
xAfter2Turns = this.sizeOfGrid -1; | |
} | |
if (yAfter2Turns < 0) { | |
yAfter2Turns = 0; | |
} else if (yAfter2Turns >= this.sizeOfGrid) { | |
yAfter2Turns = this.sizeOfGrid -1; | |
} | |
return [xAfter2Turns, yAfter2Turns, hitsWall]; | |
} | |
this.findCloseBots = function() { | |
var prevPositions; | |
var currentBot; | |
var future; | |
if (this.round > 1) { | |
prevPositions = JSON.parse(localStorage.getItem(this.storageName)); | |
} | |
for (i = 0; i < bots.length; i++) { | |
if (bots[i][2] < myself[2]) { | |
this.botSmell.up+= 3 / (this.getDistance(bots[i][1], bots[i][2]).reduce((a, b) => a + b, 0)) + 0.333 / this.getDistance(bots[i][1], bots[i][2])[1]; | |
} | |
if (bots[i][2] > myself[2]) { | |
this.botSmell.down+= 3 / (this.getDistance(bots[i][1], bots[i][2]).reduce((a, b) => a + b, 0)) + 0.333 / this.getDistance(bots[i][1], bots[i][2])[1]; | |
} | |
if (bots[i][1] < myself[1]) { | |
this.botSmell.left+= 3 / (this.getDistance(bots[i][1], bots[i][2]).reduce((a, b) => a + b, 0)) + 0.333 / this.getDistance(bots[i][1], bots[i][2])[0]; | |
} | |
if (bots[i][1] > myself[1]) { | |
this.botSmell.right+= 3 / (this.getDistance(bots[i][1], bots[i][2]).reduce((a, b) => a + b, 0)) + 0.333 / this.getDistance(bots[i][1], bots[i][2])[0]; | |
} | |
if (this.round > 1) { | |
currentBot = prevPositions.find(function(element) { | |
return element[0] == bots[i][0]; | |
}); | |
if (currentBot[0] != this.myColor) { | |
future = this.predictFuture(currentBot[1], currentBot[2], bots[i][1], bots[i][2]); | |
if (future[1] < myself[2]) { | |
this.botPredictedSmell.up+= 3.14159 / (this.getDistance(future[0], future[1]).reduce((a, b) => a + b, 0)); | |
} | |
if (future[1] > myself[2]) { | |
this.botPredictedSmell.down+= 3.14159 / (this.getDistance(future[0], future[1]).reduce((a, b) => a + b, 0)); | |
} | |
if (future[0] < myself[1]) { | |
this.botPredictedSmell.left+= 3.14159 / (this.getDistance(future[0], future[1]).reduce((a, b) => a + b, 0)); | |
} | |
if (future[0] > myself[1]) { | |
this.botPredictedSmell.right+= 3.14159 / (this.getDistance(future[0], future[1]).reduce((a, b) => a + b, 0)); | |
} | |
if (future[2][0] == 0) { | |
this.botPredictedSmell.left+=0.314159; | |
} | |
if (future[2][0] == 2) { | |
this.botPredictedSmell.right+=0.314159; | |
} | |
if (future[2][1] == 0) { | |
this.botPredictedSmell.up+=0.314159; | |
} | |
if (future[2][1] == 2) { | |
this.botPredictedSmell.down+=0.314159; | |
} | |
} | |
} | |
} | |
localStorage.setItem(this.storageName, JSON.stringify(bots)); | |
} | |
this.calculateColoringStruggle = function() { | |
for (x = 0; x < this.sizeOfGrid; x++) { | |
var yAxis = []; | |
for (y = 0; y < this.sizeOfGrid; y++) { | |
if (this.myColor == grid[x][y]) { | |
yAxis[y] = 2; | |
} else if (grid[x][y] == 0) { | |
yAxis[y] = 0; | |
} | |
else { | |
yAxis[y] = [0, 1, 2][Math.abs(this.myColor - grid[x][y])%3]; | |
} | |
} | |
this.coloringStruggle.push(yAxis); | |
} | |
} | |
this.getEmptySlotsInDirection = function() { | |
for (x = (myself[1] + 1); x < this.sizeOfGrid; x++) { | |
if (grid[x][myself[2]] == 0) { | |
this.distances.right = (x-myself[1]) * 1.23456789; | |
} else { | |
if (x-myself[1]-1 == 0) { | |
this.distances.right = 0; | |
} | |
break; | |
} | |
} | |
for (y = (myself[2] + 1); y < this.sizeOfGrid; y++) { | |
if (grid[myself[1]][y] == 0) { | |
this.distances.down = (y-myself[2]) * 1.23456789; | |
} else { | |
if (y-myself[2]-1 == 0) { | |
this.distances.down = 0; | |
} | |
break; | |
} | |
} | |
for (x = (myself[1] - 1); x > -1; x--) { | |
if (grid[x][myself[2]] == 0) { | |
this.distances.left = (myself[1]-x) * 1.23456789; | |
} else { | |
if (myself[1]-x-1 == 0) { | |
this.distances.left = 0; | |
} | |
break; | |
} | |
} | |
for (y = (myself[2] - 1); y > -1; y--) { | |
if (grid[myself[1]][y] == 0) { | |
this.distances.up = (myself[2]-y) * 1.23456789; | |
} else { | |
if (myself[2]-y-1 == 0) { | |
this.distances.up = 0; | |
} | |
break; | |
} | |
} | |
} | |
this.getBestDistance = function() { | |
var max = -999, maxDir = 'up'; | |
for (var property in this.distances) { | |
if (this.distances.hasOwnProperty(property)) { | |
this.directionPoints[property] = (this.distances[property] + this.foodSmell[property] - this.botSmell[property] - this.botPredictedSmell[property]); | |
if (this.directionPoints[property] > max && this.blockedMoves().indexOf(property) == -1) { | |
max = this.directionPoints[property]; | |
maxDir = property; | |
} | |
} | |
} | |
return maxDir; | |
}; | |
this.findCloseBots(); | |
this.calculateColoringStruggle(); | |
this.getEmptySlotsInDirection(); | |
this.finddeliciousDirection(); | |
return(this.getBestDistance()); | |
} | |
}, | |
{ | |
"name": "Territorial", | |
"func": function (myself, grid, bots, gameInfo) { | |
const w = 6, h = 6; | |
let my_c = myself[0], my_x = myself[1], my_y = myself[2], size = grid.length, roundnum = gameInfo[0]; | |
let getDistance = function (x1, y1, x2, y2) { | |
return (Math.abs(x1 - x2) + Math.abs(y1 - y2)) + 1; | |
}; | |
let getColorValue = function (color) { | |
if (color === 0) { | |
return my_c; | |
} | |
return [my_c, 0, color][Math.abs(my_c - color) % 3]; | |
}; | |
//Choosing closest corner to defend | |
const topLeft = [0, 0], bottomLeft = [0, size - 1], topRight = [size - 1, 0], bottomRight = [size - 1, size - 1]; | |
var distanceToTopLeft = getDistance(my_x, my_y, topLeft[0], topLeft[1]); | |
var distanceToTopRight = getDistance(my_x, my_y, topRight[0], topRight[1]); | |
var distanceToBottomLeft = getDistance(my_x, my_y, bottomLeft[0], bottomLeft[1]); | |
var distanceToBottomRight = getDistance(my_x, my_y, bottomRight[0], bottomRight[1]); | |
var nearestCorner = Math.min(distanceToTopLeft, distanceToTopRight, distanceToBottomLeft, distanceToBottomRight); | |
//And save it | |
if (!localStorage.territorial) { | |
if (nearestCorner === distanceToTopLeft) { | |
//console.log('nearest corner is: topLeft'); | |
var offset_x = topLeft[0]; | |
var offset_y = topLeft[1]; | |
var innermostCorner_x = topLeft[0] + w - 1; | |
var innermostCorner_y = topLeft[1] + h - 1; | |
} else if (nearestCorner === distanceToTopRight) { | |
//console.log('nearest corner is: topRight'); | |
var offset_x = topRight[0] - (w - 1); | |
var offset_y = topRight[1]; | |
var innermostCorner_x = offset_x; | |
var innermostCorner_y = topRight[1] + h - 1; | |
} else if (nearestCorner === distanceToBottomLeft) { | |
//console.log('nearest corner is: bottomLeft'); | |
var offset_x = bottomLeft[0]; | |
var offset_y = bottomLeft[1] - (h - 1); | |
var innermostCorner_x = bottomLeft[0] + w - 1; | |
var innermostCorner_y = offset_y; | |
} else if (nearestCorner === distanceToBottomRight) { | |
//console.log('nearest corner is: bottomRight'); | |
var offset_x = bottomRight[0] - (w - 1); | |
var offset_y = bottomRight[1] - (h - 1); | |
var innermostCorner_x = offset_x; | |
var innermostCorner_y = offset_y; | |
} | |
localStorage.territorial = JSON.stringify([offset_x, offset_y, innermostCorner_x, innermostCorner_y]); | |
} | |
offsets = JSON.parse(localStorage.territorial); | |
offset_x = offsets[0]; | |
offset_y = offsets[1]; | |
innermostCorner_x = offsets[2]; | |
innermostCorner_y = offsets[3]; | |
let targets = []; | |
for (let grid_x = offset_x; grid_x < offset_x + w; grid_x++) | |
{ | |
for (let grid_y = offset_y; grid_y < offset_y + h; grid_y++) | |
{ | |
if (grid[grid_x][grid_y] !== my_c && getColorValue(grid[grid_x][grid_y]) !== grid[grid_x][grid_y]) | |
{ | |
targets.push([grid_x, grid_y]); | |
} | |
} | |
} | |
let target = targets.pop(); | |
//If territory is safe, move to border nearest boardCenter | |
if (target === undefined) { | |
targets.push([innermostCorner_x, innermostCorner_y]); | |
target = targets.pop(); | |
} | |
if (target === undefined) | |
return 'wait'; | |
if (target[0] > my_x) | |
return 'right'; | |
if (target[0] < my_x) | |
return 'left'; | |
if (target[1] > my_y) | |
return 'down'; | |
if (target[1] < my_y) | |
return 'up'; | |
return "wait"; | |
} | |
}, | |
{ | |
"name": "Bob", | |
"func": function(myself, grid, bots, gameInfo) { | |
var [mc, mx, my] = myself; | |
var output; | |
var allowRetracing = false; | |
var size = 3; | |
var scoreboard = grid.map(column=>column.map(c=>c==mc? 0 : overMap(c, 2, 1, 0))); | |
for (let [bc, bx, by] of bots) if (bc != mc) {log([bc,bx,by],[mc,mx,my]); | |
scoreboard[bx][by] = -100; | |
if (inbounds([bx-2, by])) scoreboard[bx-2][by] = -50; | |
if (inbounds([bx+2, by])) scoreboard[bx+2][by] = -50; | |
if (inbounds([bx, by-2])) scoreboard[bx][by-2] = -50; | |
if (inbounds([bx, by+2])) scoreboard[bx][by+2] = -50; | |
} | |
function scoreOf (x, y) { | |
let score = 0; | |
for (let dx = -size; dx <= size; dx++) { | |
let cx = dx + x; | |
if (cx < 1 || cx >= grid.length-1) continue; | |
for (let dy = -size; dy <= size; dy++) { | |
let cy = dy + y; | |
if (cy < 1 || cy >= grid.length-1) continue; | |
score+= scoreboard[cx][cy]; | |
} | |
} | |
return score; | |
} | |
var storage = this; | |
if (gameInfo[0] < 10) this.timer = 10000; | |
function rescore() { | |
storage.bestScore = -Infinity; | |
var blur = scoreboard.map((column, x)=>column.map((c, y) => { | |
let score = scoreOf(x, y); | |
if (score > storage.bestScore) { | |
storage.bestScore = score; | |
storage.bestX = x; | |
storage.bestY = y; | |
} | |
return score; | |
})); | |
storage.atBest = false; | |
storage.timer = 0; | |
log(blur); | |
} | |
if (this.timer > 200) rescore(); | |
if (grid[mx][my] == 0 && !bots.some(([col, bx, by])=> col != mc && bx==mx && by==my)) return "wait"; | |
// annoying localStorage | |
if (!localStorage.dzaima_pastMoves) { | |
pastMoves = ["-1;0"]; | |
nowMoves = new Array(30).fill("-1;0"); | |
} else { | |
pastMoves = JSON.parse(localStorage.dzaima_pastMoves); | |
nowMoves = JSON.parse(localStorage.dzaima_pastMoves); | |
} | |
nowMoves.push(mx+";"+my); | |
nowMoves.shift(); | |
localStorage.dzaima_pastMoves = JSON.stringify(nowMoves); | |
function log(...args) { | |
// console.log(...args); | |
} | |
function over(col) { // 1 if overrides happen, -1 if overrides don't happen, 0 if override = 0 | |
let res = Math.abs(mc-col) % 3; | |
return res==1? 0 : res==0? 1 : -1; | |
} | |
function overMap(col, best, good, bad, mine = good) { // 1 if overrides happen, -1 if overrides don't happen, 0 if override = 0 | |
let res = Math.abs(mc-col) % 3; | |
return col == mc? mine : res==1? good : res==0? best : bad; | |
} | |
var iwin = col=>over(col) == 1; | |
var zeroes = col=>over(col) == 0; | |
function to([x, y]) { | |
//debugger | |
var LR = x > mx? [mx+1, my] : x < mx? [mx-1, my] : null; | |
var UD = y > my? [mx, my+1] : y < my? [mx, my-1] : null; | |
if (LR && UD) { | |
var LRScore = overMap(LR, 2, 1, 0, 0); | |
var UDScore = overMap(UD, 2, 1, 0, 0); | |
if (LRScore == UDScore) return toPos([LR, UD][Math.random()>.5? 1 : 0]) | |
else if (LRScore > UDScore) return toPos(LR); | |
else return toPos(UD); | |
} else return toPos(LR || UD || [x, y]); | |
} | |
function toPos([x,y]) { | |
if (x > mx) return "right"; | |
if (x < mx) return "left"; | |
if (y < my) return "up"; | |
if (y > my) return "down"; | |
return 'wait'; | |
} | |
function inbounds([x, y]) { | |
// if (x<grid.length && y<grid.length && x>=0 && y>=0) return true; | |
if (x<grid.length-1 && y<grid.length-1 && x>=1 && y>=1) return true; | |
return false; | |
} | |
function get([x,y]) { | |
if (inbounds([x, y])) return grid[x][y]; | |
return 0; | |
} | |
function bestOf (arr) { | |
if (arr.length == 0) return false; | |
var bestScore = -Infinity; | |
var bestPos; | |
for (var [x, y] of arr) { | |
let score = 0; | |
for (var [bcol, bx, by] of bots) { | |
let dist = Math.sqrt((x-bx)**2 + (y-by)**2); | |
let res = over(bcol); | |
let power = res==0? 1 : res==1? 0.4 : 1.4; | |
score+= power * dist; | |
} | |
if (pastMoves.includes(x+";"+y)) score-= 1000000; | |
if (score > bestScore) { | |
bestScore = score; | |
bestPos = [x,y]; | |
} | |
} | |
if (bestScore < -500000) { | |
if (allowRetracing) log("RETRACING"); | |
else return false; | |
} | |
output = to(bestPos); | |
return true; | |
} | |
// log(x,this.bestX, y,this.bestY); | |
var distToBest = Math.abs(this.bestX-mx) + Math.abs(this.bestY-my); | |
if (this.atBest || distToBest < 10) { | |
log("at best; collecting"); | |
this.atBest = true; | |
var orth = [[-1,0],[0,-1],[1,0],[0,1]]; | |
var neighbors = orth | |
.map(([x,y])=>[x+mx, y+my]) | |
.filter(c=>inbounds(c)) | |
.filter(([x,y])=>!bots.some(([bid, bx, by]) => bx==x && by==y)) | |
.map(c=>[c,get(c)]); | |
var best = neighbors.filter(([_, col]) => col != mc && col != 0 && over(col) == 1); | |
if (bestOf(best.map(([pos, col]) => pos))) { | |
log("best"); | |
return output; | |
} | |
var good = neighbors.filter(([_, col]) => col == 0); | |
if (bestOf(good.map(([pos, col]) => pos))) { | |
log("good"); | |
return output; | |
} | |
var okay = neighbors.filter(([_, col]) => over(col) == 0); | |
if (bestOf(okay.map(([pos, col]) => pos))) { | |
log("okay"); | |
return output; | |
} | |
for (let i = 2; i < grid.length; i++) { | |
if (i > 6) allowRetracing = true; | |
neighbors = orth | |
.map(([x, y]) => [x*i + mx, y*i + my]) | |
.filter(c=>inbounds(c)) | |
.map(c=>[c,get(c)]); | |
best = neighbors.filter(([_, col]) => col == 0 || (col != mc && over(col) == 1)); | |
if (bestOf(best.map(([pos, col]) => pos))) { | |
log("best dist"); | |
return output; | |
} | |
okay = neighbors.filter(([_, col]) => over(col) == 0); | |
if (bestOf(okay.map(([pos, col]) => pos))) { | |
log("okay dist"); | |
return output; | |
} | |
} | |
return ['right','left','up','down'][Math.floor(Math.random()*4)]; | |
} else log("going to best"); | |
if (scoreOf(this.bestX, this.bestY) < this.bestScore/2 || distToBest > 20) rescore(); | |
return to([this.bestX, this.bestY]); | |
} | |
}, | |
{ | |
"name": "AnnoyingLittleBrother", | |
"func": function(myself, grid, bots, gameInfo) { | |
// Some paramters | |
var brother_loop_length = 200; // For how long will we follow a brother? | |
var brother_loop_count = 0; | |
var brother_score = -1; | |
var brother_id = -1; | |
var saw_all_brothers_moves = false; | |
var moves_i = 0; | |
var moves_to_follow = 5; // How much moves will we follow? | |
var moves_saw = makeArray(moves_to_follow, 2, 0); | |
var my_id = myself[0]; | |
var my_x = myself[1]; | |
var my_y = myself[2]; | |
var round = gameInfo[0]; | |
var end_round = gameInfo[1]; | |
var last_num_of_bots = 0; | |
// console.log('-------------------------------'); | |
// Handle Storage | |
if(round === 1){ // First round TODO Why start with round 2? | |
brother_id = -1; // Choose a Big Brother randomly | |
brother_loop_count = 10;// Refind after 10 rounds | |
moves_i = 0; | |
moves_saw = makeArray(moves_to_follow, 2, 0); | |
// console.log(moves_saw); | |
// console.log(encode_moves(moves_saw)); | |
localStorage.LB_moves_saw = encode_moves(moves_saw); | |
localStorage.LB_moves_i = moves_i;// Save it | |
localStorage.LB_brother_id = brother_id;// Save it | |
localStorage.LB_brother_loop_count = brother_loop_count; // Save it | |
localStorage.LB_saw_all_brothers_moves = saw_all_brothers_moves; | |
// console.log('My ID: ' + my_id); | |
} | |
else{ | |
moves_saw = decode_moves(localStorage.LB_moves_saw); | |
moves_i = localStorage.LB_moves_i; | |
brother_id = parseInt(localStorage.LB_brother_id); | |
brother_loop_count = parseInt(localStorage.LB_brother_loop_count); | |
saw_all_brothers_moves = (localStorage.LB_saw_all_brothers_moves === 'true'); | |
last_num_of_bots = parseInt(localStorage.LB_last_num_of_bots); | |
} | |
// console.log('Round: '+round + ' , BLC: ' + brother_loop_count); | |
// console.log('Me: ' + my_x + ', ' + my_x); | |
// Check if a bot was eliminated | |
if(last_num_of_bots != bots.length){ | |
// A bot was elimitated. Just tell LittleBrother to search for a new brother | |
brother_loop_count = 0; | |
brother_id = -1; | |
last_num_of_bots = bots.length; | |
} | |
// Are we tired of this brother yet? | |
if (brother_loop_count === 0){ | |
// Yes, we are | |
// Find highest scoring brother | |
var bot_scores = Array.apply(null, Array(bots.length+1)).map(Number.prototype.valueOf,0); | |
for (var x = 0; x < grid.length; x++) { | |
for (var y = 0; y < grid.length; y++) { | |
bot_scores[grid[x][y]] += 1; // Increase the score of the bot's who color this is | |
} | |
} | |
//console.log('Scores:'); | |
//console.log(bot_scores); | |
for (var uid = 0; uid < bot_scores.length; uid++){ | |
if (bot_scores[uid]>brother_score && uid>0 && my_id!=uid){ | |
if (Math.abs(my_id - (uid))%3<2){// Will it be annoying to the brother? | |
brother_score = bot_scores[uid]; | |
brother_id = uid-1; | |
} | |
} | |
} | |
// Start the brother follow count | |
brother_loop_count = brother_loop_length; | |
moves_i = 0; | |
saw_all_brothers_moves = 0; | |
// console.log('Now following brother: ' + brother_id); | |
// console.log(bots[brother_id]); | |
} | |
// No, we will still follow him | |
brother_loop_count -= 1; // But only for so long | |
// Now do the actual following | |
var aim_x = -1; | |
var aim_y = -1; | |
if (brother_id > -1){ | |
// console.log('Brother ID: ' + brother_id); | |
// console.log('Brother Location: ' + bots[brother_id][1] + ', ' + bots[brother_id][2]); | |
// Which point are we aiming for? | |
if(saw_all_brothers_moves === true){ // Did I see how my brother moves? | |
//console.log('Footsteps:'); | |
//console.log(moves_saw); | |
// Check if we are in his footsteps? | |
var in_brothers_footsteps = false; | |
for (var step = 0; step<moves_to_follow; step++){ | |
if ((moves_saw[step][0] == my_x) && ((moves_saw[step][1] == my_y))){ | |
in_brothers_footsteps = true; | |
break; | |
} | |
} | |
if(in_brothers_footsteps === true){ | |
// We are in his footsteps. Go to the next one! | |
// console.log('We are following in brothers footsteps'); | |
step++; if (step >= moves_to_follow){step=0;} | |
aim_x = moves_saw[step][0];aim_y = moves_saw[step][1]; | |
} | |
else{ | |
// We are not in his footsteps, aim for the footsteps | |
// console.log('We are moving to brothers trail'); | |
aim_x = 0; aim_y = 0; | |
for (var step = 0; step<moves_to_follow; step++){// Calculate step's center of mass | |
aim_x += moves_saw[step][0];aim_y += moves_saw[step][1]; | |
} | |
aim_x /= moves_to_follow; aim_y /= moves_to_follow; | |
} | |
} | |
else{ | |
// No, not yet. Just run towards him | |
//console.log('Blindly running to brother'); | |
aim_x = bots[brother_id][1]; | |
aim_y = bots[brother_id][2]; | |
} | |
} | |
//console.log('My goal: [' + aim_x + ', ' + aim_y + ']'); | |
// Watch big brother's moves | |
if(brother_id > - 1){ | |
moves_saw[moves_i][0] = bots[brother_id][1]; | |
moves_saw[moves_i][1] = bots[brother_id][2]; | |
moves_i ++; | |
if (moves_i===moves_to_follow){ | |
moves_i = 0; // Wrap counter for circular buffer | |
// Have I seen enough of them? | |
if(saw_all_brothers_moves === false){ | |
saw_all_brothers_moves = true; | |
// console.log('Saw enough of big brothers steps now...') | |
} | |
} | |
} | |
// Save updated variables | |
localStorage.LB_moves_saw = encode_moves(moves_saw); | |
localStorage.LB_moves_i = moves_i;// Save it | |
localStorage.LB_brother_id = brother_id;// Save it | |
localStorage.LB_brother_loop_count = brother_loop_count; // Save it | |
localStorage.LB_saw_all_brothers_moves = saw_all_brothers_moves; | |
localStorage.LB_last_num_of_bots = last_num_of_bots; | |
// Finish function | |
if (brother_id < 0){ // If not following anybody, move randomly | |
return ["up","down","left","right"][Math.random()*4|0]; | |
} | |
else{ | |
// Following a big brother! | |
// console.log('Move: ' + toPos([aim_x, aim_y])); | |
return toPos([aim_x, aim_y]); | |
} | |
// Some functions to ease the load | |
function toPos([x,y]) { | |
var dx = x - my_x; | |
var dy = y - my_y; | |
if(Math.abs(dx)>Math.abs(dy)){ | |
if (x > my_x) return "right"; | |
if (x < my_x) return "left"; | |
if (y < my_y) return "up"; | |
if (y > my_y) return "down"; | |
} | |
else{ | |
if (y < my_y) return "up"; | |
if (y > my_y) return "down"; | |
if (x > my_x) return "right"; | |
if (x < my_x) return "left"; | |
} | |
return 'wait'; | |
} | |
function decode_moves(moves_str){ | |
var moves_array = []; | |
var moves_strs = moves_str.split(';'); | |
for (var i = 0; i<5; i++){ | |
var splot = moves_strs[i].split(','); | |
moves_array[i] = []; | |
moves_array[i][0] = parseInt(splot[0]); | |
moves_array[i][1] = parseInt(splot[1]); | |
} | |
return moves_array; | |
} | |
function encode_moves(moves_array){ | |
var moves_str = ""; | |
for (var i = 0; i < moves_array.length; i++){ | |
moves_str += moves_array[i][0] + ',' + moves_array[i][1]; | |
if (i < moves_array.length - 1){moves_str += ';';} | |
} | |
return moves_str; | |
} | |
function makeArray(w, h, val) { | |
var arr = []; | |
for(i = 0; i < w; i++) { | |
arr[i] = []; | |
for(j = 0; j < h; j++) { | |
arr[i][j] = 0; | |
} | |
} | |
return arr; | |
} | |
} | |
}, | |
{ | |
"name": "NotSoDumbBot", | |
"func": function(myself, grid, bots, gameInfo) { | |
let myCol = myself[0]; | |
let myX = myself[1]; | |
let myY = myself[2]; | |
function getScore(x) { | |
let score = 0; | |
for (let i = 0; i < grid.length; i++) { | |
for (let j = 0; j < grid[i].length; j++) { | |
if (grid[i][j] == x) { | |
score++; | |
} | |
} | |
} | |
return score; | |
} | |
let myScoreHistory = {}; | |
if (localStorage.samYonnouMyScoreHistory) { | |
myScoreHistory = JSON.parse(localStorage.samYonnouMyScoreHistory); | |
} | |
let myScore = getScore(myCol); | |
myScoreHistory[gameInfo[0]] = myScore; | |
localStorage.samYonnouMyScoreHistory = JSON.stringify(myScoreHistory); | |
function isProbalyTrolled() { | |
if (gameInfo[0] < bots.length * 2) { | |
return false; // too early to tell | |
} | |
if (localStorage.samYonnouEvasiveActionsStarted && gameInfo[0] - parseInt(localStorage.samYonnouEvasiveActionsStarted, 10) > 8) { | |
if (gameInfo[0] - parseInt(localStorage.samYonnouEvasiveActionsStarted, 10) > 13) { | |
localStorage.samYonnouEvasiveActionsStarted = null; | |
} | |
return false; // we need to test if our evasive actions worked | |
} | |
for (let i = bots.length; i > 0; i--) { | |
if (myScoreHistory[gameInfo[0] - i] < myScoreHistory[gameInfo[0] - i + 1]) { | |
return false; | |
} | |
} | |
return true; | |
} | |
function isTimeForAggression() { | |
return myScore > bots.length * 8; | |
} | |
function isInferiorExplorer(x, y) { | |
return grid[x][y] != myCol && Math.abs(grid[x][y] - myCol) % 3 == 0; | |
} | |
function isEnemyTerritory(x, y) { | |
let enemyScore = getScore(x, y); | |
return isTimeForAggression() && Math.abs(grid[x][y] - myCol) % 3 == 1 && enemyScore / myScore > 2; | |
} | |
function isRipeForExploration(x, y) { | |
return (grid[x][y] == 0 || isInferiorExplorer(x, y)); | |
} | |
function getExplorationPriority(x, y) { | |
if (isInferiorExplorer(x, y)) { | |
let inferiorExplorerScore = getScore(grid[x][y]); | |
if (isTimeForAggression() && inferiorExplorerScore > myScore * 1.5) { | |
// this explorer is a danger and needs to be knocked down a peg | |
return inferiorExplorerScore; | |
} | |
} | |
let closestBot = Number.MAX_SAFE_INTEGER; | |
for (let i = 0; i < bots.length; i++) { | |
if (bots[i][0] != myCol) { | |
let distance = Math.abs(bots[i][1] - myX) + Math.abs(bots[i][2] - myY); | |
if (distance < closestBot) { | |
closestBot = distance; | |
} | |
} | |
} | |
// the farther away the closest bot is the better | |
return -((2 * grid.length) - closestBot); | |
} | |
function selectPath(selectionFunction, adhereToDirectionBan) { | |
let possiblePaths = []; | |
let possiblePathPriority = []; | |
if (myX > 0 && selectionFunction(myX - 1, myY)) { | |
possiblePaths.push("left"); | |
possiblePathPriority.push(getExplorationPriority(myX - 1, myY)); | |
} | |
if (myX < grid.length - 1 && selectionFunction(myX + 1, myY)) { | |
possiblePaths.push("right"); | |
possiblePathPriority.push(getExplorationPriority(myX + 1, myY)); | |
} | |
if (myY > 0 && selectionFunction(myX, myY - 1)) { | |
possiblePaths.push("up"); | |
possiblePathPriority.push(getExplorationPriority(myX, myY - 1)); | |
} | |
if (myY < grid[0].length - 1 && selectionFunction(myX, myY + 1)) { | |
possiblePaths.push("down"); | |
possiblePathPriority.push(getExplorationPriority(myX, myY + 1)); | |
} | |
let tryPickPath = possiblePaths.length > 0; | |
while (tryPickPath) { | |
tryPickPath = false; | |
let selection = null; | |
let chosenPathPriority = Number.MIN_SAFE_INTEGER; | |
for (let i = 0; i < possiblePathPriority.length; i++) { | |
if (possiblePathPriority[i] > chosenPathPriority) { | |
selection = possiblePaths[i]; | |
chosenPathPriority = possiblePathPriority[i]; | |
} | |
} | |
if (adhereToDirectionBan && localStorage.samYonnouBannedDirection && selection == localStorage.samYonnouBannedDirection) { | |
possiblePaths.splice(localStorage.samYonnouBannedDirection, 1); | |
tryPickPath = possiblePaths.length > 0; | |
} else { | |
return selection; | |
} | |
} | |
return null; | |
} | |
if (isProbalyTrolled()) { | |
// we are likely being trolled, try to lose the troll by going close to another explorer the troll can target | |
let closestDecoyDistance = Number.MAX_SAFE_INTEGER; | |
let closestDecoy = null; | |
let closestDecoyScore = 0; | |
for (let i = 0; i < bots.length; i++) { | |
if (bots[i][0] != myCol && Math.abs(bots[i][0] - myCol) % 3 == 0) { | |
let distance = Math.abs(bots[i][1] - myX) + Math.abs(bots[i][2] - myY); | |
if (distance > 1 && distance < closestDecoyDistance + 50) { | |
let decoyScore = getScore(bots[i][0]); | |
if (decoyScore > closestDecoyScore) { | |
closestDecoyDistance = distance; | |
closestDecoy = bots[i]; | |
closestDecoyScore = decoyScore; | |
} | |
} | |
} | |
} | |
if (closestDecoy) { | |
if (!localStorage.samYonnouEvasiveActionsStarted && closestDecoyDistance < 3) { | |
localStorage.samYonnouEvasiveActionsStarted = JSON.stringify(gameInfo[0]); | |
} | |
localStorage.samYonnouBannedDirection = null; | |
if (closestDecoy[1] > myX) { | |
return "right"; | |
} | |
if (closestDecoy[2] > myY) { | |
return "down"; | |
} | |
if (closestDecoy[1] < myX) { | |
return "left"; | |
} | |
if (closestDecoy[2] < myY) { | |
return "up"; | |
} | |
} | |
} | |
let selection = selectPath(isRipeForExploration, true); | |
if (!selection) { | |
selection = selectPath(isEnemyTerritory, true); | |
if (!selection) { | |
selection = selectPath(isRipeForExploration, false); | |
if (!selection) { | |
function findNewStart(selectionFunction) { | |
let newStart = null; | |
let bestDistance = Number.MAX_SAFE_INTEGER; | |
for (let i = 0; i < grid.length; i++) { | |
for (let j = 0; j < grid[i].length; j++) { | |
if (selectionFunction(i, j)) { | |
let distance = Math.abs(i - myX) + Math.abs(j - myY); | |
if (distance > 1 && distance < bestDistance) { | |
newStart = [i, j]; | |
bestDistance = distance; | |
} | |
} | |
} | |
} | |
return newStart; | |
} | |
let newStart = findNewStart(isRipeForExploration); | |
if (!newStart) { | |
newStart = findNewStart(isEnemyTerritory); | |
if (!newStart) { | |
newStart = [(myX + 23) % grid.length, (myY + 23) % grid.length]; | |
} | |
} | |
if (newStart[0] > myX) { | |
selection = "right"; | |
} | |
if (newStart[1] > myY) { | |
selection = "down"; | |
} | |
if (newStart[0] < myX) { | |
selection = "left"; | |
} | |
if (newStart[1] < myY) { | |
selection = "up"; | |
} | |
} | |
} | |
} | |
localStorage.samYonnouBannedDirection = (selection == "left") ? "right" : ((selection == "up") ? "down" : ((selection == "down") ? "up" : "left")); | |
return selection; | |
} | |
}, | |
{ | |
"name": "Boxer", | |
"func": function(myself, grid, bots, gameInfo) { | |
let val = gameInfo[0] % 16; | |
if(val < 3){ | |
return "right"; | |
}else if(val < 6){ | |
return "up"; | |
}else if(val < 9){ | |
return "left"; | |
}else if(val < 12){ | |
return "down"; | |
}else if(val < 14){ | |
return ["up","down","left","right"][Math.random() *4 |0]; | |
}else{ | |
let xdist = myself[1]; | |
let ydist = myself[2]; | |
let xfardist = grid.length - 1 - myself[1]; | |
let yfardist = grid.length - 1 - myself[2]; | |
if(gameInfo[0] % 400 < 200){ | |
if (xdist < ydist && xdist < xfardist && xdist < yfardist){ | |
return "right"; | |
}else if (ydist < xfardist && ydist < yfardist){ | |
return "down"; | |
}else if (xfardist < yfardist){ | |
return "left"; | |
}else{ | |
return "up"; | |
} | |
}else{ | |
if (xdist > ydist && xdist > xfardist && xdist > yfardist){ | |
return "right"; | |
}else if (ydist > xfardist && ydist > yfardist){ | |
return "up"; | |
}else if (xfardist > yfardist){ | |
return "left"; | |
}else{ | |
return "down"; | |
} | |
} | |
} | |
} | |
}, | |
{ | |
"name": "Kneecapper", | |
"func": function(myself, grid, bots, gameInfo) { | |
let [myId, myX, myY] = myself; | |
// Figure out who's doing well. | |
let targets = bots.map(bot => bot[0]).filter(id => (id !== myId) && (Math.abs(id - myId) % 3 !== 2)); | |
let flatGrid = [].concat.apply([], grid); | |
targets = targets.sort((a, b) => flatGrid.reduce((n, val) => n + (val === a) - (val === b), 0)); | |
let targetX, targetY; | |
let targetScore = 0; | |
for (let x = 0; x < grid.length; x++) { | |
for (let y = 0; y < grid.length; y++) { | |
let dist = Math.abs(x - myX) + Math.abs(y - myY); | |
let scariness = targets.indexOf(grid[x][y]) + 1; | |
if (scariness === 0) { continue; } | |
// Find a successful opponent who's not too far away. | |
let score = scariness ** 1.5 / (dist + 1); | |
if (score > targetScore) { | |
targetX = x; | |
targetY = y; | |
targetScore = score; | |
} | |
} | |
} | |
// Move toward the target. | |
if (targetX < myX) { return "left"; } | |
if (targetX > myX) { return "right"; } | |
if (targetY < myY) { return "up"; } | |
if (targetY > myY) { return "down"; } | |
return "wait"; | |
} | |
}, | |
{ | |
"name": "MC", | |
"func": function(myself, grid, bots, gameInfo) { | |
const W = grid.length, H = grid[0].length; | |
const meIdx = bots.findIndex(b => b[0] == myself[0]); | |
const meClr = bots[meIdx][0]; | |
function copy2D(a) { | |
return a.map(l => l.slice()); | |
} | |
function paintValue(gr, x, y, clr) { | |
if (gr[x][y] == 0) return clr; | |
else return [clr, 0, gr[x][y]][Math.abs(clr - gr[x][y]) % 3]; | |
} | |
function paint(gr, x, y, clr) { | |
gr[x][y] = paintValue(gr, x, y, clr); | |
} | |
function randomStep(gr, bt) { | |
const lastDir = new Array(bt.length).fill(-1); | |
for (let i = 0; i < bt.length; i++) { | |
const b = bt[i]; | |
while (Math.random() < 0.95) { | |
const dir = Math.random() * 4 | 0; | |
if (dir == lastDir[i]) continue; | |
switch (dir) { | |
case 0: if (b[1] < W-1) b[1]++; else continue; break; | |
case 1: if (b[2] < H-1) b[2]++; else continue; break; | |
case 2: if (b[1] > 0) b[1]--; else continue; break; | |
case 3: if (b[2] > 0) b[2]--; else continue; break; | |
} | |
break; | |
} | |
paint(gr, b[1], b[2], b[0]); | |
} | |
} | |
function calcScore(gr, id) { | |
let sc = 0; | |
for (let x = 0; x < W; x++) { | |
for (let y = 0; y < H; y++) { | |
sc += gr[x][y] == id; | |
} | |
} | |
return sc; | |
} | |
const dxes = [1, 0, -1, 0, 0], dyes = [0, 1, 0, -1, 0]; | |
const outputs = ["right", "down", "left", "up", "wait"]; | |
let maxscore = -1, maxat = -1; | |
for (let i = 0; i < 5; i++) { | |
const nx = bots[meIdx][1] + dxes[i]; | |
const ny = bots[meIdx][2] + dyes[i]; | |
if (nx < 0 || nx >= W || ny < 0 || ny >= H) continue; | |
const grid2 = copy2D(grid); | |
const bots2 = copy2D(bots); | |
bots2[meIdx][1] = nx; | |
bots2[meIdx][2] = ny; | |
paint(grid2, nx, ny, meClr); | |
let score = 0; | |
for (let playouti = 0; playouti < 100; playouti++) { | |
const grid3 = copy2D(grid2); | |
const bots3 = copy2D(bots2); | |
for (let si = 0; si < 5; si++) { | |
randomStep(grid3, bots3); | |
} | |
score += calcScore(grid3, meClr); | |
} | |
if (score > maxscore) { | |
maxscore = score; | |
maxat = i; | |
} | |
} | |
if (maxscore == -1) return outputs[Math.random() * 5 | 0]; | |
else return outputs[maxat]; | |
} | |
}, | |
{ | |
"name": "Euclid", | |
"func": function euclidFn(myself, grid, bots, gameInfo) { | |
const W = grid.length, H = grid[0].length; | |
const meIdx = bots.findIndex(b => b[0] == myself[0]); | |
const meClr = bots[meIdx][0]; | |
const botIdToIndex = {}; | |
for (let i = 0; i < bots.length; i++) { | |
botIdToIndex[bots[i][0]] = i; | |
} | |
function paintValue(floor, clr) { | |
if (floor == 0) return clr; | |
else return [clr, 0, floor][Math.abs(clr - floor) % 3]; | |
} | |
function paint(gr, x, y, clr) { | |
gr[x][y] = paintValue(gr[x][y], clr); | |
} | |
function distance(x1, y1, x2, y2) { | |
return Math.abs(y2 - y1) + Math.abs(x2 - x1); | |
} | |
function calcHeatmap() { | |
const heat = new Array(W).fill(0).map(() => new Array(H).fill(0)); | |
function weight(dx, dy) { | |
const d = dx + dy; | |
return d < 3 ? 1 / (1 + d) : 0; | |
} | |
for (let x = 0; x < W; x++) { | |
for (let y = 0; y < H; y++) { | |
let s=0; | |
for (let x2 = Math.max(x-3, 0); x2 <= Math.min(W-1, x+3); x2++) { | |
for (let y2 = Math.max(y-3, 0); y2 <= Math.min(H-1, y+3); y2++) { | |
if (grid[x2][y2] == meClr) { | |
s += weight(Math.abs(x2 - x), Math.abs(y2 - y)); | |
} | |
} | |
} | |
heat[x][y] = s; | |
} | |
} | |
return heat; | |
} | |
const heatmap = calcHeatmap(); | |
function scorePos(px, py) { | |
let sc = 0; | |
if (grid[px][py] != meClr && paintValue(grid[px][py], meClr) == meClr) { | |
sc += 100; | |
} | |
let mindist = W + H + 1; | |
for (let x = 0; x < W; x++) { | |
for (let y = 0; y < H; y++) { | |
if (grid[x][y] != meClr && paintValue(grid[x][y], meClr) == meClr) { | |
let d = distance(px, py, x, y); | |
if (d < mindist) mindist = d; | |
} | |
} | |
} | |
sc -= 3 * mindist; | |
mindist = W + H + 1; | |
for (let x = 0; x < W; x++) { | |
for (let y = 0; y < H; y++) { | |
if (grid[x][y] == largestBotId) { | |
let d = distance(px, py, x, y); | |
if (d < mindist) mindist = d; | |
} | |
} | |
} | |
sc -= 6 * mindist; | |
sc -= 3 * heatmap[px][py]; | |
sc += Math.random(); | |
return sc; | |
} | |
function calcBotScores() { | |
const res = new Array(bots.length).fill(0).map((_,i) => [bots[i][0], 0]); | |
for (let x = 0; x < W; x++) { | |
for (let y = 0; y < H; y++) { | |
if (grid[x][y] > 0) { | |
let i = botIdToIndex[grid[x][y]]; | |
if (i != undefined) res[i][1]++; | |
} | |
} | |
} | |
return res; | |
} | |
const botScores = calcBotScores(); // [id, size] | |
const largestBotId = botScores | |
.filter(p => p[0] != meClr && paintValue(p[0], meClr) == meClr) | |
.sort((a,b) => b[1] - a[1]) | |
[0][0]; | |
const dxes = [1, 0, -1, 0, 0], dyes = [0, 1, 0, -1, 0]; | |
const outputs = ["right", "down", "left", "up", "wait"]; | |
let allscores = []; | |
let maxscore = -Infinity, maxat = -1; | |
let allowWait = grid[bots[meIdx][1]][bots[meIdx][2]] == 0; | |
for (let i = 0; i < 4 + allowWait; i++) { | |
const nx = bots[meIdx][1] + dxes[i]; | |
const ny = bots[meIdx][2] + dyes[i]; | |
if (nx < 0 || nx >= W || ny < 0 || ny >= H) { | |
allscores.push(null); | |
continue; | |
} | |
let score = scorePos(nx, ny); | |
if (i == 4) score -= 20; | |
if (euclidFn.lastMove != undefined && i != euclidFn.lastMove) score -= 3; | |
allscores.push(~~(score * 1000) / 1000); | |
if (score > maxscore) { | |
maxscore = score; | |
maxat = i; | |
} | |
} | |
// console.log([maxscore, maxat], allscores); | |
let move = maxscore == -1 ? Math.random() * 5 | 0 : maxat; | |
euclidFn.lastMove = move; | |
return outputs[move]; | |
} | |
}, | |
{ | |
"name": "DFSBot", | |
"func": function(myself, grid, bots, gameinfo) { | |
my_id = myself[0]; | |
my_x = myself[1]; | |
my_y = myself[2]; | |
delta = [[0, -1], [0, 1], [-1, 0], [1, 0], [0, 0]]; | |
seen = Array(grid.length).fill().map(() => Array(grid.length).fill(false)); | |
scores = Array(bots.length + 2).fill(0); | |
for (let i = 0; i < grid.length; i++) { | |
for (let j = 0; j < grid.length; j++) { | |
scores[grid[i][j]]++; | |
} | |
} | |
function search(x, y, depth) { | |
if (depth == 11) { | |
return [4, 0]; | |
} | |
let max_score = 0; | |
let best_move = 0; | |
for (let i = 0; i < 4; i++) { | |
let x1 = x + delta[i][0]; | |
let y1 = y + delta[i][1]; | |
if ((x1 < 0) || (x1 >= grid.length)) { | |
continue; | |
} | |
if ((y1 < 0) || (y1 >= grid.length)) { | |
continue; | |
} | |
if (seen[x1][y1]) { | |
continue; | |
} | |
let n = 0; | |
for (let j = 0; j < 4; j++) { | |
if (Math.abs(i - j) == 1) { | |
continue; | |
} | |
let x2 = x1 + delta[j][0]; | |
let y2 = y1 + delta[j][1]; | |
if ((x2 < 0) || (x2 >= grid.length)) { | |
continue; | |
} | |
if ((y2 < 0) || (y2 >= grid.length)) { | |
continue; | |
} | |
if (grid[x2][y2] == my_id) { | |
n++; | |
} | |
} | |
let prev = grid[x1][y1]; | |
if (prev == 0) { | |
next = my_id; | |
} else { | |
next = [my_id, 0, prev][Math.abs(my_id - prev) % 3]; | |
} | |
let score = 0; | |
score += 1e-10 * (n + 0.1 * Math.random()); | |
if (next != prev) { | |
if (next == my_id) { | |
score += 1; | |
} | |
if (prev == 0 || scores[prev] > scores[my_id]) { | |
score += 1e-6; | |
} | |
} | |
grid[x1][y1] = next; | |
seen[x1][y1] = true; | |
let final_score = 0.05 * score + 0.95 * search(x1, y1, depth + 1)[1]; | |
seen[x1][y1] = false; | |
grid[x1][y1] = prev; | |
if (final_score > max_score) { | |
max_score = final_score; | |
best_move = i; | |
} | |
} | |
return [best_move, max_score]; | |
} | |
let best_move = search(my_x, my_y, 0)[0]; | |
return ["up", "down", "left", "right", "wait"][best_move]; | |
} | |
}, | |
{ | |
"name": "GiveMeSpace", | |
"func": function(myself, grid, bots, gameInfo){ | |
if(!localStorage.givemespace){ | |
localStorage.givemespace = JSON.stringify({ | |
recent:[], | |
timeout:-1, | |
corner:[9999,-1,-1], | |
following:[] | |
}); | |
} | |
var firstchoice = {up:-1,down:-1,left:-1,right:-1}, nearestblank = [9999,-1,-1], store = JSON.parse(localStorage.givemespace), unique = [], numunique = 0, | |
currdist, i, j; | |
if(store.timeout >= 0 && store.corner[1] >= 0 && store.corner[2] >= 0){ | |
store.timeout--; | |
persiststorage(store); | |
if(store.corner[2] < myself[2]){ | |
return 'up'; | |
} | |
if(store.corner[2] > myself[2]){ | |
return 'down'; | |
} | |
if(store.corner[1] < myself[1]){ | |
return 'left'; | |
} | |
if(store.corner[1] > myself[1]){ | |
return 'right'; | |
} | |
} | |
if(store.recent.length == 20){ | |
for(i=0;i<store.recent.length;i++){ | |
if(unique.indexOf(store.recent[i][1]+"_"+store.recent[i][2]) == -1){ | |
unique.push(store.recent[i][1]+"_"+store.recent[i][2]); | |
numunique++; | |
} | |
} | |
if(numunique <= 6){ | |
store.recent = []; | |
store.timeout = 10+numunique; | |
store.corner = [[-1,0,0],[-1,0,bots.length*3-1],[-1,bots.length*3-1,0],[-1,bots.length*3-1,bots.length*3-1]][Math.random()*4|0]; | |
persiststorage(store); | |
} | |
} | |
function dist(a,b){ | |
return Math.abs(a[1]-b[1])+Math.abs(a[2]-b[2]); | |
} | |
function finalcolor(a,b){ | |
return Math.abs(a-b)%3; | |
} | |
function persiststorage(store){ | |
if(store.recent.length > 20) store.recent = store.recent.slice(1); | |
localStorage.givemespace = JSON.stringify(store); | |
} | |
store.recent.push(myself); | |
persiststorage(store); | |
if(myself[2] > 0 && myself[0] != grid[myself[1]][myself[2]-1] && (grid[myself[1]][myself[2]-1] == 0 || finalcolor(myself[0],grid[myself[1]][myself[2]-1]) == 0)){ | |
firstchoice.up = 9999; | |
} | |
if(myself[2] < bots.length*3-1 && myself[0] != grid[myself[1]][myself[2]+1] && (grid[myself[1]][myself[2]+1] == 0 || finalcolor(myself[0],grid[myself[1]][myself[2]+1]) == 0)){ | |
firstchoice.down = 9999; | |
} | |
if(myself[1] > 0 && myself[0] != grid[myself[1]-1][myself[2]] && (grid[myself[1]-1][myself[2]] == 0 || finalcolor(myself[0],grid[myself[1]-1][myself[2]]) == 0)){ | |
firstchoice.left = 9999; | |
} | |
if(myself[1] < bots.length*3-1 && myself[0] != grid[myself[1]+1][myself[2]] && (grid[myself[1]+1][myself[2]] == 0 || finalcolor(myself[0],grid[myself[1]+1][myself[2]]) == 0)){ | |
firstchoice.right = 9999; | |
} | |
if(firstchoice.up > 0 || firstchoice.down > 0 || firstchoice.left > 0 || firstchoice.right > 0){ | |
for(i=0;i<bots.length;i++){ | |
if(bots[i][0] != myself[0]){ | |
if(firstchoice.up > 0){ | |
currdist = dist(bots[i],[0,myself[1],myself[2]-1]); | |
if(currdist < firstchoice.up){ | |
firstchoice.up = currdist; | |
} | |
} | |
if(firstchoice.down > 0){ | |
currdist = dist(bots[i],[0,myself[1],myself[2]+1]); | |
if(currdist < firstchoice.down){ | |
firstchoice.down = currdist; | |
} | |
} | |
if(firstchoice.left > 0){ | |
currdist = dist(bots[i],[0,myself[1]-1,myself[2]]); | |
if(currdist < firstchoice.left){ | |
firstchoice.left = currdist; | |
} | |
} | |
if(firstchoice.right > 0){ | |
currdist = dist(bots[i],[0,myself[1]+1,myself[2]]); | |
if(currdist < firstchoice.right){ | |
firstchoice.right = currdist; | |
} | |
} | |
} | |
} | |
if(firstchoice.up >= firstchoice.down && firstchoice.up >= firstchoice.left && firstchoice.up >= firstchoice.right){ | |
return 'up'; | |
} | |
else if(firstchoice.down >= firstchoice.left && firstchoice.down >= firstchoice.right){ | |
return 'down'; | |
} | |
else if(firstchoice.left >= firstchoice.right){ | |
return 'left'; | |
} | |
else{ | |
return 'right'; | |
} | |
} | |
for(i=0;i<bots.length*3;i++){ | |
for(j=0;j<bots.length*3;j++){ | |
if((i != myself[1] || j != myself[2]) && grid[i][j] != myself[0] && (grid[i][j] == 0 || finalcolor(myself[0],grid[i][j]) == 0)){ | |
currdist = dist(myself,[0,i,j]); | |
if(currdist < nearestblank[0]){ | |
nearestblank[0] = currdist; | |
nearestblank[1] = i; | |
nearestblank[2] = j; | |
} | |
} | |
} | |
} | |
if(nearestblank[0] < 9999){ | |
if(nearestblank[2] < myself[2]){ | |
return 'up'; | |
} | |
if(nearestblank[2] > myself[2]){ | |
return 'down'; | |
} | |
if(nearestblank[1] < myself[1]){ | |
return 'left'; | |
} | |
if(nearestblank[1] > myself[1]){ | |
return 'right'; | |
} | |
} | |
return ['up','down','left','right'][Math.random()*4|0]; | |
} | |
}, | |
{ | |
"name": "Smart Ant V4", | |
"func": function(myself, grid, bots, gameInfo) { | |
// Assign variables. | |
let dest = {left: "left", right: "right", up: "up", down: "down", wait: "wait"}; | |
let deltas = {x: {left: -1, right: +1, up: 0, down: 0, wait: 0}, y: {left: 0, right: 0, up: -1, down: +1, wait: 0}}; | |
let [name, me, [rcurr, rmax], [gmin, gmax], blank] = ["Smart Ant V4", { c: myself[0], x: myself[1], y: myself[2] }, gameInfo, [0, grid.length - 1], 0]; | |
// Define local functions. | |
function onEdge(pos, x = me.x, y = me.y) { // Returns true if on the edge. | |
switch (pos) { | |
case dest.left: return x == gmin; break; | |
case dest.right: return x == gmax; break; | |
case dest.up: return y == gmin; break; | |
case dest.down: return y == gmax; break; | |
default: return x == gmin || x == gmax || y == gmin || y == gmax; | |
} | |
} | |
function validXY(x, y) { // Validate coordinates. | |
return (gmin <= x && gmin <= y && x <= gmax && y <= gmax); | |
} | |
function validMove(pos = db['curr_pos'], x = me.x, y = me.y) { | |
return validXY(x + deltas.x[pos], y + deltas.y[pos]); | |
} | |
function toEdge(pos = db['curr_pos'], x = me.x, y = me.y) { // Returns true if the next move is towards the edge. | |
return onEdge(pos, x + deltas.x[pos], y + deltas.y[pos]); | |
} | |
function turnLeft(pos = db['curr_pos']) { // Get the new direction after turning left. | |
next = {left: dest.down, right: dest.up, up: dest.left, down: dest.right}; return next[pos]; | |
} | |
function turnRight(pos = db['curr_pos']) { // Get the new direction after turning right. | |
next = {left: dest.up, right: dest.down, up: dest.right, down: dest.left}; return next[pos]; | |
} | |
function getColorAbs(x, y) { // Get color using absolute position. | |
return validXY(x, y) ? grid[x][y] : -1; | |
} | |
function getColorRel(x, y) { // Get color using relative position. | |
return getColorAbs(me.x + x, me.y + y); | |
} | |
function getColorDir(pos = db['curr_pos']) { // Get color at the direction. | |
return getColorRel(deltas.x[pos], deltas.y[pos]); | |
} | |
function getNextColor(color) { // Predict the next color. | |
return color == blank ? me.c : [me.c, 0, color][Math.abs(me.c - color) % 3]; | |
} | |
function noBlanks(color) { // Returns true if no blank squares around. | |
return Math.min(getColorDir(dest.left), getColorDir(dest.right), getColorDir(dest.up), getColorDir(dest.down)) > blank; | |
} | |
function isEdibleXY(x, y) { // Returns true if square is edible. | |
return (getColorAbs(x, y) != me.c && getNextColor(getColorAbs(x, y)) != getColorAbs(x, y)); | |
} | |
function isBotXY(bx = me.x, by = me.y) { | |
return Object.keys(bots).map(function(key, index) { | |
[c, x, y] = bots[key]; | |
return me.c != c && bx == x && by == y; | |
}); | |
} | |
function calcEdibleSq(pos = db['curr_pos'], mx = me.x, my = me.y) { // Calculate number of edible squares minus distance for the given direction. | |
let [counter, penalty, x, y] = [0, 0, mx, my]; | |
while (validXY(x, y)) { | |
[x, y, penalty] = [x + deltas.x[pos], y + deltas.y[pos], penalty + 1/gmax*2]; | |
if (isEdibleXY(x, y)) counter += Math.max(0, getColorAbs(x, y) == blank ? 1 - penalty : 1 - penalty/2); | |
else if (getColorAbs(x, y) == me.c || isBotXY(x, y)) counter -= 1 - penalty; | |
} | |
return counter; | |
} | |
function getRandomDir() { // Get a random move. | |
Object.values(dest)[Math.floor(Math.random() * Object.values(dest).length)] | |
} | |
function getLongestDir() { // Get the longest direction (away from the edges). | |
return [me.x > gmax/2 ? dest.left : dest.right, me.y > gmax/2 ? dest.up : dest.down][Math.floor(Math.random() * 2)]; | |
} | |
function getEdibleDir() { // Get direction based on number of edible squares. | |
best_moves = [] | |
// Check current position. | |
routes = [{left: calcEdibleSq(dest.left)}, {right: calcEdibleSq(dest.right)}, {up: calcEdibleSq(dest.up)}, {down: calcEdibleSq(dest.down)}] | |
.sort(function(a, b) { return Object.values(b)[0] - Object.values(a)[0]}); | |
best_moves.push(routes[0]); | |
// Check other 4 positions. | |
pos = dest.left; | |
routes = [ | |
{left: calcEdibleSq(dest.left, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{left: calcEdibleSq(dest.right, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{left: calcEdibleSq(dest.up, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{left: calcEdibleSq(dest.down, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
].sort(function(a, b) { return Object.values(b)[0] - Object.values(a)[0]}); | |
best_moves.push(routes[0]); | |
pos = dest.right; | |
routes = [ | |
{right: calcEdibleSq(dest.left, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{right: calcEdibleSq(dest.right, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{right: calcEdibleSq(dest.up, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{right: calcEdibleSq(dest.down, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
].sort(function(a, b) { return Object.values(b)[0] - Object.values(a)[0]}); | |
best_moves.push(routes[0]); | |
pos = dest.up; | |
routes = [ | |
{up: calcEdibleSq(dest.left, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{up: calcEdibleSq(dest.right, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{up: calcEdibleSq(dest.up, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{up: calcEdibleSq(dest.down, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
].sort(function(a, b) { return Object.values(b)[0] - Object.values(a)[0]}); | |
best_moves.push(routes[0]); | |
pos = dest.down; | |
routes = [ | |
{down: calcEdibleSq(dest.left, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{down: calcEdibleSq(dest.right, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{down: calcEdibleSq(dest.up, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
{down: calcEdibleSq(dest.down, me.x + deltas.x[pos], me.y + deltas.y[pos])}, | |
].sort(function(a, b) { return Object.values(b)[0] - Object.values(a)[0]}); | |
best_moves.push(routes[0]); | |
best_moves.sort(function(a, b) { return Object.values(b)[0] - Object.values(a)[0]}); | |
[cost1, cost2] = [Object.values(Object.values(best_moves)[0])[0], Object.values(Object.values(best_moves)[1])[0]]; | |
best_pos = Object.keys(Object.values(best_moves)[0])[0]; | |
//if (cost1 <= 0) { console.log(best_moves); } | |
return cost1 > 0 ? best_pos : getLongestDir() || getRandomDir(); | |
} | |
function rightMove(pos = db['curr_pos'], prev = db['prev_pos'], pri = 0) { // Check if our next move is right. | |
if (!validMove(pos)) return false; // Invalid move. | |
if (toEdge(pos)) return pri > 8; // Towards the edge. | |
if (getColorDir(pos) == me.c) return pri > 8; // Our color. | |
if (getNextColor(getColorDir(pos)) != me.c) return pri > 4; // Avoid not paintable squares. | |
if (deltas.x[pos] + deltas.x[prev] == 0 && deltas.y[pos] + deltas.y[prev] == 0) return pri > 4; // Prevent going back. | |
if (pos == dest.wait == prev) return false; // Do not wait twice. | |
return true; | |
} | |
let db = JSON.parse(localStorage.getItem(name)) || {}; | |
if (rcurr == 1) db['curr_pos'] = getEdibleDir(); | |
db['prev_pos'] = db['curr_pos']; | |
// Main code. | |
let next_move = db['curr_pos']; | |
let counter = 0; | |
if (isBotXY().includes(true)) { | |
// In case of clash with another bot, wait or dance random to avoid loops. | |
next_move = db['prev_pos'] != dest.wait ? dest.wait : getRandomDir(); | |
} | |
else if (noBlanks() && !rightMove(next_move)) { | |
next_move = getEdibleDir(); | |
} | |
else while (!rightMove(next_move, db['prev_pos'], counter)) { | |
next_move = turnRight(next_move); | |
if (++counter > 12) { next_move = getEdibleDir(); break; } | |
} | |
db['curr_pos'] = next_move || getLongestDir() || getRandomDir(); | |
localStorage.setItem(name, JSON.stringify(db)); | |
return db.curr_pos; | |
} | |
}, | |
{ | |
"name": "Humble Paint Salesman", | |
"func": function(myself, grid, bots, gameInfo) { | |
let [id, x, y] = myself; | |
// if first move | |
if(gameInfo[0] == 1) { | |
this.size = grid.length; | |
this.mid = this.size / 2; | |
this.dx = x < this.mid ? "right" : "left"; | |
this.dy = y < this.mid ? "down" : "up"; | |
this.flip = function(v) { | |
this.dict = this.dict || { | |
right: "left", | |
left: "right", | |
down: "up", | |
up: "down" | |
}; | |
return this.dict[v]; | |
} | |
this.queue = []; | |
} | |
if(grid[x][y] == 0) { | |
return "wait"; | |
} | |
else if(this.queue.length) { | |
return this.queue.shift(); | |
} | |
else if(x == 0 || x + 1 == this.size) { | |
this.dx = this.flip(this.dx); | |
if(y == 0 || y + 1 == this.size) { | |
this.dy = this.flip(this.dy); | |
} | |
this.queue.push(this.dx); | |
return this.dy; | |
} | |
return this.dx; | |
} | |
}, | |
{ | |
"name": "Clever Name", | |
"func": function(myself, grid, bots, gameInfo) { | |
// Parse the arguments. | |
let myId = myself[0]; | |
let myX = myself[1]; | |
let myY = myself[2]; | |
// Check each square to see if it's a good target. | |
let targetX, targetY, targetDist = Infinity; | |
let numAtDist; | |
for (let x = 0; x < grid.length; x++) { | |
for (let y = 0; y < grid.length; y++) { | |
// We don't care about our own squares. | |
if (grid[x][y] === myId) { continue; } | |
// Only squares that we can recolor are useful. | |
if (grid[x][y] === 0 || Math.abs(grid[x][y] - myId) % 3 !== 2) { | |
// If this is the closest we've seen, target it. | |
let dist = Math.abs(myX - x) + Math.abs(myY - y); | |
if (dist < targetDist) { | |
targetX = x; | |
targetY = y; | |
targetDist = dist; | |
numAtDist = 1; | |
// If it's tied for the closest, sometimes target it. | |
} else if (dist === targetDist) { | |
numAtDist++; | |
if (Math.floor(numAtDist * Math.random()) === 0) { | |
targetX = x; | |
targetY = y; | |
} | |
} | |
} | |
} | |
} | |
// Move toward the target. | |
if (targetX < myX) { return "left"; } | |
if (targetX > myX) { return "right"; } | |
if (targetY < myY) { return "up"; } | |
if (targetY > myY) { return "down"; } | |
return "wait"; | |
} | |
}, | |
{ | |
"name": "M.A.D.S.", | |
"func": function(myself, grid, bots, gameInfo) | |
{ | |
const w = 6, h = 6; | |
let my_c = myself[0], my_x = myself[1], my_y = myself[2], size = grid.length, roundnum = gameInfo[0]; | |
if (!localStorage.steelyeyedmissileman) { | |
var offset_x = Math.random() *(size-w-1) |0; | |
var offset_y = Math.random() *(size-h-1) |0; | |
localStorage.steelyeyedmissileman = JSON.stringify([offset_x, offset_y]); | |
} | |
offsets = JSON.parse(localStorage.steelyeyedmissileman); | |
offset_x = offsets[0]; | |
offset_y = offsets[1]; | |
let targets = []; | |
for(let grid_x = offset_x; grid_x < offset_x+6; grid_x++) | |
{ | |
for(let grid_y = offset_y; grid_y < offset_y+6; grid_y++) | |
{ | |
if(grid[grid_x][grid_y] != my_c) | |
{ | |
targets.push([grid_x, grid_y]); | |
} | |
} | |
} | |
let target = targets.pop(); | |
if(target == undefined) return 'wait'; | |
if(target[0] > my_x) return 'right'; | |
if(target[0] < my_x) return 'left'; | |
if(target[1] > my_y) return 'down'; | |
if(target[1] < my_y) return 'up'; | |
return "left"; | |
} | |
}, | |
{ | |
"name": "Hunter-Killer", | |
"func": function(myself, grid, bots, gameInfo) { | |
targetColour = myself[0] % 3; | |
// If I can paint someone else's space to my colour, do so. | |
var options = []; | |
if (myself[1] !== 0 && grid[myself[1] - 1][myself[2]] % 3 === targetColour && grid[myself[1] - 1][myself[2]] !== myself[0] && grid[myself[1] - 1][myself[2]] !== 0) | |
options.push("left"); | |
if (myself[1] !== grid.length - 1 && grid[myself[1] + 1][myself[2]] % 3 === targetColour && grid[myself[1] + 1][myself[2]] !== myself[0] && grid[myself[1] + 1][myself[2]] !== 0) | |
options.push("right"); | |
if (myself[2] !== 0 && grid[myself[1]][myself[2] - 1] % 3 === targetColour && grid[myself[1]][myself[2] - 1] !== myself[0] && grid[myself[1]][myself[2] - 1] !== 0) | |
options.push("up"); | |
if (myself[2] !== grid.length - 1 && grid[myself[1]][myself[2] + 1] % 3 === targetColour && grid[myself[1]][myself[2] + 1] !== myself[0] && grid[myself[1]][myself[2] + 1] !== 0) | |
options.push("down"); | |
if (options.length > 0) return options[Math.random() * options.length | 0]; | |
// Otherwise, move to the closest bot I can paint over. | |
var targetBots = bots.filter(bot => { | |
if (bot[0] === myself[0] || bot[0] % 3 !== targetColour) return false; | |
return true; | |
}); | |
if (targetBots.length > 0) { | |
targetBots.sort((a, b) => (Math.abs(a[1] - myself[1]) + Math.abs(a[2] - myself[2])) < (Math.abs(a[1] - myself[1]) + Math.abs(a[2] - myself[2]))); | |
if (Math.abs(targetBots[0][1] - myself[1]) > Math.abs(targetBots[0][2] - myself[2])){ | |
return targetBots[0][1] - myself[1] > 0 ? "right" : "left"; | |
} | |
return targetBots[0][2] - myself[2] > 0 ? "down" : "up"; | |
} | |
options = []; | |
// If we've killed them all, try to move to a blank space. | |
if (myself[1] !== 0 && grid[myself[1] - 1][myself[2]] === 0 && grid[myself[1] - 1][myself[2]] !== myself[0]) | |
options.push("left"); | |
if (myself[1] !== grid.length - 1 && grid[myself[1] + 1][myself[2]] === 0 && grid[myself[1] + 1][myself[2]] !== myself[0]) | |
options.push("right"); | |
if (myself[2] !== 0 && grid[myself[1]][myself[2] - 1] === 0 && grid[myself[1]][myself[2] - 1] !== myself[0]) | |
options.push("up"); | |
if (myself[2] !== grid.length - 1 && grid[myself[1]][myself[2] + 1] === 0 && grid[myself[1]][myself[2] + 1] !== myself[0]) | |
options.push("down"); | |
if (options.length > 0) return options[Math.random() * options.length | 0]; | |
options = []; | |
// If there aren't any, try to move to a space I can paint white. | |
targetColour = (targetColour + 2) % 3 | |
if (myself[1] !== 0 && grid[myself[1] - 1][myself[2]] % 3 === 0 && grid[myself[1] - 1][myself[2]] !== myself[0]) | |
options.push("left"); | |
if (myself[1] !== grid.length - 1 && grid[myself[1] + 1][myself[2]] % 3 === 0 && grid[myself[1] + 1][myself[2]] !== myself[0]) | |
options.push("right"); | |
if (myself[2] !== 0 && grid[myself[1]][myself[2] - 1] % 3 === 0 && grid[myself[1]][myself[2] - 1] !== myself[0]) | |
options.push("up"); | |
if (myself[2] !== grid.length - 1 && grid[myself[1]][myself[2] + 1] % 3 === 0 && grid[myself[1]][myself[2] + 1] !== myself[0]) | |
options.push("down"); | |
if (options.length > 0) return options[Math.random() * options.length | 0]; | |
// Otherwise, pick one at random. | |
return ["up","down","left","right"][Math.random() * 4 | 0]; | |
} | |
}, | |
{ | |
"name": "No Do Overs", | |
"func": function(myself, grid, bots, gameInfo) { | |
var c = myself[0]; | |
var x = myself[1]; | |
var y = myself[2]; | |
var n = grid.length; | |
var dirs = ["left", "up", "down", "right"] | |
for(var _ = 0; _ < 4; _++) { | |
var dir = dirs.splice(Math.random() * dirs.length | 0, 1); | |
if(dir == "left" && x != 0 && grid[x-1][y] == 0) { | |
return "left"; | |
} | |
if(dir == "right" && x != n - 1&& grid[x+1][y] == 0) { | |
return "right"; | |
} | |
if(dir == "up" && y != 0 && grid[x][y-1] == 0) { | |
return "up"; | |
} | |
if(dir == "down" && y != n - 1 && grid[x][y+1] == 0) { | |
return "down"; | |
} | |
if(dir == "left" && x != 0 && grid[x-1][y] != c) { | |
return "left"; | |
} | |
if(dir == "right" && x != n - 1 && grid[x+1][y] != c) { | |
return "right"; | |
} | |
if(dir == "up" && y != 0 && grid[x][y-1] != c) { | |
return "up"; | |
} | |
if(dir == "down" && y != n - 1 && grid[x][y+1] != c) { | |
return "down"; | |
} | |
} | |
dirs = []; | |
if(x != 0) dirs[dirs.length] = "left"; | |
if(x != n - 1) dirs[dirs.length] = "right"; | |
if(y != 0) dirs[dirs.length] = "up"; | |
if(y != n - 1) dirs[dirs.length] = "down"; | |
return dirs[Math.random() * dirs.length | 0]; | |
} | |
}, | |
{ | |
"name": "Trollbot", | |
"func": function(myself, grid, bots, gameInfo) { | |
var c = myself[0]; | |
var x = myself[1]; | |
var y = myself[2]; | |
var cd = -1; | |
var cx = -1; | |
var cy = -1; | |
var i; | |
for(i = 0; i < bots.length; i++){ | |
var bc = bots[i][0]; | |
var bx = bots[i][1]; | |
var by = bots[i][2]; | |
var flatGrid = grid.reduce((acc, val) => acc.concat(val), []); | |
var botAlive = flatGrid.filter(function(cell) {return cell == bc}).length > 1; | |
if (botAlive && c != bc && Math.abs(c-bc)%3 == 0) { | |
var d = Math.abs(x-bx)+Math.abs(y-by); | |
if (d > 0 && (cd == -1 || d<cd)) { | |
cd = d; | |
cx = bx; | |
cy = by; | |
} | |
} | |
} | |
if (cd == -1) { | |
var j; | |
for(i=0; i<grid.length; i++) { | |
for(j=0; j<grid.length; j++) { | |
if (grid[i][j] == 0) { | |
var d = Math.abs(x-i)+Math.abs(y-j); | |
var sharingWithBot = (i == x && j == y && bots.filter((item) => item[1] == i && item[2] == j).length > 1); | |
if (!sharingWithBot && (cd == -1 || d<cd)) { | |
cd = d; | |
cx = i; | |
cy = j; | |
} | |
} | |
} | |
} | |
} | |
var move; | |
var dx = cx-x; | |
var dy = cy-y; | |
if (cd == -1) { | |
move = ["up","down","left","right"][Math.random() *4 |0]; | |
} else if (dx == 0 && dy == 0) { | |
move = "wait"; | |
} else if (Math.abs(dx) > Math.abs(dy) || (Math.abs(dx) == Math.abs(dy) && Math.random() * 2 < 1)) { | |
if (dx > 0) { | |
move = "right"; | |
} else { | |
move = "left"; | |
} | |
} else { | |
if (dy > 0) { | |
move = "down"; | |
} else { | |
move = "up"; | |
} | |
} | |
return move; | |
} | |
}, | |
{ | |
"name": "The Bot That Paints The Board Constantly But Is Not A Painter", | |
"func": function (me, board, painters, info) { | |
let id = me[0], meX = me[1], meY = me[2], size = board.length, round = info[0], end = info[1], i; | |
let getDistance = function (x1, y1, x2, y2) { | |
return (Math.abs(x1 - x2) + Math.abs(y1 - y2)) + 1; | |
}; | |
let getColorValue = function (color) { | |
if (color === 0) return 2; | |
if (color === id) return 0; | |
return 2 - (Math.abs(id - color) % 3); | |
}; | |
let getScore = function (x, y) { | |
let score = 0, paintersLength = painters.length, i; | |
for (let bX = 0; bX < size; bX++) { | |
for (let bY = 0; bY < size; bY++) { | |
let distance = getDistance(x, y, bX, bY); | |
let colorValue = getColorValue(board[bX][bY]); | |
let factor = 1; | |
if (distance === 1) { | |
for (i = 0; i < paintersLength; i++) if (painters[i][1] === bX && painters[i][2] === bY) factor = 0; | |
if (factor > 0) factor = 3; | |
} | |
score += (colorValue / (distance / 4)) * factor; | |
} | |
} | |
for (i = 0; i < paintersLength; i++) { | |
let pId = painters[i][0], pX = painters[i][1], pY = painters[i][2]; | |
if (pId === id) continue; | |
let pDistance = getDistance(x, y, pX, pY); | |
let pIdValue = getColorValue(pId); | |
score -= (pIdValue / (pDistance / 2)) / 4; | |
} | |
return score + Math.random(); | |
}; | |
let possibleMoves = [{x: 0, y: 0, c: 'wait'}]; | |
if (meX > 0) possibleMoves.push({x: -1, y: 0, c: 'left'}); | |
if (meY > 0) possibleMoves.push({x: -0, y: -1, c: 'up'}); | |
if (meX < size - 1) possibleMoves.push({x: 1, y: 0, c: 'right'}); | |
if (meY < size - 1) possibleMoves.push({x: 0, y: 1, c: 'down'}); | |
let topCommand, topScore = null; | |
for (i = 0; i < possibleMoves.length; i++) { | |
let score = getScore(meX + possibleMoves[i].x, meY + possibleMoves[i].y); | |
if (topScore === null || score > topScore) { | |
topScore = score; | |
topCommand = possibleMoves[i].c; | |
} | |
} | |
return topCommand; | |
} | |
}, | |
{ | |
"name": "Random Filler", | |
"func": function([id, x, y], grid, bots, gameInfo) { | |
let painted = { | |
false: { | |
un: [], | |
other: [], | |
me: [], | |
}, | |
true: { | |
un: [], | |
other: [], | |
me: [], | |
}, | |
}; | |
let moves = { | |
left: {x: x - 1, y}, | |
up: {x, y: y - 1}, | |
right: {x: x + 1, y}, | |
down: {x, y: y + 1}, | |
wait: {x, y}, | |
}; | |
let isbot = m => bots.some(([, x, y]) => m.x == x && m.y == y); | |
let whose = n => n ? n == id || Math.abs(id - n) % 3 > 1 ? "me" : "other" : "un"; | |
for (let dir in moves) { | |
let move = moves[dir]; | |
if (move.x >= 0 && move.x < grid.length && move.y >= 0 && move.y < grid.length) | |
painted[isbot(move)][whose(grid[move.x][move.y])].push(dir); | |
} | |
choices = [painted.false.un, painted.false.other, painted.true.un, painted.true.other, painted.false.me, painted.true.me].find(choices => choices.length); | |
let move = choices[Math.random() * choices.length | 0]; | |
return move; | |
} | |
}, | |
{ | |
"name": "NearSightedGreed", | |
"func": function(myself, grid, bots, gameInfo) { | |
let ret = []; | |
let col = myself[0]; | |
let myX = myself[1]; | |
let myY = myself[2]; | |
if(grid[myX][myY] != col){ | |
return "wait"; | |
} | |
if(myX != 0 && grid[myX-1][myY] != col){ | |
ret.push("up") | |
} | |
if(myX != grid.length-1 && grid[myX+1][myY] != col){ | |
ret.push("down") | |
} | |
if(myY != 0 && grid[myX][myY-1] != col){ | |
ret.push("left") | |
} | |
if(myY != grid[0].length && grid[myX][myY+1] != col){ | |
ret.push("right") | |
} | |
return ret[Math.random() * ret.length|0] | |
} | |
}, | |
{ | |
"name": "CandyButton", | |
"func": function(myself, grid, bots, gameInfo) { | |
var mc = myself[0]; | |
var mx = myself[1]; | |
var my = myself[2]; | |
if(grid[mx][my]==0) return "wait"; // Edit: wait when white. | |
if(mx==grid.length-1 && my<grid.length-1) return "down"; | |
if(my==grid.length-1 && mx>0) return "left"; | |
if(mx==0 && my>0) return "up"; | |
if(mx==0 && my==0) return "right"; | |
if(mx%2){ | |
if(my<grid.length-2) return "down"; | |
return "right"; | |
} | |
if(my>0) return "up" | |
return "right"; | |
} | |
}, | |
{ | |
"name": "Borderline", | |
"func": function(myself, grid, bots, gameInfo) { | |
// Check if already on border | |
if (myself[1] == 0 || myself[1] == grid.length-1 || myself[2] == 0 || myself[2] == grid.length-1) { | |
// Move anticlockwise around the border | |
if (myself[1] == 0 && myself[2] != 0 && myself[2] != grid.length-1) { | |
return "down"; | |
} | |
if (myself[1] == 0 && myself[2] == 0) { | |
return "down"; | |
} | |
if (myself[2] == grid.length-1 && myself[1] != 0 && myself[1] != grid.length-1) { | |
return "right"; | |
} | |
if (myself[1] == 0 && myself[2] == grid.length-1) { | |
return "right"; | |
} | |
if (myself[1] == grid.length-1 && myself[2] != 0 && myself[2] != grid.length-1) { | |
return "up"; | |
} | |
if (myself[1] == grid.length-1 && myself[2] == grid.length-1) { | |
return "up"; | |
} | |
if (myself[2] == 0 && myself[1] != 0 && myself[1] != grid.length-1) { | |
return "left"; | |
} | |
if (myself[1] == grid.length-1 && myself[2] == 0) { | |
return "left"; | |
} | |
} else { | |
// Find the nearest border and move to it | |
if (myself[1] <= grid.length-1 - myself[1]) { | |
// Move to left border | |
return "left"; | |
} else { | |
// Move to right border | |
return "right"; | |
} | |
} | |
} | |
}, | |
{ | |
"name": "Jim", | |
"func": function(myself, grid, bots, gameInfo) { | |
var [mc, mx, my] = myself; | |
var output; | |
var allowRetracing = false; | |
var size = 3; | |
var scoreboard = grid.map(column=>column.map(c=>c==mc? 0 : overMap(c, 2, 1, 0))); | |
for (let [bc, bx, by] of bots) if (bc != mc) {log([bc,bx,by],[mc,mx,my]); | |
scoreboard[bx][by] = -100; | |
if (inbounds([bx-2, by])) scoreboard[bx-2][by] = -50; | |
if (inbounds([bx+2, by])) scoreboard[bx+2][by] = -50; | |
if (inbounds([bx, by-2])) scoreboard[bx][by-2] = -50; | |
if (inbounds([bx, by+2])) scoreboard[bx][by+2] = -50; | |
} | |
function scoreOf (x, y) { | |
let score = 0; | |
for (let dx = -size; dx <= size; dx++) { | |
let cx = dx + x; | |
if (cx < 1 || cx >= grid.length-1) continue; | |
for (let dy = -size; dy <= size; dy++) { | |
let cy = dy + y; | |
if (cy < 1 || cy >= grid.length-1) continue; | |
score+= scoreboard[cx][cy]; | |
} | |
} | |
return score; | |
} | |
var storage = this; | |
if (gameInfo[0] < 10) this.timer = 10000; | |
function rescore() { | |
storage.bestScore = -Infinity; | |
var blur = scoreboard.map((column, x)=>column.map((c, y) => { | |
let score = scoreOf(x, y); | |
if (score > storage.bestScore) { | |
storage.bestScore = score; | |
storage.bestX = x; | |
storage.bestY = y; | |
} | |
return score; | |
})); | |
storage.atBest = false; | |
storage.timer = 0; | |
log(blur); | |
} | |
if (this.timer > 200) rescore(); | |
if (grid[mx][my] == 0 && !bots.some(([col, bx, by])=> col != mc && bx==mx && by==my)) return "wait"; | |
// annoying localStorage | |
if (!localStorage.dzaima_pastMoves) { | |
pastMoves = ["-1;0"]; | |
nowMoves = new Array(30).fill("-1;0"); | |
} else { | |
pastMoves = JSON.parse(localStorage.dzaima_pastMoves); | |
nowMoves = JSON.parse(localStorage.dzaima_pastMoves); | |
} | |
nowMoves.push(mx+";"+my); | |
nowMoves.shift(); | |
localStorage.dzaima_pastMoves = JSON.stringify(nowMoves); | |
function log(...args) { | |
// console.log(...args); | |
} | |
function over(col) { // 1 if overrides happen, -1 if overrides don't happen, 0 if override = 0 | |
let res = Math.abs(mc-col) % 3; | |
return res==1? 0 : res==0? 1 : -1; | |
} | |
function overMap(col, best, good, bad, mine = good) { // 1 if overrides happen, -1 if overrides don't happen, 0 if override = 0 | |
let res = Math.abs(mc-col) % 3; | |
return col == mc? mine : res==1? good : res==0? best : bad; | |
} | |
var iwin = col=>over(col) == 1; | |
var zeroes = col=>over(col) == 0; | |
function to([x, y]) { | |
//debugger | |
var LR = x > mx? [mx+1, my] : x < mx? [mx-1, my] : null; | |
var UD = y > my? [mx, my+1] : y < my? [mx, my-1] : null; | |
if (LR && UD) { | |
var LRScore = overMap(LR, 2, 1, 0, 0); | |
var UDScore = overMap(UD, 2, 1, 0, 0); | |
if (LRScore == UDScore) return toPos([LR, UD][Math.random()>.5? 1 : 0]) | |
else if (LRScore > UDScore) return toPos(LR); | |
else return toPos(UD); | |
} else return toPos(LR || UD || [x, y]); | |
} | |
function toPos([x,y]) { | |
if (x > mx) return "right"; | |
if (x < mx) return "left"; | |
if (y < my) return "up"; | |
if (y > my) return "down"; | |
return 'wait'; | |
} | |
function inbounds([x, y]) { | |
// if (x<grid.length && y<grid.length && x>=0 && y>=0) return true; | |
if (x<grid.length-1 && y<grid.length-1 && x>=1 && y>=1) return true; | |
return false; | |
} | |
function get([x,y]) { | |
if (inbounds([x, y])) return grid[x][y]; | |
return 0; | |
} | |
function bestOf (arr) { | |
if (arr.length == 0) return false; | |
var bestScore = -Infinity; | |
var bestPos; | |
for (var [x, y] of arr) { | |
let score = 0; | |
for (var [bcol, bx, by] of bots) { | |
let dist = Math.sqrt((x-bx)**2 + (y-by)**2); | |
let res = over(bcol); | |
let power = res==0? 1 : res==1? 0.4 : 1.4; | |
score+= power * dist; | |
} | |
if (pastMoves.includes(x+";"+y)) score-= 1000000; | |
if (score > bestScore) { | |
bestScore = score; | |
bestPos = [x,y]; | |
} | |
} | |
if (bestScore < -500000) { | |
if (allowRetracing) log("RETRACING"); | |
else return false; | |
} | |
output = to(bestPos); | |
return true; | |
} | |
// log(x,this.bestX, y,this.bestY); | |
var distToBest = Math.abs(this.bestX-mx) + Math.abs(this.bestY-my); | |
if (this.atBest || distToBest < 10) { | |
log("at best; collecting"); | |
this.atBest = true; | |
var orth = [[-1,0],[0,-1],[1,0],[0,1]]; | |
var neighbors = orth | |
.map(([x,y])=>[x+mx, y+my]) | |
.filter(c=>inbounds(c)) | |
.filter(([x,y])=>!bots.some(([bid, bx, by]) => bx==x && by==y)) | |
.map(c=>[c,get(c)]); | |
var best = neighbors.filter(([_, col]) => col != mc && col != 0 && over(col) == 1); | |
if (bestOf(best.map(([pos, col]) => pos))) { | |
log("best"); | |
return output; | |
} | |
var good = neighbors.filter(([_, col]) => col == 0); | |
if (bestOf(good.map(([pos, col]) => pos))) { | |
log("good"); | |
return output; | |
} | |
var okay = neighbors.filter(([_, col]) => over(col) == 0); | |
if (bestOf(okay.map(([pos, col]) => pos))) { | |
log("okay"); | |
return output; | |
} | |
for (let i = 2; i < grid.length; i++) { | |
if (i > 6) allowRetracing = true; | |
neighbors = orth | |
.map(([x, y]) => [x*i + mx, y*i + my]) | |
.filter(c=>inbounds(c)) | |
.map(c=>[c,get(c)]); | |
best = neighbors.filter(([_, col]) => col == 0 || (col != mc && over(col) == 1)); | |
if (bestOf(best.map(([pos, col]) => pos))) { | |
log("best dist"); | |
return output; | |
} | |
okay = neighbors.filter(([_, col]) => over(col) == 0); | |
if (bestOf(okay.map(([pos, col]) => pos))) { | |
log("okay dist"); | |
return output; | |
} | |
} | |
return ['right','left','up','down'][Math.floor(Math.random()*4)]; | |
} else log("going to best"); | |
if (scoreOf(this.bestX, this.bestY) < this.bestScore/2 || distToBest > 20) rescore(); | |
return to([this.bestX, this.bestY]); | |
} | |
}, | |
{ | |
"name": "¯\\_(ツ)_/¯ (Random moves)", | |
"func": function(myself, grid, bots, gameInfo) { | |
return ["up","down","left","right"][Math.random() *4 |0] | |
} | |
} | |
]; |
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
var grid = []; | |
var CSS_COLOR_NAMES = ["Aqua","Bisque","Blue","BlueViolet","Brown","CadetBlue","Chartreuse","Chocolate","Coral","CornflowerBlue","Cornsilk","Crimson","DarkCyan","DarkGoldenRod","DarkGray","DarkGreen","DarkKhaki","DarkMagenta","DarkOliveGreen","Darkorange","DarkOrchid","DarkRed","DarkSalmon","DarkSeaGreen","DarkSlateBlue","DarkSlateGray","DarkSlateGrey","DarkTurquoise","DarkViolet","DeepPink","DeepSkyBlue","DimGray","DimGrey","DodgerBlue","FireBrick","FloralWhite","ForestGreen","Fuchsia","Gainsboro","GhostWhite","Gold","GoldenRod","Gray","Grey","Green","GreenYellow","HoneyDew","HotPink","IndianRed","Indigo","Ivory","Khaki","Lavender","LavenderBlush","LawnGreen","LemonChiffon","LightBlue","LightCoral","LightCyan","LightGoldenRodYellow","LightGray","LightGrey","LightGreen","LightPink","LightSalmon","LightSeaGreen","LightSkyBlue","LightSlateGray","LightSlateGrey","LightSteelBlue","LightYellow","Lime","LimeGreen","Linen","Magenta","Maroon","MediumAquaMarine","MediumBlue","MediumOrchid","MediumPurple","MediumSeaGreen","MediumSlateBlue","MediumSpringGreen","MediumTurquoise","MediumVioletRed","MidnightBlue","MintCream","MistyRose","Moccasin","NavajoWhite","Navy","OldLace","Olive","OliveDrab","Orange","OrangeRed","Orchid","PaleGoldenRod","PaleGreen","PaleTurquoise","PaleVioletRed","PapayaWhip","PeachPuff","Peru","Pink","Plum","PowderBlue","Purple","Red","RosyBrown","RoyalBlue","SaddleBrown","Salmon","SandyBrown","SeaGreen","SeaShell","Sienna","Silver","SkyBlue","SlateBlue","SlateGray","SlateGrey","Snow","SpringGreen","SteelBlue","Tan","Teal","Thistle","Tomato","Turquoise","Violet","Wheat","White","WhiteSmoke","Yellow","YellowGreen"]; | |
var arenaSize = botData.length * 3; | |
var maxRounds = 200; | |
var turnNumber = 1; | |
var interval; | |
var running = false; | |
Array.prototype.shuffle = function() { | |
var i = this.length, j, temp; | |
if ( i == 0 ) return this; | |
while ( --i ) { | |
j = Math.floor( Math.random() * ( i + 1 ) ); | |
temp = this[i]; | |
this[i] = this[j]; | |
this[j] = temp; | |
} | |
return this; | |
} | |
function decodeHTMLEntities(text) { | |
var entities = [ | |
['amp', '&'], | |
['apos', '\''], | |
['#x27', '\''], | |
['#x2F', '/'], | |
['#39', '\''], | |
['#47', '/'], | |
['lt', '<'], | |
['gt', '>'], | |
['nbsp', ' '], | |
['quot', '"'] | |
]; | |
for (var i = 0, max = entities.length; i < max; ++i) | |
text = text.replace(new RegExp('&'+entities[i][0]+';', 'g'), entities[i][1]); | |
return text; | |
} | |
var stringify = function(obj, prop) { | |
var placeholder = '____PLACEHOLDER____'; | |
var fns = []; | |
var json = JSON.stringify(obj, function(key, value) { | |
if (typeof value === 'function') { | |
fns.push(value); | |
return placeholder; | |
} | |
return value; | |
}, 2); | |
json = json.replace(new RegExp('"' + placeholder + '"', 'g'), function(_) { | |
return fns.shift(); | |
}); | |
return 'this["' + prop + '"] = ' + json + ';'; | |
}; | |
function updateBotData() { | |
botData = []; | |
$.ajax( | |
{ | |
url: "https://api.stackexchange.com/2.2/questions/170908/answers?page=1&site=codegolf&pagesize=100&filter=withbody&order=desc&sort=creation", | |
method: "get", | |
dataType: "jsonp", | |
crossDomain: !0, | |
success: function(e){ | |
e.items.forEach( | |
function(e){ | |
try { | |
var answerBody = e.body; | |
answerBody = answerBody.replace(/<[\/]*a[^>]*>/g,""); | |
var regexp = /(?:<h\d>)([^<]+)(?=<\/h\d>)/g; | |
var name = regexp.exec(answerBody)[1]; | |
var regexp2 = /(?:<code>)([^<]+)(?=<\/code>)/g | |
var code = regexp2.exec(answerBody)[1]; | |
var func; | |
eval("func = "+decodeHTMLEntities(code)); | |
botData.push({ | |
name: name, | |
func: func | |
}); | |
} catch(err) { | |
// Do nothing | |
} | |
} | |
) | |
console.log(stringify(botData, "botData")); | |
initialise(); | |
} | |
} | |
); | |
} | |
function runBots() { | |
var bots_array = []; | |
for (var j = 0; j < botData.length; j++) { | |
if (!botData[j].eliminated) { | |
bots_array.push([botData[j].uid, botData[j].x, botData[j].y]); | |
} | |
} | |
for (var i = 0; i < botData.length; i++) { | |
if (!botData[i].eliminated) { | |
var uid = botData[i].uid; | |
var botself = [uid, botData[i].x, botData[i].y]; | |
var gameInfo = [turnNumber, maxRounds]; | |
var botself_copy = JSON.parse(JSON.stringify(botself)); | |
var grid_copy = JSON.parse(JSON.stringify(grid)); | |
var bots_array_copy = JSON.parse(JSON.stringify(bots_array)); | |
var gameInfo_copy = JSON.parse(JSON.stringify(gameInfo)); | |
var response = botData[i].func(botself_copy, grid_copy, bots_array_copy, gameInfo_copy); | |
if (response == "up") { | |
botData[i].y -= 1; | |
if (botData[i].y == -1) { | |
// Prevent bot from leaving arena | |
botData[i].y = 0 | |
} | |
} else if (response == "down") { | |
botData[i].y += 1; | |
if (botData[i].y == arenaSize) { | |
// Prevent bot from leaving arena | |
botData[i].y = arenaSize-1; | |
} | |
} else if (response == "right") { | |
botData[i].x += 1; | |
if (botData[i].x == arenaSize) { | |
// Prevent bot from leaving arena | |
botData[i].x = arenaSize-1; | |
} | |
} else if (response == "left") { | |
botData[i].x -= 1; | |
if (botData[i].x == -1) { | |
// Prevent bot from leaving arena | |
botData[i].x = 0; | |
} | |
} | |
if (grid[botData[i].x][botData[i].y] > 0) { | |
grid[botData[i].x][botData[i].y] = [botData[i].uid, 0, grid[botData[i].x][botData[i].y]][Math.abs(botData[i].uid-grid[botData[i].x][botData[i].y])%3]; | |
} else { | |
grid[botData[i].x][botData[i].y] = botData[i].uid; | |
} | |
} | |
} | |
for (var a = 0; a < botData.length; a++) { | |
for (var b = 0; b < botData.length; b++) { | |
if (!botData[a].eliminated && !botData[b].eliminated && botData[a] != botData[b] && botData[a].x == botData[b].x && botData[a].y == botData[b].y) { | |
grid[botData[a].x][botData[a].y] = 0; | |
} | |
} | |
} | |
var count_array = findArea(); | |
for (var j = 0; j < count_array.length; j++) { | |
if (count_array[j] <= 1 && turnNumber >= 5) { | |
botData[j].eliminated = true; | |
} | |
} | |
} | |
function colourGrid() { | |
var canvas = document.getElementById("playWindow"); | |
var ctx = canvas.getContext("2d"); | |
for (var x = 0; x < arenaSize; x++) { | |
for (var y = 0; y < arenaSize; y++) { | |
ctx.beginPath(); | |
if (grid[x][y] == 0 || (botData[grid[x][y]-1].eliminated)) { | |
ctx.fillStyle = "#FFFFFF"; | |
} else { | |
ctx.fillStyle = botData[grid[x][y]-1].colour; | |
} | |
ctx.fillRect(x*20+1, y*20+1, 18, 18); | |
ctx.closePath(); | |
} | |
} | |
} | |
function drawBots() { | |
var canvas = document.getElementById("playWindow"); | |
var ctx = canvas.getContext("2d"); | |
for (var i = 0; i < botData.length; i++) { | |
if (!botData[i].eliminated) { | |
ctx.beginPath(); | |
ctx.fillStyle = "#000"; | |
var xpos = botData[i].x*20; | |
var ypos = botData[i].y*20; | |
ctx.fillRect(xpos, ypos, 20, 20); | |
ctx.closePath(); | |
ctx.beginPath(); | |
ctx.fillStyle = botData[i].colour; | |
ctx.fillRect(xpos+2, ypos+2, 16, 16); | |
ctx.closePath(); | |
} | |
} | |
} | |
function drawGrid() { | |
var canvas = document.getElementById("playWindow"); | |
canvas.width = arenaSize*20; | |
canvas.height = canvas.width; | |
var ctx = canvas.getContext("2d"); | |
ctx.clearRect(0, 0, canvas.width, canvas.height) | |
ctx.lineWidth = 1; | |
for (var x = 0; x < canvas.width; x+=20) { | |
ctx.beginPath(); | |
ctx.strokeStyle = "#DDDDDD"; | |
ctx.moveTo(x,0); | |
ctx.lineTo(x, canvas.width); | |
ctx.stroke(); | |
ctx.closePath(); | |
} | |
for (var y = 0; y < canvas.width; y+=20) { | |
ctx.beginPath(); | |
ctx.strokeStyle = "#DDDDDD"; | |
ctx.moveTo(0,y); | |
ctx.lineTo(canvas.height, y); | |
ctx.stroke(); | |
ctx.closePath(); | |
} | |
} | |
function findArea() { | |
var count_array = new Array(botData.length); | |
for (var x = 0; x < arenaSize; x++) { | |
for (var y = 0; y < arenaSize; y++) { | |
if (grid[x][y] > 0) { | |
count_array[grid[x][y]-1] = (count_array[grid[x][y]-1] | 0) + 1; | |
} | |
} | |
} | |
return count_array; | |
} | |
function findWinner() { | |
var count_array = findArea(); | |
for (var n = 0; n < count_array.length; n++) { | |
if (count_array[n] === undefined) { | |
count_array[n] = 0; | |
} | |
} | |
var maxArea = Math.max.apply(null, count_array); | |
var message = ""; | |
var end = " wins!" | |
for (var i = 0; i < count_array.length; i++) { | |
if (count_array[i] == maxArea && !botData[i].eliminated) { | |
var name = botData[i].name; | |
var colour = botData[i].colour; | |
if (message.length == 0) { | |
message += "<span style=\"color:"+colour+";\">" + name + "</span>"; | |
} else { | |
message += " and <span style=\"color:"+colour+";\">" + name + "</span>"; | |
end = " win!"; | |
} | |
} | |
} | |
message += end; | |
document.getElementById("roundNum").innerHTML += " - "+message; | |
} | |
function removeBot(id) { | |
botData.splice(id-1, 1); | |
initialise(); | |
} | |
function updateBoard() { | |
document.getElementById("playerTable").innerHTML = "<tr><td style=\"font-size: 1.5em;font-weight: bold\">Player Name</td><td style=\"font-size: 1.5em;font-weight: bold\">Score</td><td style=\"font-size: 1.5em;font-weight: bold\">Eliminated?</td></tr>"; | |
var scores = findArea(); | |
for (var n = 0; n < scores.length; n++) { | |
if (scores[n] === undefined) { | |
scores[n] = 0; | |
} | |
} | |
var maxArea = Math.max.apply(null, scores); | |
var winners = []; | |
for (var i = 0; i < scores.length; i++) { | |
if (scores[i] == maxArea && !botData[i].eliminated) { | |
winners.push(botData[i].name) | |
} | |
} | |
for (var k = 0; k < botData.length; k++) { | |
document.getElementById("playerTable").innerHTML += "<tr><td><span style=\"font-size: 1.5em;font-weight: bold;color:"+botData[k].colour+"\">"+botData[k].name + (winners.indexOf(botData[k].name)>=0?"*":"") + "</span></td><td><span style=\"font-size: 1.5em;font-weight: bold;color:"+botData[k].colour+"\">" + (botData[k].eliminated?0:scores[k]) + "</span></td><td><span style=\"font-size: 1.5em;font-weight: bold;color:"+botData[k].colour+"\">"+(botData[k].eliminated?"Yes":"No")+"</span></td><td><button onclick=\"javascript:removeBot("+botData[k].uid+")\">Remove Bot</button></td></tr>"; | |
} | |
} | |
function doStuff() { | |
turnNumber++; | |
if (turnNumber == maxRounds) { | |
running = false; | |
clearInterval(interval); | |
} | |
runBots(); | |
drawGrid(); | |
colourGrid(); | |
drawBots(); | |
updateBoard(); | |
document.getElementById("roundNum").innerHTML = turnNumber; | |
if (turnNumber == maxRounds) { | |
findWinner(); | |
} | |
} | |
function runGame() { | |
if (!running) { | |
maxRounds = document.getElementById("gameInput").value; | |
var fps = document.getElementById("fpsInput").value; | |
running = true; | |
interval = setInterval(doStuff, 1/fps * 1000); | |
} | |
} | |
function stopGame() { | |
running = false; | |
clearInterval(interval); | |
} | |
function initialise() { | |
stopGame(); | |
localStorage.clear(); | |
document.getElementById("roundNum").innerHTML = "0"; | |
turnNumber = 0; | |
arenaSize = botData.length * 3; | |
grid = []; | |
botData = botData.shuffle(); | |
for (var i = 0; i < arenaSize; i++) { | |
grid.push([]); | |
for (var j = 0; j < arenaSize; j++) { | |
grid[i].push(0); | |
} | |
} | |
var previous_x = []; | |
var previous_y = []; | |
for (var k = 0; k < botData.length; k++) { | |
do { | |
botData[k].x = Math.floor(Math.random()*arenaSize); | |
botData[k].y = Math.floor(Math.random()*arenaSize); | |
} | |
while (previous_x.indexOf(botData[k].x) >= 0 || previous_y.indexOf(botData[k].y) >= 0) | |
previous_x.push(botData[k].x); | |
previous_y.push(botData[k].y); | |
botData[k].colour = CSS_COLOR_NAMES[k]; | |
botData[k].uid = k+1; | |
botData[k].eliminated = false; | |
grid[botData[k].x][botData[k].y] = botData[k].uid; | |
} | |
updateBoard(); | |
drawGrid(); | |
drawBots(); | |
} | |
function addNewBot() { | |
botName = document.getElementById("newBotNameInput").value; | |
eval("botFunction = "+document.getElementById("newBotCodeInput").value); | |
stopGame(); | |
botData.push({ | |
name: botName, | |
func: botFunction | |
}); | |
initialise(); | |
runGame(); | |
} | |
document.addEventListener("DOMContentLoaded", function(event) { | |
initialise(); | |
}); |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Art Attack KoTH</title> | |
<style> | |
@font-face { | |
font-family: ubuntu; | |
src: url(Ubuntu-R.ttf); | |
} | |
@font-face { | |
font-family: ubuntu; | |
src: url(Ubuntu-B.ttf); | |
font-weight: bold; | |
} | |
@font-face { | |
font-family: ubuntu; | |
src: url(Ubuntu-RI.ttf); | |
font-style: italic; | |
} | |
body { | |
background-color: #000000; | |
font-family: ubuntu; | |
color: #ffffff; | |
padding: 20px; | |
} | |
canvas { | |
background-color: #ffffff; | |
} | |
button { | |
padding-top: 5px; | |
padding-bottom: 5px; | |
padding-left: 20px; | |
padding-right: 20px; | |
border-radius: 5px; | |
font-weight: bold; | |
background-color: gainsboro; | |
} | |
.colourKey { | |
padding: 30px; | |
} | |
li { | |
margin: 10px 0; | |
} | |
#playerTable td { | |
padding: 10px; | |
text-align: center; | |
border: 1px solid white; | |
width: 300px; | |
} | |
#playerTable { | |
border-collapse: collapse; | |
} | |
a { | |
font-size: 110%; | |
text-decoration: none; | |
color: blue; | |
} | |
</style> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> | |
<script src="botData.js"></script> | |
<script src="controller.js"></script> | |
</head> | |
<body> | |
<h1>Art Attack - King of the Hill challenge</h1> | |
<h3>Round <span id="roundNum">0</span></h3> | |
<table> | |
<tr> | |
<td><canvas id="playWindow" width=10000 height=10000></canvas><br/><br/> | |
<button onclick="javascript:runGame()">Run Game</button> <button onclick="javascript:stopGame()">Pause Game</button> <button onclick="javascript:initialise()">Reset Game</button> <button onclick="javascript:doStuff()">Step a Single Round</button> | |
</td> | |
<td style="padding:50px"> | |
<table id="playerTable"> | |
<tr><td>Player Name</td><td>Score</td><td>Eliminated</td></tr> | |
</table> | |
<br/><strong>Asterisk denotes the player(s) in the lead</strong> | |
<br/><br/><strong><a href="javascript:updateBotData()">Update Bots from PPCG</a></strong> | |
</td> | |
</tr> | |
</table> | |
<br/><br/> | |
<table> | |
<tr> | |
<td><strong>Game Length: </strong></td><td><input type="text" id="gameInput" value="200"></td> | |
</tr> | |
<tr> | |
<td><strong>FPS: </strong></td><td><input type="text" id="fpsInput" value="5"></td> | |
</tr> | |
</table> | |
<br/><hr> | |
<h2>Add your own bot to the game:</h2> | |
<em>Note that this is not permanent and should only be used for testing</em><br/><br/> | |
<strong>Name: </strong><input type="text" id="newBotNameInput" value=""> | |
<br/><br/> | |
<strong>Code:</strong><br/><br/> | |
<textarea id="newBotCodeInput" rows=10 cols=80 style="padding:10px;font-style:monospace">function(myself, grid, bots, gameInfo) { | |
// Code here | |
return move; | |
}</textarea> | |
<br/><br/> | |
<button onclick="javascript:addNewBot()">Add and run</button> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Latest version: https://github.com/beta-decay/beta-decay.github.io/tree/master/art_attack