Gold Hunter KoTH

3

1

This challenge is based off of Gold Collector KoTH. In this KoTH, the aim is to be the winner. Wait, I need to be more specific?

Objective

Collect the most coins without dying. Coins are spread throughout a circular arena, and bots attempt to collect them. If two bots collide, the bot with more coins survives!

Coins

There are two types of coins: Silver, and Gold. Silver are worth 2 coins, and gold are worth 5. Once a coin is collected, another is placed at a random spot in the arena of the same type (silver/gold). At the beginning of a round, 5 coins are placed randomly, and there is a 20% chance of any one of them being gold. When one is collected, the new coin is of the same type. Therefore, it is possible to have a round with silver coins only. If multiple bots land on a coin, the one with the most coins gets it (providing that the two don't collide). Bots can also drop coins as distractions; see "Coin Dropping" section.

Bot collisions

When two bots collide, the bot with more coins survives. The other bot will vanish. The bot who wins will receive all of the enemy's coins, and the collisions with the highest sum of coin values are evaluated first. If bots collide over a coin, the coin is awarded to the winning bot.

Arena

The arena is a circle shape, as well as all of the bots. Bots may leave the arena, but should be aware that no coins can exist outside of it. The radius is calculated with the following equation: (sqrt(botCount) * 3) + 1. All coins and bots are separated at minimum by three spaces between the centers, and all coins and bots have a radius of one space. Coins can be in the same spaces as each other, but cannot generate too close to a bot. The center of the arena is [0,0], and all coordinates are arrays containing two digits (x and y).

Movement

Your bot will be a JavaScript function with no parameters. It will be run once per turn. It will control movement by returning an array with a minimum of 2 elements. Movement is controlled with speed and direction. Speed ranges from 0 (Not moving) to 10 (Full speed). At level 10 speed, your bot is moving at one space per move. There are 16 directions:

0=North
1=2 North, 1 West
2=Northwest
3=2 West, 1 North
4=West
5=2 West, 1 South
6=Southwest
7=2 South, 1 West
8=South
9=2 South, 1 East
10=Southeast
11=2 East, 1 South
12=East
13=2 East, 1 North
14=Northeast
15=2 North, 1 East

The return value will an array: [speedChange, directionChange]. Either can be from -2 to 2, and directionChange must be an integer. Speed cannot be negative, but can be a floating point decimal. If your bot errors or returns nothing, output defaults to [0,0]. Your speed and direction both begin at 0.

Coin Dropping

To drop a coin, use a third element in your return array. Set it to either 2 or 5. After any movements and directional changes, a coin with said value is dropped behind your bot as a distraction. Your bot will ignore this coin. It's value will be subtracted from your coin total.

BotGlobal

In order to obtain information such as other bots, coin locations, and the size of the arena, use BotGlobal:

function myBot() {
    var bg = new BotGlobal();
    var coins = bg.getCoins();
}

Map:

BotGlobal
    getArenaRadius(): Number
    getDistance(pos): Number
    getDirection(pos): Number
    getCoins(includeOwnDropped = false): Array[Coin]
        getLocation(): Array[x,y]
        getValue(): Number
        isOwn(): Boolean
    getBots(includeSelf = false): Array[Bot]
        getLocation(): Array[x,y]
        getCoins(): Number
        getName(): String*
        getSpeed(): Number*
        getDirection(): Number*
        getTeamCode(): String/Null
        getStorage(): Object[Storage]*
            setData(key, value): Object[Storage]
            getData(key): Any type
        isSelf(): Boolean
    getTeams(): Array[Team]
        getMembers(): Array[Bot]
        getCode(): String
        getStorage(): Object[Storage]*
    getTeam(code): Object[Team]
    getSelf(): Object[Bot]

Anything with a * indicates a method which returns null unless used by its owner (So you can't get the name of a bot, or its speed, unless you are that bot). The getArenaRadius() function returns the radius of the arena. The getDistance(pos) function returns the distance between your bot and any set of coordinates (pos). The getDirection(pos) function shows which direction your bot must face in order to head in the direction of a target.

Teams and Storage

You may have noticed two interesting features in the map above: Teams, and Storage. First, storage. Storage is used by bots or teams to hold information between rounds. Storage can only be accessed by the bot that owns it. As for teams, they also have a team storage variable. But what's a team?

Teams are created by the people who create bots. When you create a bot, you can specify a team for it. Teams can conatin up to 1/8 of the total number of bots each, and teammates cannot collide. Instead, they ignore each other. The only way to "give" a teammate coins is to drop a coin and hope the person you intended to give it to arrives first (rather than a bot who just sees a coin and goes for it). Bots can also access the methods with *s for their teammates. If bot mainBot() and bot backupBot() are teammates, they can access each other's names while others cannot.

Example Bots

//Name: SampleBotOne
//Team: SampleBots
function sampleBotOne() {
    var bg = new BotGlobal();
    var dir = bg.getSelf().getDirection();
    var cdir = bg.getDirection(bg.getCoins()[0].getLocation()); //Get direction of first coin
    return [2, cdir - dir]; //If a bot attempts to raise its speed above 10, nothing happens
}

//Name: SampleBotTwo
//Team: SampleBots
function sampleBotNumberTwo() {
    var bg = new BotGlobal();
    var dir = bg.getSelf().getDirection();
    var cdir = bg.getDirection(bg.getCoins()[4].getLocation()); //Get direction of last coin
    return [2, cdir - dir];
}

//Name: RandomBot
//Team: None
function weuwiHwhcui() {
    return [(Math.random() * 2) - 1, Math.round(Math.random() * 4) - 2];
}

Rules

  • No duplicate bots

  • Only information in the BotGlobal system can be used (No accessing BotData, BotDataList, coinPos, teamData, etc.)

  • Your bot may only join a team that you create, unless the owner of another team allows other bots to join

  • You may submit any number of bots

  • When all bots are run in the final competition, there will be 2,000 turns per game, and around 400-800 games

  • Deadline is two weeks past post date at UTC Noon

  • Standard loopholes prohibited

Controller

Copy and paste this into the dev. console on about:blank. Use runTest(fps, turns) multiple times (with the same number of turns), then use interpretData(turns) to see stats on your bot compared to the two sample bots. Also, click on the color column of a bot to place colored lines around the bot in question (To assist in identifying it).

var botDataList = [
    {
        name: "SampleBotOne",
        team: "__samples",
        run: function() {
            var bg = new BotGlobal();
            var csp = bg.getSelf().getSpeed();
            var cdr = bg.getSelf().getDirection();
            var dir = bg.getDirection(bg.getCoins()[0].getLocation());
            var ret = dir - cdr;
            var irt = ret - 16;
            if (Math.abs(irt) < ret)
                return [2,irt];
            return [2,ret];
        }
    }, {
        name: "SampleBotTwo",
        team: "__samples",
        run: function() {
            var bg = new BotGlobal();
            var csp = bg.getSelf().getSpeed();
            var cdr = bg.getSelf().getDirection();
            var coins = bg.getCoins();
            var coin = 0;
            for (var i = 1; i < coins.length; i++) {
                if (bg.getDistance(coins[i].getLocation(), bg.getSelf().getLocation()) < bg.getDistance(coins[coin].getLocation(), bg.getSelf().getLocation()))
                    coin = i;
            }
            var dir = bg.getDirection(bg.getCoins()[coin].getLocation());
            var ret = dir - cdr;
            var irt = ret - 16;
            if (Math.abs(irt) < ret)
                return [2,irt];
            return [2,ret];
        }
    }, { //Your bot here
        name: "",
        team: null,
        run: function(){}
    }
];
var botData = [];
var coinPos = [];
var botCount = botDataList.length;
var arena = Math.sqrt(botCount) * 3 + 1;
var teamData = [];

var distance = (pos1, pos2) => {
    return Math.sqrt(Math.abs(pos1[0] - pos2[0]) ** 2 + Math.abs(pos1[1] - pos2[1]) ** 2);
};
var angCalc = (input) => (Math.atan(input) / Math.PI);
var ang = [angCalc(1/2), (angCalc(1/2) + 0.25) / 2, (angCalc(2) + 0.25) / 2, (angCalc(2) + 0.5) / 2];
var randOnCirc = (mr) => {
    var r = Math.random() * mr;
    var theta = Math.random() * (2 * Math.PI);
    return [r * Math.cos(theta), r * Math.sin(theta)];
};

function BotGlobal() {
    var callerName = arguments.callee.caller.bot;
    this.getArenaRadius = function() {
        return deepClone(arena);
    };
    this.getDistance = function(pos) {
        for (var i = 0; i < botCount; i++) {
            if (botData[i].name === callerName) {
                var loc = botData[i].loc;
                break;
            }
        }
        return Math.sqrt(Math.abs(pos[0] - loc[0]) ** 2 + Math.abs(pos[1] - loc[1]) ** 2);
    };
    this.getDirection = function(pos) {
        for (var i = 0; i < botCount; i++) {
            if (botData[i].name === callerName) {
                var loc = botData[i].loc;
                break;
            }
        }
        var x = pos[0] - loc[0];
        var y = pos[1] - loc[1];
        if (!x && !y)
            return null;
        if (!y)
            return (x < 0) ? 12 : 4;
        if (!x)
            return (y < 0) ? 8 : 0;
        var slope = Math.atan(y / x) / Math.PI;
        if (slope >= ang[0] / 2 && slope <= ang[1])
            return (x > 0) ? 3 : 11;
        if (slope >= ang[1] && slope <= ang[2])
            return (x > 0) ? 2 : 10;
        if (slope >= ang[2] && slope <= ang[3])
            return (x > 0) ? 1 : 9;
        if (slope <= 0 - ang[2] && slope >= 0 - ang[3])
            return (y > 0) ? 15 : 7;
        if (slope <= 0 - ang[1] && slope >= 0 - ang[2])
            return (y > 0) ? 14 : 6;
        if (slope <= 0 - ang[0] && slope >= 0 - ang[1])
            return (y > 0) ? 13 : 5;
        if (slope >= ang[3] || slope <= 0 - ang[3])
            return (y > 0) ? 0 : 8;
        if ((slope <= ang[0] / 2 && slope > 0) || (slope >= 0 - ang[0] && slope < 0))
            return (x > 0) ? 4 : 12;
        return null;
    };
    this.getCoins = function(incOwn) {
        var coins = [];
        for (var c, i = 0; i < coinPos.length; i++) {
            if (incOwn || !(coinPos[i][3] === callerName)) {
                c = new Coin(deepClone(coinPos[i]), callerName);
                coins.push(c);
            }
        }
        return coins;
    };
    this.getBots = function(incSelf) {
        var bots = [];
        for (var b, n, i = 0; i < botDataList.length; i++) {
            b = botData[i];
            if (incSelf || b.name !== callerName)
                bots.push(new Bot(b, callerName));
        }
        return bots;
    };
    this.getTeams = function() {
        var teams = [];
        for (var t, i = 0; i < teamData.length; i++) {
            t = new Team(teamData[i], callerName);
            teams.push(t);
        }
        return teams;
    };
    this.getTeam = function(code) {
        for (var i = 0; i < teamData.length; i++) {
            if (teamData[i].code === code)
                return new Team(teamData[i], callerName);
        }
        return null;
    };
    this.getSelf = function() {
        for (var b, i = 0; i < botCount; i++) {
            b = botData[i];
            if (b.name === callerName)
                return new Bot(b, callerName);
        }
    }
}

function Coin(input, cn) {
    this.getLocation = function() {
        return deepClone([input[0], input[1]]);
    };
    this.isOwn = function() {
        return input[3] === cn;
    };
    this.getValue = function() {
        return deepClone(input[2]);
    }
}

function Bot(input, cn) {
    this.getLocation = function() {
        return deepClone(input.loc);
    };
    this.getDirection = function() {
        if ((input.name === cn) || ((input.team) ? input.team.members.includes(cn) : false))
            return deepClone(input.dir);
        else
            return null;
    };
    this.getSpeed = function() {
        if ((input.name === cn) || ((input.team) ? input.team.members.includes(cn) : false))
            return deepClone(input.speed * 10);
        else
            return null;
    };
    this.getTeamCode = function() {
        return deepClone((input.team) ? input.team.code : null);
    };
    this.getName = function() {
        if ((input.name === cn) || ((input.team) ? input.team.members.includes(cn) : false))
            return deepClone(input.name);
        else
            return null;
    };
    this.isSelf = function() {
        if (input.name === cn)
            return true;
        else
            return false;
    };
    this.getCoins = function() {
        return deepClone(bot.coins);
    };
    this.getStorage = function() {
        if (input.name === cn)
            return new Storage(input, cn);
        else
            return null;
    };
}

function Team(input, cn) {
    this.getMembers = function() {
        var mems = [];
        for (var m, i = 0; i < input.members.length; i++) {
            m = botByName(input.members[i]);
            if (m)
                mems.push(new Bot(botByName(input.members[i]), cn));
        }
        return mems;
    };
    this.getCode = function() {
        return deepClone(input.code);
    };
    this.getStorage = function() {
        if (input.members.includes(cn))
            return new Storage(input, cn);
    };
}

function Storage(input, cn) {
    var stor = input.storage;
    this.getData = function(key) {
        if (stor[key] !== undefined)
            return deepClone(stor[key]);
        else
            return null;
    };
    this.setData = function(key, value) {
        stor[key] = deepClone(value);
        return this;
    };
}

function checkValid(pos) {
    if (botData.length === 0)
        return true;
    for (var b, i = 0; i < botData.length; i++) {
        b = botData[i].loc;
        if (distance(b, pos) <= 3)
            return false;
    }
    return true;
}

function botSetup() {
    botData = [];
    botCount = botDataList.length;
    var n;
    for (var t, b, i = 0; i < botCount; i++) {
        b = botDataList[i];
        if (b.team) {
            t = teamByCode(b.team);
            if (t)
                t.members.push(b.name);
            else
                teamData.push({members:[b.name],code:b.team,storage:{}});
        }
    }
    for (i = 0; i < botCount; i++) {
        var loc;
        do
            loc = randOnCirc(arena - 1);
        while (!(checkValid(loc)));
        botData[i] = {};
        botData[i].loc = loc;
        botData[i].coins = 0;
        botData[i].dir = 0;
        botData[i].speed = 0;
        botData[i].run = botDataList[i].run;
        botData[i].name = botDataList[i].name;
        botData[i].team = (botDataList[i].team) ? teamByCode(botDataList[i].team) : null;
        botData[i].storage = {};
        if (!botDataList[i].targetColor)
            botDataList[i].targetColor = "None";
        botDataList[i].run.bot = botDataList[i].name;
        if (!(botDataList[i].rec))
            botDataList[i].rec = [0, 0, 1];
        else
            botDataList[i].rec[2] += 1;
    }
    for (i = 0; i < 5; i++) {
        var loc;
        do
            loc = randOnCirc(arena - 1);
        while (!(checkValid(loc)));
        coinPos[i] = loc;
        coinPos[i].push((Math.random() * 1.25) | 0);
    }
}

function runBots() {
    var b, d, n, t, s, i, x, l, y, moves = [], locs = [], conf = [];
    for (i = 0; i < botCount; i++) {
        b = botData[i];
        try {
            var ret = botDataList[i].run() || [0, 0];
        } catch(e) {
            var err = e.toString().split(': ');
            console.warn(botDataList[i].name + ': ' + err[0] + ' [' + err[1] + ']');
            var ret = [0, 0];
        }
        moves.push(ret);
        botDataList[i].rec[1] += 1;
    }
    var mov = [[0,1], [1,2], [1,1], [2,1], [1,0], [2,-1], [1,-1], [1,-2], [0,-1], [-1,-2], [-1,-1], [-2,-1], [-1,0], [-2,1], [-1,1], [-1,2]];
    for (i = 0; i < botCount; i++) {
        b = moves[i];
        d = botData[i];
        if (({}).toString.call(b[1]).match(/\s([a-zA-Z]+)/)[1].toLowerCase() == 'number') {
            b[1] = Math.round(b[1]);
            if (Math.abs(b[1]) > 2)
                b[1] = Math.sign(b[1]) * 2;
            if (b[1] + d.dir < 0)
                b[1] += 16;
            d.dir = (d.dir + b[1]) % 16;
        }
        if (({}).toString.call(b[0]).match(/\s([a-zA-Z]+)/)[1].toLowerCase() == 'number') {
            if (Math.abs(b[0]) > 2)
                b[0] = Math.sign(b[0]) * 2;
            if (d.speed + (b[0] / 10) < 0)
                b[0] = 0 - botData[i].speed;
            if (d.speed + (b[0] / 10) > 1)
                b[0] = 1 - botData[i].speed;
            d.speed += b[0] / 10;
        }
        s = d.speed ** 2;
        [x,y] = mov[d.dir];
        t = d.speed / Math.sqrt(x ** 2 + y ** 2);
        locs[i] = [t * x + d.loc[0], t * y + d.loc[1]];
        d.loc = locs[i];
        for (n = 0; n < locs.length; n++) {
            if (n !== i) {
                if (distance(locs[n], locs[i]) < 2 && ((d.team) ? d.team.code : []) !== ((botData[n].team) ? botData[n].team.code : []))
                    conf.push([d.name, botData[n].name]);
            }
        }
    }
    conf.sort((a, b) => ((b[0] + b[1]) - (a[0] + a[1])));
    for (i = 0; i < conf.length; i++) {
        d = conf[i];
        b = [botData.find((x) => (x.name == d[0])), botData.find((x) => (x.name == d[1]))];
        l = [botDataList.find((x) => (x.name == d[0])).rec, botDataList.find((x) => (x.name == d[1])).rec];
        if (b[0] && b[1]) {
            if (b[0].coins < b[1].coins) {
                botCount -= 1;
                l[1][0] += b[0].coins;
                b[1].coins += b[0].coins;
                botData.splice(botData.findIndex((x) => (x.name == d[0])), 1);
            } else if (b[1].coins > b[0].coins) {
                botCount -= 1;
                l[0][0] += b[1].coins;
                b[0].coins += b[1].coins;
                botData.splice(botData.findIndex((x) => (x.name == d[1])), 1);
            } else {
                botCount -= 1;
                botData.splice(botData.findIndex((x) => (x.name == d[0])), 1);
                botCount -= 1;
                botData.splice(botData.findIndex((x) => (x.name == d[1])), 1);
            }
        }
    }
    var sbd = deepClone(botData);
    sbd.forEach((a, i)=>(a.index = i));
    sbd.sort((a, b)=>(a.coins - b.coins));
    for (i = 0; i < botCount; i++) {
        n = coinPos.length;
        while (n--) {
            if (distance(coinPos[n], sbd[i].loc) < 2 && coinPos[n][3] !== sbd[i].name) {
                l = coinPos[n][2];
                botData[sbd[i].index].coins += (l) ? 2 : 5;
                botDataList[sbd[i].index].rec[0] += (l) ? 2 : 5;
                if (!(coinPos[n][3]))
                    newCoin(l);
                coinPos.splice(n, 1);
            }
        }
    }
    var sqrts = [Math.sqrt(0.1), Math.sqrt(0.5)];
    mov = [[0, 1], [sqrts[0], sqrts[0] * 3], [sqrts[1], sqrts[1]], [sqrts[0] * 3, sqrts[0]], [1, 0], [sqrts[0] * 3, 0 - sqrts[0]], [sqrts[1], 0 - sqrts[1]], [sqrts[0], 0 - (sqrts[0] * 3)], [0, -1], [0 - sqrts[0], 0 - (sqrts[0] * 3)], [0 - sqrts[1], 0 - sqrts[1]], [0 - (sqrts[0] * 3), 0 - sqrts[0]], [-1, 0], [0 - (sqrts[0] * 3), sqrts[0]], [0 - sqrts[1], sqrts[1]], [0 - sqrts[0], sqrts[0] * 3]];
    for (i = 0; i < botCount; i++) {
        t = moves[i][2];
        d = (botData[i].dir + 8) % 16;
        l = botData[i].loc;
        if (t && ({}).toString.call(t).match(/\s([a-zA-Z]+)/)[1].toLowerCase() == 'number') {
            t = (t > 2) ? 1 : 0;
            if (botData[i].coins >= (t * 5 || 2)) {
                n = coinPos.push([l[0] + mov[d][0], l[1] + mov[d][1]]) - 1;
                coinPos[n].push(t);
                coinPos[n].push(botData[i].name);
                botData[i].coins -= t * 5 || 2;
            }
        }
    }
}

function newCoin(val) {
    var loc;
    do
        loc = randOnCirc(arena - 1);
    while (!(checkValid(loc)));
    var i = coinPos.push(loc) - 1;
    coinPos[i].push(val);
}

function deepClone(val) {
    return JSON.parse(JSON.stringify(val));
}

function teamByCode(code) {
    for (var i = 0; i < teamData.length; i++) {
        if (teamData[i].code === code)
            return teamData[i];
    }
    return null;
}

function botByName(name) {
    for (var i = 0; i < botData.length; i++) {
        if (botData[i].name === name)
            return botData[i];
    }
    return null;
}

function createCanvas() {
    window.unit = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
    var h = window.unit;
    document.body.style.margin = '0';
    document.body.innerHTML = "<canvas id='arena' width='" + h + "' height='" + h + "'></canvas><div id='table'><div><div class='cell' style='width: calc(60% - 1px)'>Bot Name</div><div class='cell' style='width: calc(20% - 1px)'>Coins</div><div class='cell' style='width: 20%; border-right: none;'>Color</div></div></div>";
    document.head.innerHTML = "<style>.cell{line-height: 15px; font-size: 15px; font-family: Roboto, Arial, Sans-serif; box-sizing: border-box; padding: 5px; border-bottom: 1px solid black; background-color: #fdfdfd; border-right: 1px solid black; display: inline-block;}</style>";
    var ctx = document.getElementById('arena').getContext('2d');
    window.ctx = ctx;
    ctx.beginPath();
    ctx.arc(h / 2, h / 2, h / 2 - 1, 0, 2 * Math.PI);
    ctx.stroke();
    var tab = document.getElementById('table');
    tab.style.width = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - h - 1;
    tab.style.height = h;
    tab.style.backgroundColor = "#f6f6f6";
    tab.style.position = "absolute";
    tab.style.right = 0;
    tab.style.top = 0;
    tab.style.borderLeft = "1px solid black";
    tab.style.overflowY = "scroll";
    for (var b, i = 0; i < botCount; i++) {
        b = botData[i];
        tab.innerHTML += "<div><div class='cell' style='width: calc(60% - 1px)'>" + b.name + "</div><div class='cell' style='width: calc(20% - 1px)'>" + b.coins + "</div><div class='cell' style='width: 20%; border-right: none;' onclick='setColor(this, " + i + ")'>" + botDataList[i].targetColor + "</div></div>"; //Set up targetColor
    }
}

function updateCanvas() {
    var h = unit / 2;
    var u = h / arena;
    ctx.clearRect(0, 0, unit, unit);
    ctx.beginPath();
    ctx.arc(h, h, h, 0, 2 * Math.PI);
    ctx.stroke();
    var colors = {Black: '#000000', Red: '#ff0000', Green: '#006600', Blue: '#0000ff', Grey: '#aaaaaa'};
    for (var c, b, d, i = 0; i < botCount; i++) {
        b = colors[botDataList[i].targetColor];
        d = botData[i];
        if (b) {
            ctx.beginPath();
            ctx.moveTo(0, h - d.loc[1] * u);
            ctx.lineTo(h * 2, h - d.loc[1] * u);
            ctx.moveTo(h - d.loc[0] * u, 0);
            ctx.lineTo(h - d.loc[0] * u, h * 2);
            ctx.strokeStyle = b;
            ctx.stroke();
        }
    }
    ctx.strokeStyle = '#000000';
    for (i = 0; i < coinPos.length; i++) {
        c = coinPos[i];
        ctx.beginPath();
        ctx.arc(h - c[0] * u, h - c[1] * u, u, 0, 2 * Math.PI);
        if (c[2])
            ctx.fillStyle = '#ffd700';
        else
            ctx.fillStyle = '#dddddd';
        ctx.fill();
    }
    var tab = document.getElementById('table');
    tab.innerHTML = "<div><div class='cell' style='width: calc(60% - 1px)'>Bot Name</div><div class='cell' style='width: calc(20% - 1px)'>Coins</div><div class='cell' style='width: 20%; border-right: none;'>Color</div></div>";
    for (i = 0; i < botCount; i++) {
        b = botData[i];
        ctx.beginPath();
        ctx.arc(h - b.loc[0] * u, h - b.loc[1] * u, u, 0, 2 * Math.PI);
        ctx.fillStyle = '#333333';
        ctx.fill();
        tab.innerHTML += "<div><div class='cell' style='width: calc(60% - 1px)'>" + b.name + "</div><div class='cell' style='width: calc(20% - 1px)'>" + b.coins + "</div><div class='cell' style='width: 20%; border-right: none;' onclick='setColor(this, " + i + ")'>" + botDataList[i].targetColor + "</div></div>";
    }
}

function setColor(div, i) {
    var color = div.innerText;
    if (color === 'None')
        botDataList[i].targetColor = 'Black';
    else if (color === 'Black')
        botDataList[i].targetColor = 'Red';
    else if (color === 'Red')
        botDataList[i].targetColor = 'Green';
    else if (color === 'Green')
        botDataList[i].targetColor = 'Blue';
    else if (color === 'Blue')
        botDataList[i].targetColor = 'Grey';
    else
        botDataList[i].targetColor = 'None';
}

function runTest(fps, turns) {
    botSetup();
    createCanvas();
    var clear = window.setInterval(function(){
        runBots();
        updateCanvas();
    }, 1000 / fps);
    setTimeout(function(){
        clearInterval(clear);
    }, (1000 / fps) * turns);
}

function interpretData(rounds) {
    console.group("Bot Stats");
    console.log("Number of Bots:" + botDataList.length);
    console.log("Total Turns Per Game:" + rounds);
    console.log("Total Games:" + botDataList[0].rec[2]);
    for (var b, i = 0; i < botDataList.length; i++) {
        b = botDataList[i];
        console.group(b.name);
        console.log("Total Coins:" + b.rec[0]);
        console.log("Total Turns Survived:" + b.rec[1] + "/" + (rounds * b.rec[2]));
        console.log("Average Coins Per Game:" + ((b.rec[0] * 100) / b.rec[2]) / 100);
        console.log("Average Coins Per Turn:" + Math.round((b.rec[0] * 100) / b.rec[1]) / 100);
        console.log("Average Turns Survived Per Game:" + Math.round((b.rec[1] * 100) / b.rec[2]) / 100 + "/" + rounds);
        console.log("Average Time Survived:" + Math.round(100 * ((b.rec[1] / b.rec[2]) / rounds)) + "%");
        console.groupEnd(b.name);
    }
    console.groupEnd("Bot Stats");
}

Redwolf Programs

Posted 2018-10-12T16:06:24.653

Reputation: 2 561

I honestly don't understand how this is different from the linked challenge, besides the fact that the arena is a circle. – Quintec – 2018-10-12T16:43:02.500

@Quintec It has a different method of moving (Speed/direction instead of NESW), it has the ability to drop coins, and it has teams – Redwolf Programs – 2018-10-12T16:47:14.043

I've remove the reference to a bounty if rep is received, following consensus on meta

– trichoplax – 2018-10-12T17:52:29.633

"No duplicate bots" - how is this measured? At least one character changed, or any behavior change? – Alion – 2018-10-12T19:44:35.767

Also, is it just me or is the arena way too cramped for bots to be able to do anything interesting? There's coins everywhere and basically no breathing space. – Alion – 2018-10-12T19:45:59.197

1Why not allow floating-point directions? – Solomon Ucko – 2018-10-12T19:53:03.263

@SolomonUcko No answer – Redwolf Programs – 2018-10-12T20:33:21.600

No answers