Skip to content

Instantly share code, notes, and snippets.

@Radvylf
Created January 11, 2019 04:18
Show Gist options
  • Save Radvylf/0dfb42a78776c6787810fc586076ce9e to your computer and use it in GitHub Desktop.
Save Radvylf/0dfb42a78776c6787810fc586076ce9e to your computer and use it in GitHub Desktop.
You'll need to add your own system of ending games, for now I just have a system for animating games. Put your bots in the run variable, following the example templates.
var Positions = {
SOLDIER: 0,
CANNON: 1,
SPY: 2,
CAPTAIN: 3,
GENERAL: 4
};
var Moves = {
NONE: [0],
MOVE: {
NORTH: [1, 0],
EAST: [1, 1],
SOUTH: [1, 2],
WEST: [1, 3]
},
ATTACK: {
NORTH: [2, 0],
EAST: [2, 1],
SOUTH: [2, 2],
WEST: [2, 3]
},
HEAL: [3]
}
var Seek = {
listBots: function(self = false) {
var ind = current;
return shuffle(other.map((el, ind) => ({
id: el.id,
position: run[ind].position,
team: teams[run[ind].team].id,
location: [data[ind][0], data[ind][1]],
hp: el.hp
})).filter(el => el.hp > 0 && (!self || el.id !== other[ind].id)));
},
seePosition: function(pos) {
for (var i = 0; i < data.length; i++)
if (data[i][0] == pos[0] && data[i][1] == pos[1] && other[i].hp > 0)
return other[i].id;
return null;
},
getById: function(id) {
var ind = other.map(el => el.id).indexOf(id);
if (ind == -1)
return null;
else if (other[ind].hp < 0)
return null;
else
return {
id: other[ind].id,
position: run[ind].position,
team: teams[run[ind].team].id,
location: [data[ind][0], data[ind][1]],
hp: other[ind].hp
};
},
getSelf: function() {
var ind = current;
return {
id: other[ind].id,
position: run[ind].position,
team: teams[run[ind].team].id,
location: [data[ind][0], data[ind][1]],
hp: other[ind].hp
};
},
lookAround: function(dist = 1, mode = 0) {
var ind = current;
var filter = data.map(el => Tools.distanceBetween(el, data[ind]) <= dist);
for (var near = [], i = 0; i < filter.length; i++)
if (filter[i])
near.push({
id: other[i].id,
position: run[i].position,
team: teams[run[i].team].id,
location: [data[i][0], data[i][1]],
hp: other[i].hp
});
if (mode === 0)
return shuffle(near.filter(el => el.hp > 0 && el.id != other[ind].id));
else if (mode === 1)
return {
NORTH: near.filter(el => Tools.dirCodeTo(el.location) == "NORTH"),
EAST: near.filter(el => Tools.dirCodeTo(el.location) == "EAST"),
SOUTH: near.filter(el => Tools.dirCodeTo(el.location) == "SOUTH"),
WEST: near.filter(el => Tools.dirCodeTo(el.location) == "WEST")
};
else if (mode === 2)
return [near.filter(el => Tools.dirCodeTo(el.location) == "NORTH"), near.filter(el => Tools.dirCodeTo(el.location) == "EAST"), near.filter(el => Tools.dirCodeTo(el.location) == "SOUTH"), near.filter(el => Tools.dirCodeTo(el.location) == "WEST")];
},
getTeamById: function(id) {
var ind = Object.keys(teams)[Object.values(teams).map(el => el.id).indexOf(id)];
if (ind == undefined)
return null;
else
return shuffle(teams[ind].members.map(i => ({
id: other[i].id,
position: run[i].position,
team: teams[run[i].team].id,
location: [data[i][0], data[i][1]],
hp: other[i].hp
})).filter(el => el.hp > 0));
}
};
var Tools = {
distanceBetween: function(pos, target) {
return Math.abs(pos[0] - target[0]) + Math.abs(pos[1] - target[1]);
},
distanceTo: function(target) {
var ind = current;
return Tools.distanceBetween(target, data[ind]);
},
dirCodeTo: function(pos, prefSide) {
if (prefSide === undefined)
prefSide = Math.round(Math.random());
var ind = current;
var dist = [pos[0] - data[ind][0], pos[1] - data[ind][1]];
if (!(dist[0] || dist[1]))
return "NONE";
if (Math.abs(dist[0]) > Math.abs(dist[1]) || (prefSide && Math.abs(dist[0]) == Math.abs(dist[1])))
if (dist[0] > 0)
return "EAST";
else
return "WEST";
else
if (dist[1] < 0)
return "NORTH";
else
return "SOUTH";
},
dirCodeFrom: function(from, to, prefSide) {
if (prefSide === undefined)
prefSide = Math.round(Math.random());
var dist = [to[0] - from[0], to[1] - from[1]];
if (!(dist[0] || dist[1]))
return "NONE";
if (Math.abs(dist[0]) > Math.abs(dist[1]) || (prefSide && Math.abs(dist[0]) == Math.abs(dist[1])))
if (dist[0] > 0)
return "EAST";
else
return "WEST";
else
if (dist[1] < 0)
return "NORTH";
else
return "SOUTH";
}
};
var Storage = {
get: function(key) {
var ind = current;
var team = teams[run[ind].team];
var val = team.storage[key];
if (val === undefined)
return undefined;
other[ind].storage.read[key] = val.value;
return JSON.parse(JSON.stringify(val.value));
},
set: function(key, value) {
var ind = current;
var team = teams[run[ind].team];
var val = team.storage[key];
if (val === undefined)
team.storage[key] = {};
other[ind].storage.read[key] = value;
team.storage[key].value = value;
},
getKeys: function() {
var ind = current;
var team = teams[run[ind].team];
return Object.keys(team.storage);
}
};
var Stolen = {
getAll: function() {
var index = current;
if (run[index].position != 2)
return null;
var stolen = other[index].storage.stolen;
if (!stolen)
stolen = other[index].storage.stolen = {
order: [],
data: []
};
var data = [];
for (var i = 0; i < stolen.data.length; i++)
data.push(stolen.data[i]);
return shuffle(data.map((el, ind) => ((el !== undefined) ? {
id: other[ind].id,
data: JSON.parse(JSON.stringify(el.read)),
state: el.state
} : false)).filter(el => el));
},
getData: function(id) {
var index = current;
if (run[index].position != 2)
return null;
var stolen = other[index].storage.stolen;
if (!stolen)
stolen = other[index].storage.stolen = {
order: [],
data: []
};
var keys = stolen.order;
if (!keys.includes(id))
return null;
return JSON.parse(JSON.stringify(stolen.data[id]));
},
getLastId: function() {
var index = current;
if (run[index].position != 2)
return null;
var stolen = other[index].storage.stolen;
if (!stolen)
stolen = other[index].storage.stolen = {
order: [],
data: []
};
if (stolen.order.length)
return stolen.order.slice(-1)[0];
else
return null;
}
};
var run = [
{
name: "Move-Rest",
team: "Example",
position: Positions.GENERAL,
owner: "RedwolfPrograms",
bot: function() {
if (state) {
state = 0;
return Moves.HEAL;
} else {
state = 1;
return Moves.MOVE[["NORTH", "EAST", "SOUTH", "WEST"][(Math.random() * 4) | 0]];
}
}
},
{
name: "Accept my Bribe",
team: "Example",
position: Positions.SPY,
owner: "RedwolfPrograms",
bot: function() {
var other = Seek.listBots().filter(el => el.team !== Seek.getSelf().team)[0];
if (Stolen.getLastId() !== null && state !== Stolen.getLastId())
Storage.set(Stolen.getLastId(), Stolen.getData(Stolen.getLastId()));
if (Tools.distanceTo(other.location) > 1)
return Moves.MOVE[Tools.dirCodeTo(other.location)];
else
return Moves.ATTACK[Tools.dirCodeTo(other.location)];
}
},
{
name: "Firing Squad",
team: "Example",
position: Positions.CANNON,
owner: "RedwolfPrograms",
bot: function() {
var me = Seek.getSelf().location;
var target = Seek.listBots().filter(el => el.team !== Seek.getSelf().team && !(el.location[0] - me[0] && el.location[1] - me[1]))[0];
if (!target)
return Moves.MOVE[["NORTH", "EAST", "SOUTH", "WEST"][(Math.random() * 4) | 0]];
else
return Moves.ATTACK[Tools.dirCodeTo(target.location)];
}
}
];
var data = [];
var other = [];
var arena = Math.ceil(Math.sqrt(run.length)) * 3;
var teams = {};
var current, state, turns, games;
var shuffle = arr => {
arr = arr.slice(0);
for (var j, i = arr.length - 1; i > 0; i--) {
j = (Math.random() * (i + 1)) | 0;
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
};
function teamCheck() {
var t, m, c;
for (var i in teams) {
t = teams[i];
m = t.members;
if ((c = m.filter(el => run[el].position == 4)).length != 1)
console.warn("Team " + i + " had " + ((c.length > 1) ? "too many Generals:\n" + c.map(el => "-" + el.name + " by " + el.owner).join("\n") + "\n" : "no Generals:\n") + "Exclude from competition.");
if (m.filter(el => run[el].position == 0).length / (c = m.filter(el => run[el].position == 1)).length < 3)
console.warn("Team " + i + " had too many Cannons:" + c.map(el => "-" + el.name + " by " + el.owner).join("\n") + "\nExclude from competition.");
if (m.filter(el => run[el].position == 0).length / (c = m.filter(el => run[el].position == 2)).length < 5)
console.warn("Team " + i + " had too many Spies:" + c.map(el => "-" + el.name + " by " + el.owner).join("\n") + "\nExclude from competition.");
if (m.filter(el => run[el].position == 0).length / (c = m.filter(el => run[el].position == 3)).length < 4)
console.warn("Team " + i + " had too many Captains:" + c.map(el => "-" + el.name + " by " + el.owner).join("\n") + "\nExclude from competition.");
if (m.map(el => el.owner).filter((el, ind) => m.indexOf(el) == ind).length < 3)
console.warn("Team " + i + " was not contributed to by enough people.\nExclude from competition.");
}
}
function gameSetup() {
var colors = ["#ff9999", "#99ff99", "#9999ff", "#999999", "#ffff99", "#ff99ff", "#99ffff", "#fedcba", "#dddddd"];
for (var i = 0; i < run.length; i++) {
if (teams[run[i].team])
teams[run[i].team].members.push(i);
else
teams[run[i].team] = {
members: [i],
storage: {
read: {}
},
color: colors[i]
};
run[i].bot.index = i;
run[i].history = {
games: 0,
deaths: [],
kills: []
};
}
var rand = shuffle(Object.keys(teams));
for (i in teams)
teams[i].id = rand.indexOf(i);
games = 0;
}
function prepareGame() {
var width = Math.ceil(Math.sqrt(run.length));
var spaces = width ** 2;
var empty = spaces - run.length;
var pos = [];
for (var i = 0; i < spaces; i++)
pos[i] = undefined;
var p;
for (i = 0; i < empty; i++) {
do
p = (Math.random() * pos.length) | 0;
while (pos[p] === null);
pos[p] = null;
}
var bots = shuffle(run.map((el, ind) => ind));
for (i = 0; i < spaces; i++)
if (pos[i] !== null)
data.push([(i % width) * 3 + 1, Math.floor(i / width) * 3 + 1]);
for (i = 0; i < run.length; i++) {
other[i] = {};
other[i].storage = {
read: {},
state: undefined
};
other[i].hp = [10, 8, 4, 12, 16][run[i].position];
other[i].id = bots.indexOf(i);
run[i].history.games += 1;
}
turns = 0;
games += 1;
}
function runTick() {
for (var moves = [], i = 0; i < run.length; i++) {
current = i;
state = other[i].storage.state;
try {
if (other[i].hp > 0)
moves[i] = run[i].bot() || [0];
else
moves[i] = [0];
} catch (e) {
console.warn("Bot " + run[i].name + " of team " + run[i].team + " by " + run[i].owner + ":\n" + e.stack || e.line);
moves[i] = [0];
}
other[i].storage.state = (state === undefined) ? undefined : parseFloat(state);
}
current = null;
var x, n, pos = [[]];
for (i = 0; i < arena; i++) {
if (!pos[i])
pos[i] = [];
for (n = 0; n < arena; n++)
pos[i][n] = [[], [], [], []];
}
for (i = 0; i < moves.length; i++) {
x = 1;
if (other[i].hp >= 0)
pos[data[i][0]][data[i][1]][3].push([i, moves[i]]);
if (!moves[i][0])
continue;
if (moves[i][0] == 1) {
if (moves[i][1] == 0 && data[i][1] > 0)
pos[data[i][0]][data[i][1] - 1][0].push(i);
else if (moves[i][1] == 1 && data[i][0] < arena - 1)
pos[data[i][0] + 1][data[i][1]][0].push(i);
else if (moves[i][1] == 2 && data[i][1] < arena - 1)
pos[data[i][0]][data[i][1] + 1][0].push(i);
else if (data[i][0] > 0)
pos[data[i][0] - 1][data[i][1]][0].push(i);
} else if (moves[i][0] == 2 && [0, 3, 4].includes(run[i].position)) {
if (moves[i][1] == 0 && data[i][1] > 0)
pos[data[i][0]][data[i][1] - 1][1].push([[2, 0, 0, 2.5, 3.75][run[i].position], i]);
else if (moves[i][1] == 1 && data[i][0] < arena - 1)
pos[data[i][0] + 1][data[i][1]][1].push([[2, 0, 0, 2.5, 3.75][run[i].position], i]);
else if (moves[i][1] == 2 && data[i][1] < arena - 1)
pos[data[i][0]][data[i][1] + 1][1].push([[2, 0, 0, 2.5, 3.75][run[i].position], i]);
else if (run[i][0] > 0)
pos[data[i][0] - 1][data[i][1]][1].push([[2, 0, 0, 2.5, 3.75][run[i].position], i]);
} else if (moves[i][0] == 2 && run[i].position == 1) {
if (moves[i][1] == 0 && data[i][1] > 0)
do
pos[data[i][0]][data[i][1] - x++][1].push([6.25, i]);
while (data[i][1] - x >= 0);
else if (moves[i][1] == 1 && data[i][0] < arena - 1)
do
pos[data[i][0] + x++][data[i][1]][1].push([6.25, i]);
while (data[i][0] + x <= arena - 1);
else if (moves[i][1] == 2 && data[i][1] < arena - 1)
do
pos[data[i][0]][data[i][1] + x++][1].push([6.25, i]);
while (data[i][1] + x <= arena - 1);
else if (data[i][0] > 0)
do
pos[data[i][0] - x++][data[i][1]][1].push([6.25, i]);
while (data[i][0] - x >= 0);
} else if (moves[i][0] == 2) {
if (moves[i][1] == 0 && data[i][1] > 0)
pos[data[i][0]][data[i][1] - 1][2].push(i);
else if (moves[i][1] == 1 && data[i][0] < arena)
pos[data[i][0] + 1][data[i][1]][2].push(i);
else if (moves[i][1] == 2 && data[i][1] < arena)
pos[data[i][0]][data[i][1] + 1][2].push(i);
else if (data[i][0] > 0)
pos[data[i][0] - 1][data[i][1]][2].push(i);
} else if (moves[i][0] == 3) {
if (other[i].hp + 1.25 > [10, 8, 4, 12, 16][run[i].position])
other[i].hp = [10, 8, 4, 12, 16][run[i].position];
else
other[i].hp += 1.25;
}
}
var followers = [];
for (i = 0; i < data.length; i++)
if (other[i].hp >= 0)
followers[i] = pos[data[i][0]][data[i][1]][0].sort((a, b) => run[b].position - run[a].position).map(el => ((moves[el][1] != (moves[i][1] + 2) % 4) ? el : false))[0];
else
followers[i] = null;
var train, trains = [], t, ends = followers.map((el, i) => el === undefined && followers.includes(i)), indv = followers.map((el, i) => el === undefined && !followers.includes(i) && moves[i][0] == 1);
for (i = 0, train = []; i < ends.length; i++) {
t = i;
if (ends[i]) {
train = [i];
while ((t = followers.map(el => el == t).indexOf(true)) != -1)
train.push(t);
} else if (indv[i])
train = [i];
else
continue;
trains.push([].concat(train).reverse());
}
for (i = 0; i < trains.length; i++)
if (data[trains[i][0]][1] > 0 && moves[trains[i][0]][1] == 0 && !pos[data[trains[i][0]][0]][data[trains[i][0]][1] - 1][0].filter(el => el !== trains[i][0]).length && !pos[data[trains[i][0]][0]][data[trains[i][0]][1] - 1][3].filter(el => [0, 2, 3].includes(el[1][0])).length)
for (n = 0; n < trains[i].length; n++)
if (moves[trains[i][n]][1] == 0)
data[trains[i][n]][1] -= 1;
else if (moves[trains[i][n]][1] == 1)
data[trains[i][n]][0] += 1;
else if (moves[trains[i][n]][1] == 2)
data[trains[i][n]][1] += 1;
else
data[trains[i][n]][0] -= 1;
else if (data[trains[i][0]][0] < arena - 1 && moves[trains[i][0]][1] == 1 && !pos[data[trains[i][0]][0] + 1][data[trains[i][0]][1]][0].filter(el => el !== trains[i][0]).length && !pos[data[trains[i][0]][0] + 1][data[trains[i][0]][1]][3].filter(el => [0, 2, 3].includes(el[1][0])).length)
for (n = 0; n < trains[i].length; n++)
if (moves[trains[i][n]][1] == 0)
data[trains[i][n]][1] -= 1;
else if (moves[trains[i][n]][1] == 1)
data[trains[i][n]][0] += 1;
else if (moves[trains[i][n]][1] == 2)
data[trains[i][n]][1] += 1;
else
data[trains[i][n]][0] -= 1;
else if (data[trains[i][0]][1] < arena - 1 && moves[trains[i][0]][1] == 2 && !pos[data[trains[i][0]][0]][data[trains[i][0]][1] + 1][0].filter(el => el !== trains[i][0]).length && !pos[data[trains[i][0]][0]][data[trains[i][0]][1] + 1][3].filter(el => [0, 2, 3].includes(el[1][0])).length)
for (n = 0; n < trains[i].length; n++)
if (moves[trains[i][n]][1] == 0)
data[trains[i][n]][1] -= 1;
else if (moves[trains[i][n]][1] == 1)
data[trains[i][n]][0] += 1;
else if (moves[trains[i][n]][1] == 2)
data[trains[i][n]][1] += 1;
else
data[trains[i][n]][0] -= 1;
else if (data[trains[i][0]][0] > 0 && moves[trains[i][0]][1] == 3 && !pos[data[trains[i][0]][0] - 1][data[trains[i][0]][1]][0].filter(el => el !== trains[i][0]).length && !pos[data[trains[i][0]][0] - 1][data[trains[i][0]][1]][3].filter(el => [0, 2, 3].includes(el[1][0])).length)
for (n = 0; n < trains[i].length; n++)
if (moves[trains[i][n]][1] == 0)
data[trains[i][n]][1] -= 1;
else if (moves[trains[i][n]][1] == 1)
data[trains[i][n]][0] += 1;
else if (moves[trains[i][n]][1] == 2)
data[trains[i][n]][1] += 1;
else
data[trains[i][n]][0] -= 1;
for (i = 0; i < data.length; i++) {
if (other[i].hp <= 0)
continue;
t = pos[data[i][0]][data[i][1]];
if (t[1].length)
other[i].hp -= t[1].reduce((acc, el) => acc + el[0], 0);
if (other[i].hp <= 0) {
run[i].history.deaths.push([games, turns, t[1].map(el => el[1])]);
t[1].forEach(el => {
run[el[1]].history.kills.push([games, turns, i]);
});
continue;
}
t[2].forEach(el => {
if (!other[el].storage.stolen)
other[el].storage.stolen = {
order: [],
data: []
};
var stolen = other[el].storage.stolen;
stolen.data[i] = other[i].storage;
stolen.order.push(i);
});
}
turns += 1;
refreshCanvas();
}
function mouseSelect(e) {
var [x, y] = [e.clientX, e.clientY];
var canvas = document.getElementById("display");
var w = canvas.width;
var scale = w / arena;
if (y > w)
return null;
x = (x / scale) | 0;
y = (y / scale) | 0;
if (!data.map((el, i) => i).filter((el, i) => data[i][0] == x && data[i][1] == y && other[i].hp > 0).length)
return null;
var bot = data.map((el, i) => i).filter((el, i) => data[i][0] == x && data[i][1] == y && other[i].hp > 0)[0];
alert("Bot " + run[bot].name + " of Team " + run[bot].team + " by " + run[bot].owner + ", Position " + ["Soldier", "Cannon", "Spy", "Captain", "General"][run[bot].position]);
}
function prepareCanvas() {
document.body.style.margin = 0;
document.body.innerHTML = "<div style='width: 100vw; height: 100vh'><canvas id='display' style='width: 100vh; height: 100vh;' onclick='mouseSelect(event)'></canvas><div style='width: calc(100vw - 100vh); height: 100vh; position: absolute; right: 0; top: 0;'><div></div><div id='stats'></div></div></div>";
var canvas = document.getElementById("display");
var stats = document.getElementById("stats");
var size = window.innerHeight || document.documentElement.clientHeight;
canvas.width = size;
canvas.height = size;
}
function refreshCanvas() {
var canvas = document.getElementById("display");
var w = canvas.width;
var scale = w / arena;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < data.length; i++) {
if (other[i].hp > 0) {
ctx.fillStyle = teams[run[i].team].color || "#444444";
ctx.fillRect(data[i][0] * scale, data[i][1] * scale, scale, scale);
}
}
ctx.strokeStyle = "#aaaaaa";
ctx.lineWidth = 1;
ctx.beginPath();
for (i = 1; i < arena; i++) {
ctx.moveTo(0, scale * i);
ctx.lineTo(w, scale * i);
ctx.moveTo(scale * i, 0);
ctx.lineTo(scale * i, w);
}
ctx.stroke();
ctx.strokeStyle = "#000000";
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, w);
ctx.lineTo(w, w);
ctx.lineTo(w, 0);
ctx.lineTo(0, 0);
ctx.stroke();
}
prepareCanvas();
gameSetup();
prepareGame();
refreshCanvas();
setInterval(runTick, 500);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment