Skip to content

Instantly share code, notes, and snippets.

@Radvylf
Last active January 28, 2021 17:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Radvylf/4d9915632fed1b4d7a7e9407e2a38edf to your computer and use it in GitHub Desktop.
Save Radvylf/4d9915632fed1b4d7a7e9407e2a38edf to your computer and use it in GitHub Desktop.
// To include a bot, place it in the botData object.
// To run a game, use the function runGame(rounds, turns, log).
// Rounds is the number of rounds, turns is the max turns (default is 100000), and log is the log level (0=none, 1=round overview, 2=all collisions/workers)
// To draw a round, use the function drawRound(turns, log, fps, zoom). Defaults to log level 2.
// Example bot is included.
var botData = [
{
name: "ExampleBot",
color: "#aaaaaa",
run: () => dirTo(chars().sort((a, b) => dist(center(), a.pos) - dist(center(), b.pos))[0].pos)
},
{
name: "Honnold",
color: "#0000FF",
run:()=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)
},
{
name: "The Caveman",
color: "#FF0000",
run:()=>{w=bots().sort((a,b)=>a.score-b.score)[0];return self().score<=w.score?dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos):dirTo(w.pos)}
},
{
name: "True Neutral",
color: "#400000",
run: _=>dirTo(chars().sort((a,b)=>dist(a.pos,[0,0])-dist(b.pos,[0,0])+distTo(a.pos)-distTo(b.pos))[0].pos)
},
{
name: "Centrist",
color: "#666666",
run:_=>dirTo(center())
},
{
name: "Rabbit",
color: "#FFC0CB",
run:_=>turn()%1e3?dirTo(chars()[0].pos):build(self().source)
}
];
// _=>{var r,d,e,l,I,N,P,D=distTo,s=self(),p=s[P="pos"],w="_=>dirTo(chars().sort((a,b)=>distTo(a.pos)-distTo(b.pos))[0].pos)",n=w.split``;s.chars.map(c=>n[n[N="indexOf"](c)]="");if(!n.find(_=>_))return build(w);r=chars().filter(c=>!bots().find(b=>b.uid!=s.uid&&dist(b[P],c[P])-D(c[P])<1));if(r.length)return dirTo(r.sort((a,b)=>D(a[P])-D(b[P])+(n[I="includes"](b)-n[I](a))*10)[0][P]);l={n:-p[1],e:p[0],s:p[1],w:-p[0]};d="nesw".split``.sort((a,b)=>l[a]-l[b]);e=bots().filter(b=>b.uid!=s.uid&&b.score>=s.score&&D(b[P])<5).map(b=>d[d[N](dirTo(b[P])[0][0])]="");return[north,east,south,west]["nesw"[N](d.find(x=>x))]()}
var game = {
randPos: (center, any = !1, uid = 0, owner = 0, p = 0.1) => {
var theta, radius, pos;
do {
theta = Math.random() * Math.PI * 2;
radius = 0;
while (Math.random() > p)
radius++;
pos = [Math.trunc(center[0] + Math.cos(theta) * radius), Math.trunc(center[1] + Math.sin(theta) * radius)];
} while (!any && game.bots.find(a => a && a.uid != uid && Math.abs(a.pos[0] - pos[0]) + Math.abs(a.pos[1] - pos[1]) < (a.uid == owner ? 3 : 4)));
return pos;
},
debug: function(){},
log: 0 // 0 = NONE, 1 = SUMMARY, 2 = ALL
};
var north = () => ["north"];
var east = () => ["east"];
var south = () => ["south"];
var west = () => ["west"];
var build = code => ["worker", code];
var drop = {
north: char => ["drop.north", char],
east: char => ["drop.east", char],
south: char => ["drop.south", char],
west: char => ["drop.west", char]
};
var bots = () => game.bots.map(a => ({
uid: a.uid,
owner: a.owner,
original: a.original,
score: a.score,
pos: [...a.pos],
chars: game.uid == a.uid ? [...a.chars] : undefined,
source: game.uid == a.uid ? a.source : undefined
})).sort((a, b) => a.uid - b.uid);
var chars = () => game.chars.map(a => ({
char: a.char,
pos: [...a.pos]
}));
var self = () => {
var bot = game.bots.find(a => a.uid == game.uid);
return bot ? {
uid: bot.uid,
owner: bot.owner,
original: bot.original,
score: bot.score,
pos: [...bot.pos],
chars: [...bot.chars],
source: bot.source
} : null;
};
var owner = () => {
var bot = game.bots.find(a => a.uid == game.bots.find(b => b.uid == game.uid).owner);
return bot ? {
uid: bot.uid,
owner: bot.owner,
original: bot.original,
score: bot.score,
pos: [...bot.pos],
chars: [...bot.chars],
source: bot.source
} : null;
};
var center = () => game.center;
var turn = () => game.turns;
var at = pos => ({
bot: (game.bots.find(b => b.pos[0] == pos[0] && b.pos[1] == pos[1]) || {uid: null}).uid,
chars: chars().filter(c => c.pos[0] == pos[0] && c.pos[1] == pos[1])
});
var dir = (posFrom, pos) => {
if (Math.abs(posFrom[0] - pos[0]) <= Math.abs(posFrom[1] - pos[1]))
return posFrom[1] < pos[1] ? ["north"] : ["south"];
else
return posFrom[0] < pos[0] ? ["west"] : ["east"];
};
var dirTo = pos => {
var bot = game.bots.find(a => a.uid == game.uid);
if (Math.abs(pos[0] - bot.pos[0]) <= Math.abs(pos[1] - bot.pos[1]))
return pos[1] < bot.pos[1] ? ["north"] : ["south"];
else
return pos[0] < bot.pos[0] ? ["west"] : ["east"];
};
var dist = (posFrom, pos) => {
return Math.abs(posFrom[0] - pos[0]) + Math.abs(posFrom[1] - pos[1]);
};
var distTo = pos => {
var bot = game.bots.find(a => a.uid == game.uid);
return Math.abs(pos[0] - bot.pos[0]) + Math.abs(pos[1] - bot.pos[1]);
};
async function runRound(turns = 100000) {
var uids = [];
game.perf = performance.now();
for (let i = 1; i <= botData.length; i++)
uids[i - 1] = i;
for (let j, i = uids.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
[uids[i], uids[j]] = [uids[j], uids[i]];
}
game.bots = [];
game.chars = [];
game.records = game.records || [];
game.uids = [];
for (let i = 0; i < botData.length; i++) {
game.bots[i] = {
uid: uids[i],
owner: uids[i],
original: uids[i],
score: Math.floor(botData[i].run.toString().length * -1 / 2),
chars: [],
pos: game.randPos([0, 0]),
source: botData[i].run.toString(),
run: botData[i].run,
storage: {},
name: botData[i].name || "Bot",
color: botData[i].color || "#000000"
};
game.uids[uids[i]] = i;
game.records[i] = game.records[i] || 0;
}
game.center = [
game.bots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0),
game.bots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / game.bots.reduce((a, b) => a + (b.score + 1), 0)
];
game.charPool = game.bots.map(a => a.source).join("");
for (let i = 0; i < botData.length * 4; i++)
game.chars.push({
char: game.charPool[Math.random() * game.charPool.length | 0],
pos: game.randPos([0, 0]),
game: !0
});
game.cuid = botData.length + 1;
game.turns = 0;
if (!game.fps) {
while (game.chars.length && game.bots.length && game.turns < turns) {
runTurn();
game.turns++;
}
} else {
game.debug();
while (game.chars.length && game.bots.length && game.turns < turns) {
await new Promise(function(resolve) {
setTimeout(resolve, 1000 / game.fps);
});
if (!game.pause) {
runTurn();
game.debug();
game.turns++;
}
}
}
game.bots.map(b => game.records[game.uids[b.original]] += b.score);
if (game.log)
console.log("Round Completed (" + ((performance.now() - game.perf) / 1000).toFixed(3) + "s):\n" + game.bots.map(a => a).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join("\n"));
}
function runTurn() {
var cbots = [];
var npos = [];
var nposl = [];
var nbots = [];
for (let b, p, m, i = 0; i < game.bots.length; i++) {
b = game.bots[i];
game.uid = b.uid;
try {
m = b.run(b.storage);
} catch(e) {
m = ["dead"];
if (game.log == 2)
console.warn("[" + game.turns + "] Error: " + b.name + "\n" + (e.stack || e.message));
for (let j = 0; j < b.chars.length; j++)
game.chars.push({
char: b.chars[j],
pos: game.randPos(b.pos, !0, 0, 0, 0.2),
game: !1
});
}
if (!Array.isArray(m))
m = [];
if (m[0] == "north")
p = [b.pos[0], b.pos[1] - 1];
else if (m[0] == "east")
p = [b.pos[0] + 1, b.pos[1]];
else if (m[0] == "south")
p = [b.pos[0], b.pos[1] + 1];
else if (m[0] == "west")
p = [b.pos[0] - 1, b.pos[1]];
else
p = [...b.pos];
if (m[0] != "dead")
npos.push({
bot: b.uid,
pos: p
});
if (m[0] == "worker" && m[1].split("").reduce((c, d, e) => c && d && (e = c.indexOf(d)) != -1 ? c.filter((f, g) => g != e) : null, [...b.chars])) {
p = game.randPos(b.pos, !1, 0, b.uid);
try {
cbots.push({
uid: game.cuid,
owner: b.uid,
original: b.original,
score: 0,
chars: [],
pos: p,
source: m[1],
run: eval(m[1]),
storage: {},
name: b.name + "*",
color: b.color
});
npos.push({
bot: game.cuid++,
pos: p
});
b.score -= Math.floor(m[1].length / 2);
for (let n, j = 0; j < m[1].length; j++) {
n = b.chars.indexOf(m[1][j]);
b.chars = b.chars.slice(0, n).concat(b.chars.slice(n + 1));
}
if (game.log == 2)
console.log("[" + game.turns + "] New Worker: " + b.name);
} catch(e) {
if (game.log == 2)
console.warn("[" + game.turns + "] Invalid Worker: " + b.name + "\n" + (e.stack || e.message));
}
}
if (typeof m[0] == "string" && m[0].match(/^drop.(north|east|south|west)$/) && b.chars.includes(m[1])) {
b.score--;
for (let j = 0; j < b.chars.length; j++) {
if (b.chars[j] == m[1]) {
b.chars = b.chars.slice(0, j) + b.chars.slice(j + 1);
break;
}
}
if (m[0] == "drop.north")
p = [b.pos[0], b.pos[1] - 1];
else if (m[0] == "drop.east")
p = [b.pos[0] + 1, b.pos[1]];
else if (m[0] == "drop.south")
p = [b.pos[0], b.pos[1] + 1];
else if (m[0] == "drop.west")
p = [b.pos[0] - 1, b.pos[1]];
game.chars.push({
char: m[1],
pos: p,
game: !1
});
}
}
game.bots.push(...cbots);
for (let f, i = 0; i < npos.length; i++) {
if (!(f = nposl.find(a => a.pos[0] == npos[i].pos[0] && a.pos[1] == npos[i].pos[1])))
nposl.push(f = {
pos: [...npos[i].pos],
bots: []
});
f.bots.push(npos[i].bot);
}
for (let n, m, b, i = 0; i < nposl.length; i++) {
n = nposl[i];
if (n.bots.length > 1) {
m = Math.max(...n.bots.map(a => game.bots.find(b => b.uid == a).score));
if (game.bots.filter(a => n.bots.includes(a.uid) && a.score == m).length > 1) {
m += 1;
if (game.log == 2)
console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
} else {
if (game.log == 2)
console.log("[" + game.turns + "] Collision: " + n.bots.map(a => game.bots.find(b => a == b.uid)).sort((a, b) => b.score - a.score).map(a => a.name + " [" + a.score + "]").join(", "));
}
for (let j = 0; j < n.bots.length; j++) {
b = game.bots.find(a => a.uid == n.bots[j]);
if (b.score < m) {
for (let k = 0; k < b.chars.length; k++)
game.chars.push({
char: b.chars[k],
pos: game.randPos(b.pos, !0, 0, 0, 0.2),
game: !1
});
game.records[game.uids[b.original]] += b.score;
} else {
nbots.push({
uid: b.uid,
owner: b.owner,
original: b.original,
score: b.score,
chars: [...b.chars],
pos: n.pos,
source: b.source,
run: b.run,
storage: b.storage,
name: b.name,
color: b.color
});
}
}
} else {
b = game.bots.find(a => a.uid == n.bots[0]);
nbots.push({
uid: b.uid,
owner: b.owner,
original: b.original,
score: b.score,
chars: [...b.chars],
pos: n.pos,
source: b.source,
run: b.run,
storage: b.storage,
name: b.name,
color: b.color
});
}
}
game.center = [
nbots.reduce((a, b) => a + b.pos[0] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0),
nbots.reduce((a, b) => a + b.pos[1] * (b.score + 1), 0) / nbots.reduce((a, b) => a + (b.score + 1), 0)
];
game.charPool = nbots.map(a => a.source).join("");
for (let b, c, i = 0; i < game.chars.length; i++) {
c = game.chars[i];
if (b = nbots.find(a => a.pos[0] == c.pos[0] && a.pos[1] == c.pos[1])) {
b.score++;
b.chars.push(c.char);
if (c.game && game.chars.filter(a => a && a.game).length < nbots.length * 4 && game.bots.map(a => a.original).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length > 1)
game.chars.push({
char: game.charPool[Math.random() * game.charPool.length | 0],
pos: game.randPos([0, 0]),
game: !0
});
game.chars[i] = null;
}
}
game.chars = game.chars.filter(a => a);
game.bots = nbots;
};
function drawRound(turns = 100000, log = 2, fps = 5, zoom = 50) {
var c, ctx, wdim, scale;
document.body.innerHTML = "<canvas></canvas>";
c = document.body.firstChild;
c.style.position = "absolute";
c.style.top = "0";
c.style.left = "0";
c.style.zIndex = "2";
ctx = c.getContext("2d");
game.records = new Array(botData.length).fill(0);
game.log = log;
game.pause = !1;
game.fps = fps;
(window.onresize = function() {
wdim = [window.innerWidth || 600, window.innerHeight || 400];
scale = Math.ceil(wdim[1] / zoom);
c.width = wdim[0];
c.height = wdim[1];
})();
window.onkeydown = function() {
var key = event.code;
if (key == "Escape")
game.pause = !game.pause;
if (key == "ArrowLeft" && game.fps > 1)
game.fps -= 1;
if (key == "ArrowRight")
game.fps += 1;
};
game.debug = function() {
ctx.clearRect(0, 0, wdim[0], wdim[1]);
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.font = Math.floor(scale * 0.6) + "px monospace";
for (let x = -Math.ceil(wdim[0] / 2 / scale), i = wdim[0] / 2 - (Math.ceil(wdim[0] / 2 / scale) - 0.5) * scale; i <= wdim[0]; i += scale, x++) {
for (let b, c, y = -Math.ceil(wdim[1] / 2 / scale), j = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; j <= wdim[1]; j += scale, y++) {
if ((c = game.chars.filter(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))).length) {
for (let k = 0; k < c.length; k++)
ctx.fillText(JSON.stringify(c[k].char).slice(1, -1).replace(/\\"/, "\"").replace(/\\\\/, "\\").replace(/ /, "_"), i + scale / 2, j + scale / 2);
}
if (b = game.bots.find(a => a.pos[0] == Math.floor(x) && a.pos[1] == Math.floor(y))) {
ctx.fillStyle = b.color;
ctx.fillRect(i, j, scale, scale);
ctx.fillStyle = "#000000";
}
}
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i, wdim[1]);
ctx.stroke();
}
for (let i = wdim[1] / 2 - (Math.ceil(wdim[1] / 2 / scale) - 0.5) * scale; i <= wdim[1]; i += scale) {
ctx.beginPath();
ctx.moveTo(0, i);
ctx.lineTo(wdim[0], i);
ctx.stroke();
}
ctx.fillRect(wdim[0] / 2 - 3, wdim[1] / 2 - 3, 7, 7);
};
runRound(turns);
}
function runGame(rounds = 1, turns = 100000, log = 0) {
game.records = new Array(botData.length).fill(0);
game.log = log;
for (let i = 0; i < rounds; i++)
runRound(turns, 0);
console.log("Game Conclusion:\n" + botData.map((a, b) => [a.name, game.records[b]]).sort((a, b) => b[1] - a[1]).map(a => "[" + a[1] + "] " + a[0]).join("\n"));
}
@razetime
Copy link

frick

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment