Skip to content

Instantly share code, notes, and snippets.

@Radvylf
Last active August 9, 2019 16:27
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/a8089fee5921b6118b92f429f8f60083 to your computer and use it in GitHub Desktop.
Save Radvylf/a8089fee5921b6118b92f429f8f60083 to your computer and use it in GitHub Desktop.
Copy/paste into console, add bot functions, run games. One has an event logger, one doesn't.
//Copy and paste bot functions here:
function freeTestBotA(me, others) {
if (me.levels.attack < 5) {
if (me.gold < cost(me.levels.attack))
return farm();
return upgrade("attack");
}
return attack(others[0].uid);
}
function freeTestBotB(me, others) {
if (me.gold >= cost(me.levels.attack))
return upgrade("attack");
if (me.hp < 50)
if (Math.random() < 0.5)
return stun(others[0].uid);
else
return heal();
else
if (Math.random() < 0.5)
return attack(others[0].uid);
else
return shield();
}
//Put bot names and functions below. Set debug to 1 or 2 for event log.
var botData = [
{
name: "FreeTestBotA",
debug: 0,
run: freeTestBotA
},
{
name: "FreeTestBotB",
debug: 0,
run: freeTestBotB
}
];
//Just call this function to test. Errors will not stop the game. Max turns: 1000. Event logger included, set log to false to disable
function runGame(rounds = 1, log = true) {
records = [];
for (let i = 0; i < rounds; i++)
runRound(log);
var ids = [];
for (let i = 0; i < records.length; i++)
ids[i] = i;
ids = ids.sort((a, b) => records[b].worth - records[a].worth);
var results = "Results: ";
for (let b, i = 0; i < ids.length; i++) {
b = ids[i];
results += "\n " + (i < 9 ? " " : "") + (i + 1) + ". " + botData[b].name + ": " + (records[b].worth / rounds);
}
console.log(results);
}
//Load bot into botData
function loadBot(name, debug, run) {
var old = botData.findIndex(el => el.name == name);
if (old != -1)
botData[old] = {
name: name,
debug: debug,
run: run
};
else
botData.push({
name: name,
debug: debug,
run: run
});
}
//Include either UID or name to get information about bot. Will return a boolean indicating whether bot exists, or will return botData is passed "botData", records if passed "records", or internal bot information (HP, worth, etc.) of last round if passed "internal"
function queryBot(info, value) {
if (typeof info == "string" || info instanceof String)
var bot = botData.findIndex(el => el.name == info);
else
var bot = bots.findIndex(el => el.uid == info);
if (bot == -1)
return false;
if (value == "botData")
return botData[bot];
if (value == "records")
return records[bot];
if (value == "internal")
return bots[bot];
return true;
}
//Ignore everything under this, it's internal stuff
var records = [];
var bots = [];
var turns = 0;
var heal = () => ["heal"];
var attack = bot => {
var index = bots.findIndex(el => el.uid == bot && el.hp > 0);
if (index == -1)
return [null, "attack", bot];
return ["attack", index];
};
var shield = () => ["shield"];
var stun = bot => {
var index = bots.findIndex(el => el.uid == bot && el.hp > 0);
if (index == -1)
return [null, "attack", bot];
return ["stun", index];
};
var farm = () => ["farm"];
var upgrade = item => {
if (["heal", "attack", "shield", "farm"].includes(item))
return ["upgrade", item];
return [null, "upgrade", item];
};
var cost = lvl => 2.5 * (lvl ** 2) + 2.5 * lvl + 10;
var turn = () => turns;
var move = code => {
if (!Array.isArray(code))
return "Invalid [" + code + "]";
else if (code[0] == "heal")
return "Healed";
else if (code[0] == "attack")
return "Attacked " + botData[code[1]].name;
else if (code[0] == "shield")
return "Shielded";
else if (code[0] == "stun")
return "Stunned " + botData[code[1]].name;
else if (code[0] == "farm")
return "Farmed";
else if (code[0] == "upgrade")
return "Upgraded " + code[1][0].toUpperCase() + code[1].slice(1);
else if (!code[0] && !code[1])
return "Skipped";
else if (!code[0] && code[1] == "attack")
return "Attacked/Stunned Unknown UID [" + code[2] + "]";
else if (!code[0] && code[1] == "upgrade")
return "Upgraded Unknown Move [" + code[2] + "]";
else if (!code[0] && code[1] == "stun")
return "Stunned by " + code[2];
else if (!code[0] && code[1] && !code[2])
return "No Action [" + code[1] + "]";
else if (!code[0] && code[1] && code[2])
return "No Action [" + code[1] + ": " + JSON.stringify(code[2]) + "]";
else
return "Unknown [" + code[0] + "]";
};
function runRound(log) {
var uids = [];
for (let i = 0; i < 100; i++)
uids[i] = i;
for (let j, i = 99; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
[uids[i], uids[j]] = [uids[j], uids[i]];
}
for (let i = 0; i < botData.length; i++) {
bots[i] = {
uid: uids[i],
hp: 100,
gold: 25,
shield: 0,
stun: false,
attackers: [],
worth: 0,
lvl: {
heal: 0,
attack: 0,
shield: 0,
farm: 0
},
storage: {}
};
records[i] = records[i] || {
worth: 0,
attackers: {}
};
}
turns = 0;
while (bots.filter(el => el.hp > 0).length > 1 && turns < 1000) {
turns += 1;
runTurn(log);
}
}
function runTurn(log) {
var moves = [];
var uids = bots.filter(el => el.hp > 0).map(el => ({
uid: el.uid,
hp: el.hp + el.shield,
worth: el.worth,
attack: el.lvl.attack
}));
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]];
}
for (let ls, u, m, r, b, i = 0; i < bots.length; i++) {
b = bots[i];
if (log && botData[i].debug == 1)
ls = JSON.stringify(b.storage);
if (b.hp > 0 && !b.stun) {
try {
r = botData[i].run(m = {
uid: b.uid,
hp: b.hp,
gold: b.gold,
shield: b.shield,
levels: {
heal: b.lvl.heal,
attack: b.lvl.attack,
shield: b.lvl.shield,
farm: b.lvl.farm
}
}, u = uids.filter(el => el.uid != b.uid), b.storage) || [null];
} catch (e) {
console.error("Error in " + botData[i].name + ":\n" + e.stack);
r = [null];
}
} else {
r = [null, "stun", b.stun];
b.stun = false;
}
if (log && botData[i].debug == 1 && b.hp > 0) {
if (!r[0] && r[1] == "stun")
console.log("[" + turns + "-] " + botData[i].name + ": Stunned by " + r[2]);
else
console.log("[" + turns + "-] " + botData[i].name + ":\n Self: " + JSON.stringify(m) + "\n Others: " + JSON.stringify(u) + "\n Storage: " + ls + "\n Move: " + move(r));
}
if (r[0] == "heal")
b.hp = Math.min(100, b.hp + b.lvl.heal + 5);
moves[i] = r;
}
for (let m, b, n, i = 0; i < moves.length; i++) {
m = moves[i];
b = bots[i];
n = botData[i].name;
if (!m)
continue;
if (m[0] == "attack") {
bots[m[1]].hp = bots[m[1]].hp - Math.max(0, b.lvl.attack * 1.25 + 5 - bots[m[1]].shield);
bots[m[1]].shield = Math.max(0, bots[m[1]].shield - b.lvl.attack * 1.25 - 5);
bots[m[1]].attackers.push(i);
let atkr = records[m[1]].attackers[n];
if (atkr) {
atkr.turn.push(turns);
atkr.damage.push(b.lvl.attack * 1.25 + 5);
} else {
records[m[1]].attackers[n] = {
turn: [turns],
damage: [b.lvl.attack * 1.25 + 5]
};
}
} else if (m[0] == "stun") {
if (bots[m[1]].stun)
bots[m[1]].stun += ", " + n;
else
bots[m[1]].stun = n;
} else if (m[0] == "farm") {
b.gold += b.lvl.farm * 2 + 5;
b.worth += b.lvl.farm * 2 + 5;
records[i].worth += b.lvl.farm * 2 + 5;
b.hp -= 2;
} else if (m[0] == "upgrade" && b.gold >= cost(b.lvl[m[1]])) {
b.lvl[m[1]] += 1;
b.gold -= cost(b.lvl[m[1]] - 1);
}
}
for (let m, b, n, i = 0; i < moves.length; i++) {
m = moves[i];
b = bots[i];
n = botData[i].name;
if (log && botData[i].debug == 2 && b.hp > 0) {
if (!m[0] && m[1] == "stun")
console.log("[" + turns + "+] " + n + ": Stunned by " + m[2]);
else
console.log("[" + turns + "+] " + n + ":\n HP/SHP: " + b.hp + "/" + (b.hp + b.shield) + "\n Attackers: " + (b.attackers.map(el => botData[el].name + " [" + (bots[el].lvl.attack * 1.25 + 5) + "]").join(", ") || "None") + "\n Gold/Worth: " + b.gold + "/" + b.worth + "\n Levels: " + b.lvl.heal + "/" + b.lvl.attack + "/" + b.lvl.shield + "/" + b.lvl.farm + "\n Move: " + move(m));
}
if (m[0] == "shield") {
b.shield += b.lvl.shield * 1.5 + 5;
}
if (b.hp < 0) {
if (log && (b.attackers.length || m == "farm"))
console.log("%c[" + turns + "] " + n + " was killed by " + (b.attackers.map(el => botData[el].name).join(", ") || "overfarming"), "font-weight: bold");
for (let a, j = 0; j < b.attackers.length; j++) {
a = bots[b.attackers[j]];
a.gold += Math.ceil(b.worth / 2);
a.worth += Math.ceil(b.worth / 2);
records[b.attackers[j]].worth += Math.ceil(b.worth / 2);
}
}
b.attackers = [];
}
}
@plokmijnuhby
Copy link

I don't have enough reputation for the chat room, but it seems that when running the logging version, dead bots will repeatedly show "Invalid: [undefined]" until the end of the round. (This will not happen if you only run two bots, as the round ends the moment one of them dies.) Could this be fixed?

@Radvylf
Copy link
Author

Radvylf commented Aug 1, 2019

I don't have enough reputation for the chat room, but it seems that when running the logging version, dead bots will repeatedly show "Invalid: [undefined]" until the end of the round. (This will not happen if you only run two bots, as the round ends the moment one of them dies.) Could this be fixed?

Fixed. Thanks for pointing this out!

@plokmijnuhby
Copy link

Another problem - at the end the results are returned with the names in the wrong order, so each bot will have the score of someone else showing.

@Radvylf
Copy link
Author

Radvylf commented Aug 5, 2019

Another problem - at the end the results are returned with the names in the wrong order, so each bot will have the score of someone else showing.

Are you sure? The scores match up, they're just rearranged from highest to lowest

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