[Question]
  [
> 
> **Note: The survey for community favorite will be released soon**
> 
> 
> 
In this KoTH, the aim is to be the last bot alive. Coins will be placed in random areas, and your bot must get the coins first. If a bot runs into another bot, the bot with more coins wins, and the other bot dies. More details below.
## Coin types
There will be 2 types of coins: gold and silver. Gold adds 5 coins to the bot's strength, and silver adds 2. Once a coin is collected, another coin is placed at another spot on the board. At any given time, there is one gold coin and four silver coins in the arena.
## Bot collisions
In the event of two bots trying to occupy the same space, the one with more coins will stay, and the one with less will...not. The winning bot will gain **85%** of opponents coins (Rounded up). If they are tied, both die. If three or more try to occupy the same space, the most powerful wins, and gets 85% of all the other bot's coins. In the event that the most powerful bot is a tie, **all** of the bots die who tried to enter the space.
## Arena
The arena's side length is calculated with `4 + botCount`. When placing bots in the beginning of the game, random places are chosen. The system ensures that no bots start in the same space, or next to each other. Coins generate randomly, excluding a 3 by 3 square centered on each bot. If a bot is found outside of the arena, it dies instantly. The arena starts at (0,0), or Northwest, in the upper left corner, and the location of a bot is always an integer.
## Your bot
Your bot should be a function, in any object oriented language that has arrays, integers, strings, and functions. Note that all submissions will be converted to Javascript, to make things simple. To store information between moves, use `botNotes.storeData(key, value)` and `botNotes.getData(key, value)`. You may not store or access data any way, other than that which is provided through the parameters and `botNotes`. You should create a function that, when called, returns a string `north`, `east`, `south`, `west`, or `none`. There will be 3 arguments for the function:
* An object with four integers (`locationX`, `locationY`, `coins`, `arenaLength`), your current location, your coins, and the length of the arena
* A multidimensional array with the X and Y coordinates of other bots, and their coin count, ex-`[[0,5,4],[4,7,1],[7,4,12]]`
* An array with the coin locations listed (Gold is always first)
This is a king of the hill challenge, [Standard Loopholes](https://codegolf.meta.stackexchange.com/questions/1061/loopholes-that-are-forbidden-by-default) prohibited. Your function will be run several thousand times, each time allowed one "Move". Note that if the game exceeds **20,000 moves**, the bot with the most coins wins. This will be done 8,000 times, to remove randomness.
**Chatroom:** <https://chat.stackexchange.com/rooms/81347/gold-collectors-koth>
**Prizes:**
**First Place:** 100-point bounty  
**Community Favorite:** 15-point accepted answer
**Winners:**
**First Place:** TBTPTGCBCBA  
**Second Place:** Big King Little Hill  
**Third Place:** Potentially Victorious  
**Fourth Place:** Polite Near-Sighted Drunk Bot  
**Fifth Place:** Safety Coin
  
[Answer]
## BaitBot - JavaScript Node.JS
Why bother chasing or running if you can never catch? Instead, BaitBot finds the nearest coin and waits for a weaker bot to also approach it. When they're both adjacent, baitBot goes for the coin assuming the weaker bot will as well. If baitBot is waiting and a stronger bot approaches, he just grabs the coin and skedaddles. [Try Me!](https://tio.run/##pVVbb9owFH7nV5ynkmguhZS2UwV96C7SpF0eVmmroqhywgGMUnuyTemE@O3s2AlJCLSatheM7XP9vs8nC/7ETabFL3sq1QS32@lSZlYoCSkX9lbZ4BEZKDtHbRhkSkgTwroDkKOFidDojQ2MIe5KJbHLusiNpcWopZ3TukK/lUrTNiHPKsNEGMtlhncq4EVQAI12qSUEX7id93hqAh73EziFR@zlKuPO72cIb6BxP2jd34chhdocy3SLdoUoA84gDV9PmNJyLFFKy2ECPlnwDKV9L3Tdy9lZn3CRisGAReycDWn34fuPr/5STIP9/sdjGOw8/bUv5Ga/8/Wu3sGmNMTcYGU9auG0Lq3PD60Hrdj3tXXUtK4yDovT4tfH2dn3N204cCJSkQv7@4BYyuhFREjyOEpKx44D66OQE5DINQnGKw04HcxIZxKfLVgFwpbCy3JlyOqdMxoXquwZElhAzKYE5U1LW6fNfRqGhFWnTUEjZkjQVFyQVePqNU4K5TcAOnQdveTqX8nLrods1a7Fy3rVd/SSb/FIN00aPk1hhV2NFeq8JsONAewaOrJzjbwkgsFiSZTNNE9rikoi7wq7cTlCelORW6Q34jjaU8kI@uF/c7iXlGKOIWrprx5ZcfPNNskvROmx@OaKXgnX4orGISylFXkTBa6dPnluVAUX3XrAShwKE/MXCNxAH05ODmZVozQGuzlRdF7G7uUoZ3buAoT/1uwxD4K3s@lsn7gm8TyodEEtrCvpXtNMg0pRfuef4TVcUZUaJf/si7qGt5uOi1F0/8C1dt@K@NzNw4TFbjIOIvpzxYbsMkm8rYtEsZvWcJEwiCMapeUaufWSwdCtF@U95e5TjIzqV7lT/CyoP2OuB9aog@3lCcPtHw)
```
function baitBot(me, others, coins) {
  let directions = ['none','east','south','west','north']
  function distanceTo(a) {
    return (Math.abs(a[0] - me.locationX) + Math.abs(a[1] - me.locationY))
  }
  function distanceBetween(a, b){
    return (Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]))
  }
  function adjacentDir(a) {
    //0 = no, 1,2,3,4 = ESWN
    if(distanceTo(a) == 1) {
      if(a[0] > me.locationX){ return 1}
      else if(a[0] < me.locationX) {return 3}
      else if(a[1] > me.locationY) {return 2}
      else{ return 4}
    }
    else {return 0}
  }
  function edibility(a) {
    return me.coins - a[2]
  }
  
  //Find nearest coin and get next to it
  let closestCoin = coins.sort((a,b) => distanceTo(a) - distanceTo(b))[0]
  if(distanceTo(closestCoin) > 1) {
    if(closestCoin[0] > me.locationX){ return 'east'}
    else if(closestCoin[0] < me.locationX){ return 'west'}
    else if(closestCoin[1] < me.locationY){ return 'north'}
    else if(closestCoin[1] > me.locationY){ return 'south'}
  }
  
  //If we're next to a coin and there's a threat close, just grab it
  let nearestThreat = others.filter(a => edibility(a) < 0).sort((a,b) => distanceBetween(a, closestCoin) - distanceBetween(b, closestCoin))[0]
  if(nearestThreat && distanceBetween(nearestThreat, closestCoin) <= 2) {
    return directions[adjacentDir(closestCoin)]
  }
  
  //Otherwise, wait until there's a target also next to the coin. If none are close, just take it
  let targets = others.filter(a => edibility(a) > 0 && distanceBetween(closestCoin, a) <= 3)
  targets.sort((a,b) => distanceBetween(a, closestCoin) - distanceBetween(b, closestCoin))
  if(targets.length > 0 && distanceBetween(targets[0], closestCoin) > 1){
    return directions[0]
  }
  return directions[adjacentDir(closestCoin)]
}
```
[Answer]
# Big King Little Hill | JavaScript
```
function BigKingLittleHill(me, enemies, coins) {
	
	// Is a move safe to execute?
	function isItSafe(x){
			let loc = [x[0] + me.locationX,x[1] + me.locationY];
			return loc[0] >= 0 && loc[0] < me.arenaLength
			&& loc[1] >= 0 && loc[1] < me.arenaLength
			&& enemies
					.filter(enemy => me.coins <= enemy[2])
					.filter(enemy => getDist(enemy,loc) == 1).length === 0;
	}
	
	// Dumb conversion of relative coord to direction string
	function coordToString(coord){
		if (coord[0] == 0 && coord[1] == 0) return 'none';
		if (Math.abs(coord[0]) > Math.abs(coord[1]))
			return coord[0] < 0 ? 'west' : 'east';
		return coord[1] < 0 ? 'north' : 'south';
	}
	
	
	// Calculate a square's zone of control
	function getZOC(x) {
		let n = 0;
		for(let i = 0; i < me.arenaLength;i++){
			for(let j = 0; j < me.arenaLength;j++){
				if (doesAControlB(x, [i,j])) n++;
			}
		}
		return n;
	}
	
	function doesAControlB(a, b) {
		return getEnemyDist(b) > getDist(a, b);
	}
  
	// Distance to nearest enemy
	function getEnemyDist(x) {
			return enemies.filter(enemy => enemy[2] >= me.coins/50).map(enemy => getWeightedDist(enemy, x)).reduce((accumulator, current) => Math.min(accumulator, current));
	}
  
	// Weights distance by whether enemy is weaker or stronger
	function getWeightedDist(enemy, pos) {
		return getDist(enemy, pos) + (enemy[2] < me.coins ? 1 : 0);
	}
  
	function getDist(a, b){
		return (Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]))
	}
	
	//check whether there are coins in our Zone of Control, if yes move towards the closest one
	let loc = [me.locationX,me.locationY];
	let sortedCoins = coins.sort((a,b) => getDist(loc,a) - getDist(loc,b));
	for (let coin of sortedCoins) {
		if (doesAControlB(loc,coin)){
			return coordToString([coin[0] - loc[0],coin[1] - loc[1]]);
		}
	}
	
	//sort moves by how they increase our Zone of Control
	northZOC = [[0,-1], getZOC([loc[0],loc[1]-1])];
	southZOC = [[0,1], getZOC([loc[0],loc[1]+1])];
	westZOC = [[-1,0], getZOC([loc[0]-1,loc[1]])];
	eastZOC = [[1,0], getZOC([loc[0]+1,loc[1]])];
	noneZOC = [[0,0], getZOC([loc[0],loc[1]])];
	let moves = [northZOC,southZOC,westZOC,eastZOC,noneZOC].sort((a,b) => b[1] - a[1]);
	
	//check whether these moves are safe and make the highest priority safe move
	for (let move of moves) {
		if (isItSafe(move[0])) { 
			return coordToString(move[0]);
		}
	}
	//no moves are safe (uh oh!), return the highest priority
	return coordToString(moves[0][0])
}
```
[Try it online!](https://tio.run/##jVZRb9s2EH6WfsXtpZZgxrGWpcWSOMWaDlixDnvogG01/CDLZ4upTGYklTgL8tuzO1KSZccG9pDQPH3Hu/v43Um3@X1uCyPv3InSC3x5WdaqcFIr@CBXv0q1@iydq/AXWVXJGgWgwrVEK6DQUtkUnuI4iqPTU/hkIYe1vkew@RLBacANFrXD93HUnSntJ/eFHieb9CmOoqhCB5UuYALTzXQ8gyGscUSGnNF/ic0027P9PbtkP4OuNopd2et6AmN486bdXrFDblDln1GtXMkOzdNsF5wdBTdl8i6KRktZOTQJGx9hcs0uvny4mnjk4/T7WXoEu0L3UVoXDIKipjCZQJaOKh@PNpQQFfXcEvmxXs@JXXWPxjJnegkGK6qeqC20NgvmdiENBkqtM3RLPY495g/9xdsTv/NkyyWEHXM0aVgIhiwYUmh4HSitcHDZOP2Wu3KUz23nncI17BmzWZr2LqaLc0Vh3sPgAa0bwAUMMKcffPAOMOuAShtXeqTVNf3yxEQNMzd5VdTEBJLS7D813drAwr@UKnNEjDmjqx4RxPzX329IasDls9QUBK6jpTYJG6Q30LIvg0s5HAaJttDbAL19Db1toZ6thUb7001I5kOyETCV4pbYATUceu1SPf6vYUC1JXZ5756QC5iHChoHKutn1pJX1ZzvopWYh/rjAIKUyJqrwnejQsrZuqDXXZa2xzVctaGaLnil6Vbz3ExtL5yej9PROr/bUf6fKFelw0WvA2CTpiODi7rAJMmLol7zlWpDE6U2xKpL2dfLay3VYcROkSGGpZZoip0/wkOJrkQTEqWpAw@Yf6O9NtwvWq3Q7FJwKNE7bfeJf/V4CElHxtV2LryHjDQ83ubZj7W9q97h2y7LuW9OYO4bbQg9exbsodeeQ1MUJRbfunr5H3WHwTCeQdL4qA18bXqkEZUAEuoj2jCunX7IzcKyLxSVtiwSwsf94bwzlffHMeMs9S0ubnzQSQg@YhtdsZin/TlIriJPqZC@Ye6vlFoNfK@xP@fbOzXcxOsGY2@Gp@nT/vzpZuCUAYHV8IrwHoHO8BqYcXimtGGVA3t2LKup1A/MDglJFYYmGB4iNY787KKJw4RNx@Ikm4l2Bk2buCEaPUmZOD/itg5H8cMGz1O0hZ9kYvwKT8a2HsbzsG3xh@DDXTgP/W0242PZBDBfUyCI4G3loq1INKmKJgXRnD3bE8U8XAJLm2/goKCJ7hCIZe2/LXK1gDX1s5dsSY3Lkr0zUhvpHgOEPXqC8kKnu/IHbaXUfY6wnfuNHsFRGbWgrVROT5XeTy6pS9Dld6loX6aHkoyPR7AUgqPEzy8@cySCn7rmu4BzAV33@Z1vtgs4E9B7KV1Alj3HfEAzw/2tnosfxNlM0PrOr@/EuV/P/DqL295r0fAjiYC0ACzN6VsBb3mlSOdhZR9671pd8UhYJf/rkzF9@Q8 "JavaScript (Node.js) – Try It Online")
Big King Little Hill makes decisions based on “zones of control”. It will only pursue coins that are in its zone of control meaning it can reach the coin before any other bot can. When there are no coins in its zone of control it instead moves to maximize the size of its zone of control. Big King Little Hill calculates the zone of control of each of its 5 possible moves and favors the moves that maximize the size of its zone of control. In this way, Big King Little Hill eventually reaches a local maximum (little hill) of control and waits for a coin to be generated within its zone. Additionally, Big King Little Hill rejects any move that could result in its death unless there are no alternatives.
Big King Little Hill is a pessimist (it prefers the term realist) because it does not bother to contest any coin it isn't assured to get. It is also a pacifist in that it does not pursue weaker bots in any sense (although it might step on one if they get in the way). Lastly, Big King Little Hill is a coward that will not jeopardize its own life for any reward unless it absolutely has to.
[Answer]
# First Gen Learning Algorithm | [JavaScript (Node.js)](https://nodejs.org)
```
function run() {
	return ['north','east','south','west'][(Math.random()*4)|0];
}
```
[Try it online!](https://tio.run/##y0osSyxOLsosKNHNy09J/f8/rTQvuSQzP09DU6Gai7MotaS0KE8hWj0vv6gkQ11HPTWxuARIFeeXgrnlqUBubLSGb2JJhl5RYl5Kfq6GppaJZo1BrDVX7f//AA "JavaScript (Node.js) – Try It Online")
Have you ever seen those timelapses of learning algorithms learning to play a game? They often move almost randomly in the first few generations...
[Answer]
# Potentially Victorious | JavaScript
Preferred color for this bot is `#1600a6`.
```
function (me, others, coins)
{
    let huntingTimer = botNotes.getData("huntingTimer");
    let huntedIndex = botNotes.getData("huntedIndex");
    if(!huntingTimer)
    huntingTimer = 0;
    else if(huntingTimer >0)
    huntingTimer--;
    else if(huntingTimer == -1)
    huntingTimer = Math.ceil(20*(1+Math.log2(me.coins/25)));
    else
    huntingTimer++;
    function distanceFromMe(X, Y) { return Math.abs(me.locationX - X) + Math.abs(me.locationY - Y); }
    function U(x, y)
    {
    function distance(X, Y) { return Math.abs(X-x) + Math.abs(Y-y); }
    function gravitation(k, X, Y) { return - k / ( distance(X, Y) + .2 ); }
    function exponential(k, q, X, Y) { return - 5*k * Math.exp(- q * distance(X,Y)); }
    // No going away from the arena.
    if(!((0 <= x) && (x < me.arenaLength) && (0 <= y) && (y < me.arenaLength)))
    {
        return Infinity;
    }
    let reachability = [1, 1, 1, 1, 1];
    let distances = coins.map(c => distanceFromMe(c[0], c[1]));
    for(let i = 0; i < others.length; i++)
    {
        for(let coin = 0; coin < 5; coin++)
            reachability[coin] += (Math.abs(others[i][0] - coins[coin][0]) + Math.abs(others[i][1] - coins[coin][1])) < distances[coin];
    }
    let potential = gravitation(40, coins[0][0], coins[0][1]) / (reachability[0]); // Gold
    // Silver
    for(let i = 1; i < 5; i++)
    {
        potential += gravitation(10, coins[i][0], coins[i][1]) / (reachability[i]);
    }
    others.sort((a, b) => b[2] - a[2]);
    // Other bots
    for(let i = 0; i < others.length; i++)
    {
        if(
            ((Math.abs(me.locationX - others[i][0]) + Math.abs(me.locationY - others[i][1])) < 3) &&
            (huntingTimer == 0) &&
            (me.coins > 25) && 
            (me.coins < (others[0][2]*.9)) &&
            (others[i][2] < me.coins-5) && (others[i][2] >= 10)
        )
        {
            huntingTimer = -10;
            huntedIndex = i;
        }
        if((huntingTimer < 0) && (huntedIndex == i))
           potential += exponential(30, 1, others[i][0], others[i][1]);
        if(others[i][2] >= me.coins)
        {
        // Otherwise, they could eat us, and we will avoid them.
        potential += exponential(-1400, 3, others[i][0], others[i][1]);
        }
    }
    return potential;
    }
    // All possible squares we can move to, with their names.
    let movements = [
    [ "north", U(me.locationX, me.locationY - 1)],
    [ "south", U(me.locationX, me.locationY + 1)],
    [ "east", U(me.locationX + 1, me.locationY)],
    [ "west", U(me.locationX - 1, me.locationY)],
    [ "none", U(me.locationX, me.locationY)]
    ];
    botNotes.storeData("huntingTimer", huntingTimer);
    botNotes.storeData("huntedIndex", huntedIndex);
    // Sort them according to the potential U and go wherever the potential is lowest.
    movements.sort((a, b) => a[1] - b[1]);
    return movements[0][0];
}
```
*(Apologies for sloppy formatting, the 4 spaces indentation for this site doesn't go well with my custom of using tabs.)*
## Rough explanation
I hereby resign at the attempt to update the explanation of the formulae. The coefficients are constantly changing and it's kinda hard to keep the explanation up to date. So I will just explain the general principle.
Each coin and each bot generates a force field with some potential. I just add the potentials from everything together and the bot then goes wherever the potential is the lowest. (Obviously this idea is stolen from physics.)
I use two kinds of potentials. First one is a pseudo-gravitational one (which acts at any range), with
$$U = - \frac{k}{r + \frac 1 5} \cdot \frac{1}{1 + n}.$$
The *k* is a "strength" of the field, and, with this choice of sign, the potential is attractive. The *r* here (and everywhere else) is the distance in the taxicab metric, *r = |x₁ - x₂| + |y₁ - y₂|*.
I use *k = 40* for gold coins and *k = 10* for silver coins. *n* is the number of bots who are nearer to the particular coin than we are. Otherwise, we absolutely ignore the other bots (if we get in the way of a stronger bot, we run away, but that's it). I value gold coins for more than they are worth because otherwise the bots that go primarily after the gold all the time beat me.
The second potential is an exponentially decaying one (which effectively acts only at very small distances). This is generated by the other, mainly the more powerful, bots.
These produce a field with
$$ U = -5 \times 1400 e^{-3r}. $$
This force is prohibitively strong at range 0-1, but decays to almost nothing at the larger distances. (Distance + 1 means cutting the force in 1/20.)
We generally don't attack the other bots intentionally (of course if they get in our way and we step on them, it's their fault), but there is a possibility to do that. If some harsh conditions are fulfilled, we may enter the *hunting mode*, being focused on a single bot. To enter the hunting mode:
1. We must have at least 25 coins. (We need to get some coins first.)
2. They must have at most *(our coins - 5)* coins, and at least 10 coins. (We don't want to be hunting someone who grabs one coin and is suddenly more powerful, and we don't want to be pursuing zero-coin bots either.)
3. We must lag behind the currently leading bot by at least 1/10 of his coins. (You need to be lucky to hunt something, so there is no need to give away a good position just for trying our luck.)
4. We must not be on a *hunting cooldown* (see below).
If all these are satisfied, the hunting mode is activated. For the next 10 rounds, the hunted bot only emits the potential
$$ U = -150 e^{-r}. $$
After these 10 rounds elapse, we enter the hunting cooldown, during which we may not enter hunting mode again. (That is to prevent us from endlessly and fruitlessly chasing one bot while all other happily grab coins.) The hunting cooldown is 20 rounds when we have 25 coins, and increases by 20 coins per each doubling of that. (In other words, the cooldown is `20(1 + log2(c / 25))`.) (We use that because in the endgame, all the huntable bots are most likely dead, and so any hunt will be most likely in vain. Because of that, we want to limit the wasted time. But sometimes, a lucky late-game eating can change everything, so we keep the possibility.)
Finally, the whole arena is placed into an infinite potential well which prevents the bot from escaping.
[Answer]
# The Bot That Plays The Game Cautiously But Can Be Aggressive | JavaScript
**Preferred color:** `#F24100`
**Note:** Although this bot has taken 1st place, it is due to teaming up with "Feudal Noble" at the end and eating him for more coins. Otherwise this bot would have been 3rd. If you are interested in bots that are more powerful individually, checkout [Potentially Victorious](https://codegolf.stackexchange.com/a/170268/81663) and [Big King Little Hill](https://codegolf.stackexchange.com/a/170450/81663).
```
function (me, monsters, coins) {
    var i, monstersCount = monsters.length, phaseSize = Math.round((me.arenaLength - 4) / 4),
        center = (me.arenaLength - 1) / 2, centerSize = me.arenaLength / 4,
        centerMin = center - centerSize, centerMax = center + centerSize, centerMonsters = 0, centerMonstersAvg = null,
        end = 2e4, apocalypse = end - ((me.arenaLength * 2) + 20), mode = null;
    var getDistance = function (x1, y1, x2, y2) {
        return (Math.abs(x1 - x2) + Math.abs(y1 - y2)) + 1;
    };
    var isAtCenter = function (x, y) {
        return (x > centerMin && x < centerMax && y > centerMin && y < centerMax);
    };
    var round = botNotes.getData('round');
    if (round === null || !round) round = 0;
    round++;
    botNotes.storeData('round', round);
    var isApocalypse = (round >= apocalypse && round <= end);
    if (isApocalypse) {
        mode = botNotes.getData('mode');
        if (mode === null || !mode) mode = 1;
    }
    for (i = 0; i < monstersCount; i++) if (isAtCenter(monsters[i][0], monsters[i][1])) centerMonsters++;
    var lc = botNotes.getData('lc');
    if (lc === null || !lc) lc = [];
    if (lc.length >= 20) lc.shift();
    lc.push(centerMonsters);
    botNotes.storeData('lc', lc);
    if (lc.length >= 20) {
        centerMonstersAvg = 0;
        for (i = 0; i < lc.length; i++) centerMonstersAvg += lc[i];
        centerMonstersAvg = centerMonstersAvg / lc.length;
    }
    var getScore = function (x, y) {
        var score = 0, i, chaseFactor = 0.75, coinFactor = 1;
        if (monstersCount < phaseSize) {
            chaseFactor = 0;
            coinFactor = 0.25;
        } else if (monstersCount < phaseSize * 2) {
            chaseFactor = 0;
            coinFactor = 0.5;
        } else if (monstersCount < phaseSize * 3) {
            chaseFactor = 0.5;
            coinFactor = 0.75;
        }
        if (isApocalypse) {
            if (mode === 1) {
                var centerDistance = getDistance(x, y, center, center);
                if (centerDistance <= 3) {
                    mode = 2;
                } else {
                    score += 5000 / (centerDistance / 10);
                }
            }
            if (mode === 2) chaseFactor = 1000;
        }
        for (i = 0; i < monstersCount; i++) {
            var monsterCoins = monsters[i][2], monsterDistance = getDistance(x, y, monsters[i][0], monsters[i][1]);
            if (me.coins > monsterCoins && monsterDistance <= 3) {
                score += (Math.min(5, monsterCoins) * chaseFactor) / monsterDistance;
            } else if (me.coins <= monsterCoins && monsterDistance <= 3) {
                score -= (monsterDistance === 3 ? 50 : 10000);
            }
        }
        for (i = 0; i < coins.length; i++) {
            var coinDistance = getDistance(x, y, coins[i][0], coins[i][1]),
                coinDistanceCenter = getDistance(center, center, coins[i][0], coins[i][1]),
                coinValue = (i === 0 ? 250 : 100), coinCloserMonsters = 0;
            for (var j = 0; j < monstersCount; j++) {
                var coinMonsterDistance = getDistance(monsters[j][0], monsters[j][1], coins[i][0], coins[i][1]);
                monsterCoins = monsters[j][2];
                if (
                    (coinMonsterDistance < coinDistance && monsterCoins >= me.coins / 2) ||
                    (coinMonsterDistance <= coinDistance && monsterCoins >= me.coins)
                ) {
                    coinCloserMonsters++;
                }
            }
            var coinMonsterFactor = (100 - ((100 / monstersCount) * coinCloserMonsters)) / 100;
            if (coinMonsterFactor < 1) coinMonsterFactor *= coinFactor;
            if (coinMonsterFactor >= 1) coinMonsterFactor *= 15;
            score += ((coinValue * coinMonsterFactor) / coinDistance) + (centerMonstersAvg === null || centerMonstersAvg > 1.75 ? -1 * (50 / coinDistanceCenter) : 200 / coinDistanceCenter);
        }
        return score + Math.random();
    };
    var possibleMoves = [{x: 0, y: 0, c: 'none'}];
    if (me.locationX > 0) possibleMoves.push({x: -1, y: 0, c: 'west'});
    if (me.locationY > 0) possibleMoves.push({x: -0, y: -1, c: 'north'});
    if (me.locationX < me.arenaLength - 1) possibleMoves.push({x: 1, y: 0, c: 'east'});
    if (me.locationY < me.arenaLength - 1) possibleMoves.push({x: 0, y: 1, c: 'south'});
    var topCommand, topScore = null;
    for (i = 0; i < possibleMoves.length; i++) {
        var score = getScore(me.locationX + possibleMoves[i].x, me.locationY + possibleMoves[i].y);
        if (topScore === null || score > topScore) {
            topScore = score;
            topCommand = possibleMoves[i].c;
        }
    }
    if (isApocalypse) botNotes.storeData('mode', mode);
    return topCommand;
}
```
This bot (aka "TBTPTGCBCBA") tries to make the best decision possible by generating a score for each possible move and selects the move with higher score for each turn.
The scoring system has many details which are evolved since the start of the challenge. They can be described generally like this:
* The closer the coins are to a possible move, the more score that move gets. If a coin has no other possible contestants, the score goes even higher. If a coin has other possible contestants, the score goes lower.
* If another bot is close to a possible move and has less coins, depending on the phase of the game, it could mean more score for that move. So it is casual for "TBTPTGCBCBA" to eat a few of other bots in each game.
* If another bot is close to a possible move with equal or more points, that move gets enough negative score to make sure death is avoided. Of course there could be some cases that all possible moves are bad and death can't be avoided, but that is very rare.
* There is a mechanism to keep track of number of bots in middle of the board for last 20 turns. If average is low enough, all moves towards coins in the middle get more score and if the average is high, then all moves towards coins in the middle get lower score. This mechanism allows to avoid conflicts with "Feudal Noble". Since the "Feudal Noble" is always in the middle (unless it is being chased), the average number of bots in the middle goes up and "TBTPTGCBCBA" understands to avoid middle if there is a better option outside of middle area. If "Feudal Noble" dies, the average goes down and "TBTPTGCBCBA" understands that it can use the middle.
* There are some factors that dynamically change based on the phase of the game (detected from number of bots alive), these factors affect the scoring in each of above items.
* This bot has a special ability. Over time it grows tired of the selfishness of "Feudal Noble" and the oppression of peasants. At the right moment, it will rise to end the unpleasant Feudalism system. A successful attempt not only helps poor peasants, but also provides a higher win chance due to the coins taken from the "Feudal Noble".
[Answer]
# Safety coin | JavaScript
```
SafetyCoin=(myself,others,coins)=>{
  x=myself.locationX;
  y=myself.locationY;
  power=myself.coins;
  arenaSize=myself.arenaLength;
  dist=0;
  optimalCoin=7;
  optimalDist=11*arenaSize;
  for(i=0;i<coins.length;i++){
    enemyDist=3*arenaSize;
    dist=Math.abs(x-coins[i][0])+Math.abs(y-coins[i][1])
    for(j=0;j<others.length;j++){
      if(i==0){
        if(others[j][2]+5>=power){
          enemyDist=Math.min(enemyDist,Math.abs(others[j][0]-coins[i][0])+Math.abs(others[j][1]-coins[i][1]))
        }
      }
      else{
        if(others[j][2]+2>=power){
          enemyDist=Math.min(enemyDist,Math.abs(others[j][0]-coins[i][0])+Math.abs(others[j][1]-coins[i][1]))
        }
      }
    }
    if(enemyDist>dist){
      if(i==0){
        if(dist/5<optimalDist){
          optimalDist=dist/5;
          optimalCoin=i;
        }
      }
      else{
        if(dist/2<optimalDist){
          optimalDist=dist/2;
          optimalCoin=i;
        }
      }
    }
  }
  if(optimalCoin==7){
    safeDir=15;
    if(x==0){safeDir-=8;}
    if(x==arenaSize-1){safeDir-=2;}
    if(y==0){safeDir-=1;}
    if(y==arenaSize-1){safeDir-=4;}
    for(i=0;i<others.length;i++){
      if(others[i][2]>=power){
        if(Math.abs(x-others[i][0])+Math.abs(y-others[i][1])==2){
          if(x-others[i][0]>0){safeDir-=8;}
          if(x-others[i][0]<0){safeDir-=2;}
          if(y-others[i][1]>0){safeDir-=1;}
          if(y-others[i][1]<0){safeDir-=4;}
        }
      }
    }
    directions=["north","east","south","west"];
    if(safeDir!=0){
      tmp="";
      tmp+="0".repeat(Math.max(Math.sqrt(arenaSize)/2|0,y-(arenaSize/2|0)));
      tmp+="2".repeat(Math.max(Math.sqrt(arenaSize)/2|0,(arenaSize/2|0)-y));
      tmp+="1".repeat(Math.max(Math.sqrt(arenaSize)/2|0,(arenaSize/2|0)-x));
      tmp+="3".repeat(Math.max(Math.sqrt(arenaSize)/2|0,x-(arenaSize/2|0)));
      rnd=tmp[Math.random()*tmp.length|0];
      while(!(2**rnd&safeDir)){rnd=tmp[Math.random()*tmp.length|0];}
      return directions[rnd];
    }
    return "none";//the only safe move is not to play :P
  }
  distX=coins[optimalCoin][0]-x;
  distY=coins[optimalCoin][1]-y;
  if(Math.abs(distX)>Math.abs(distY)){
    if(distX>0){return "east";}
    else{return "west";}
  }
  else{
    if(distY>0){return "south";}
    else{return "north";}
  }
}
```
This bot heads straight towards a weighted valued coin (value/distance) which it can not die from reaching at the same time or after another bot. If there is no valid coin with this property ~~it sits where it is~~ the bot now moves in a random safe direction(safety means that if a bot moves towards it, safety coin cannot collide. This allows the bot to swap places with another bot if immediately next to it), weighted towards the center of the arena.
[Answer]
# The GUT, JavaScript
```
function gut(me, others, coins) {
    // Prepare values for the calculation
    var x = me.locationX;
    var y = me.locationY;
    var cMe = me.coins+1;
    var arenaLen = me.arenaLength;
    var objects = [];
    // Add bots to objects
    for (var i = 0; i < others.length; i++) {
        objects.push([others[i][0],others[i][1],others[i][2]/cMe]);
    }
    // Add coins to objects
    for (var j = 0; j < coins.length; j++) {
        var coinVal = 0;
        if (j == 0) {
            // Gold has a higher coin value
            coinVal = -10;
        } else {
            // Silver has a lower coin value
            coinVal = -5;
        }
        objects.push([coins[j][0],coins[j][1],coinVal/cMe]);
    }
    // Perform the calculation
    // x acceleration
    var x_acceleration = 0;
    for (var k=0; k < objects.length; k++) {
        var kval = objects[k][2];
        var xval = objects[k][0];
        x_acceleration += 200*kval/cMe*(x-xval)*Math.exp(Math.pow(kval,2)-50*Math.pow(x-xval,2));
    }
    // y acceleration
    var y_acceleration = 0;
    for (var l=0; l < objects.length; l++) {
        var kval = objects[l][2];
        var yval = objects[l][1];
        y_acceleration += 200*kval/cMe*(y-yval)*Math.exp(Math.pow(kval,2)-50*Math.pow(y-yval,2));
    }
    // Compare the values
    if (Math.abs(y_acceleration)>Math.abs(x_acceleration)) {
        if (y_acceleration < 0) {
            // Don't fall off the edge
            if (y>0) {
                return "north";
            } else {
                return "none";
            }
        } else {
            if (y<arenaLen-1) {
                return "south";
            } else {
                return "none";
            }
        }
    } else if (Math.abs(y_acceleration)<Math.abs(x_acceleration)) {
        if (x_acceleration < 0) {
            if (x>0) {
                return "west";
            } else {
                return "none";
            }
        } else {
            if (x<arenaLen-1) {
                return "east";
            } else {
                return "none";
            }
        }
    } else {
        return "none";
    }
}
```
With [Potentially Victorious](https://codegolf.stackexchange.com/a/170268/30525) we've got two fields: the bot field and the coin field. However, nature isn't that complicated. It's time to unify the two fields to produce the ***Grand Unified Theory***.
Firstly, we need to work out what the potential of the field is. Assuming our own bot doesn't influence the field in any way, we can write this as:
$$V = \sum\_\limits{n} k\_n\left(e^{k\_n^2-100(x-x\_n)^2}+e^{k\_n^2-100(y-y\_n)^2}\right)$$
Where \$k\_n\$ is the "relative property" of the object and \$(x\_n, y\_n)\$ are the coordinates of each object.
The relative property of the object is calculated like so:
$$k = \frac{c\_{\text{object}}}{c\_{\text{me}}}$$
Where \$c\$ is the coin value. (The coin value is positive for bots and negative for coins). And \$c\_{\text{me}} = c\_{\text{self}} + 1\$ where \$c\_{\text{self}}\$ is my own coin value (with 1 correction to prevent a division by zero).
**Let's just call this correction part of [Modified Betanian Dynamics (MOBD)](https://en.wikipedia.org/wiki/Modified_Newtonian_dynamics).**
We can also find the kinetic energy as:
$$T = \frac{1}{2}c\_{\text{me}}(\dot{x}^2 + \dot{y}^2)$$
We can now calculate the action:
$$\begin{align}\text{Action} &= \int^b\_a (T-V)dt\\&=\int^b\_a \left(\frac{1}{2}c\_{\text{me}}(\dot{x}^2 + \dot{y}^2) - \sum\_\limits{n} k\_n\left(e^{k\_n^2-100(x-x\_n)^2}+e^{k\_n^2-100(y-y\_n)^2}\right)\right) dt\end{align}$$
And so the Lagrangian of our bot in the coin-bot field is:
$$\mathcal{L} = \frac{1}{2}c\_{\text{me}}(\dot{x}^2 + \dot{y}^2) - \sum\_\limits{n} k\_n\left(e^{k\_n^2-100(x-x\_n)^2}+e^{k\_n^2-100(y-y\_n)^2}\right)$$
We now need to solve the Euler-Lagrange equations:
$$\frac{d}{dt} \frac{\partial \mathcal{L}}{\partial \dot{x}} = \frac{\partial \mathcal{L}}{\partial x}$$
and:
$$\frac{d}{dt} \frac{\partial \mathcal{L}}{\partial \dot{y}} = \frac{\partial \mathcal{L}}{\partial y}$$
So:
$$\frac{d}{dt} \frac{\partial \mathcal{L}}{\partial \dot{x}} = \frac{d}{dt} \left[c\_{\text{me}}\dot{x}\right] = c\_{\text{me}}\ddot{x}$$
$$\frac{\partial \mathcal{L}}{\partial x} = \sum\limits\_n 200 k\_n \left(x - x\_n\right) e^{k\_n^2 - 100(x-x\_n)^2}$$
$$\Rightarrow \ddot{x} = \sum\limits\_n \frac{200k\_n}{c\_{\text{me}}} \left(x - x\_n\right) e^{k\_n^2 - 100(x-x\_n)^2}$$
And also:
$$\frac{d}{dt} \frac{\partial \mathcal{L}}{\partial \dot{y}} = \frac{d}{dt} \left[c\_{\text{me}}\dot{y}\right] = c\_{\text{me}}\ddot{y}$$
$$\frac{\partial \mathcal{L}}{\partial y} = \sum\limits\_n 200 k\_n \left(y - y\_n\right) e^{k\_n^2 - 100(y-y\_n)^2}$$
$$\Rightarrow \ddot{y} = \sum\limits\_n \frac{200k\_n}{c\_{\text{me}}} \left(y - y\_n\right) e^{k\_n^2 - 100(y-y\_n)^2}$$
Now we don't need to go any further: we just look at the direction of the overall acceleration: 
$$\text{output} = \begin{cases}\text{north} &\text{if }\ddot{y}<0 \text{ and } |\ddot{y}|>|\ddot{x}| \\ & \\ \text{south} &\text{if }\ddot{y}>0 \text{ and } |\ddot{y}|>|\ddot{x}| \\ & \\ \text{west} &\text{if }\ddot{x}<0 \text{ and } |\ddot{x}|>|\ddot{y}| \\ & \\ \text{east} &\text{if }\ddot{x}>0 \text{ and } |\ddot{x}|>|\ddot{y}| \\ & \\ \text{none} &\text{if }|\ddot{y}|=|\ddot{x}|\end{cases}$$
And just like that, we've unified the coins and the bots. Where's my Nobel Prize?
[Answer]
# Goldilocks, [JavaScript (Node.js)](https://nodejs.org)
```
function goldilocks(me, others, coins) {
  let target = coins[0]; // Gold
  let x = target[0] - me.locationX;
  let y = target[1] - me.locationY;
  mymove = 'none'
  if (Math.abs(x) <= Math.abs(y) && x != 0)
    mymove = x < 0 ? 'west' : 'east'
  else if (y != 0)
    mymove = y < 0 ? 'north' : 'south'
  return mymove
}
```
[Try it online!](https://tio.run/##bZFNb8IwDIbv/ArvQlvJg5aVMRXQjrts901VhUIJbVmbTElgVIjf3jml42PayYn9vK9jZ8N2TKeq@DL3Qq5406y3IjWFFJDJclWUMv3UbsURpMm50gipLIT24NADKLkBw1RGYX7Kx34yheEQXkjbAXuqnSAqwj1UfECmzLZ4n3ZMfWGCP8zHtEdQVVdyx4lyhBTcoUyxBveNmXzAltrdezCbw/lae9DvU@O7OfgesVf6PczAh2dwvrk2DkTgcEYHgnipeeta/6erf3VCKpO3Qi23dLKvU9xslejg3rHZMUUjLORyQ8rDedoIAoTzXO2tXVoEEwSmuGCvXGQmj@Dp2LMep5UvmFLkE8c@jjFMMA5xggHFCYYYjJKkZa0TeV/oB4RxghCPEIIujmx8RAhtHHd16u2TRyqFlqXde@be/LwdA6@egjetPK/5AQ "JavaScript (Node.js) – Try It Online")
Just locks on to the gold coin's location and moves towards it every time. (Thanks to @Mayube's 'B33-L1N3' bot for the original code this used, though barely any of it remains.)
[Answer]
# The AntiCapitalist | Javascript
Has no incentive to go after coins, but tries to place himself exactly between the two richest bots with the same amount of money, in the hope they will hunt him and eventually catch him at the same time, taking two capitalists with him when he dies. Does not actively resist getting coins, so he might become a more juicy target.
```
function antiCapitalist(me, capitalists, coins){
    function acquireTargets(capitalists){
        capitalists.sort((a, b) => a[2] < b[2]);
        let previousCapitalist;
        for(let i in capitalists){
            let capitalist = capitalists[i];
            if(capitalist[2] === 0){
                return false;
            }
            if(previousCapitalist && capitalist[2] === previousCapitalist[2]){
                return [previousCapitalist, capitalist];
            }
            previousCapitalist = capitalist;
        }
        return false;
    }
    function move(){
        const targets = acquireTargets(capitalists);
        if(!targets){
            return 'none';
        }
        const coordinates = [Math.floor((targets[0][0] + targets[1][0]) / 2), Math.floor((targets[0][1] + targets[1][1]) / 2)];
        if(me.locationX !== coordinates[0]){
            return me.locationX < coordinates[0] ? 'east' : 'west';
        }
        else if(me.locationX !== coordinates[1]){
            return me.locationY < coordinates[1] ? 'south' : 'north';
        }
        else {
            return 'none';
        }
    }
    return move();
}
```
[Answer]
# Third Gen Learning Algorithm | [JavaScript (Node.js)](https://nodejs.org)
```
function run(me) {
	options = [];
	if (me.locationX > 0) options.push('west');
	if (me.locationY > 0) options.push('north');
	if (me.locationX < me.arenaLength) options.push('east');
	if (me.locationY < me.arenaLength) options.push('south');
	return options[Math.floor(Math.random() * options.length)];
}
```
[Try it online!](https://tio.run/##hZC9DsIwDITn9im8NUGiYufnDZgpqhiiNqVBxa6cBAbEs4eUvwGK2Gzfd3eSD@qkbMWmd1OkWofQeKycIQT2KI5awiVNqB8uFpZQ7uZpYhqISt5RpYZ7ASuYSXhCee9tK7Kzti6T3/B2DEZi147RBSwgroo1qo2pXfvp1OpXzR@jJf@oTBPWzjO@9HKtXJs3HRGL@8gKazoKCZN3RKdxHyPjL64h3AA "JavaScript (Node.js) – Try It Online")
After a few generations of learning, this bot has learned that leaving the arena = bad
[Answer]
# B33-L1N3 | [JavaScript (Node.js)](https://nodejs.org)
```
function(me, others, coins) {
	// Do nothing if there aren't any coins
	if (coins.length == 0) return 'none';
	// Sort by distance using Pythagoras' Theorem
	coins = coins.sort((a, b) => (a[0] ** 2 + a[1] ** 2) - (b[0] ** 2 + b[1] ** 2));
	// Closest coin
	let target = coins[0];
	let x = target[0];
	let y = target[1];
	// Util function for movement
	function move(pos, type) {
		let moveTypes = { X: ['east', 'west'], Y: ['south', 'north'] };
		if (pos > me['location'+type]) return moveTypes[type][0];
		else return moveTypes[type][1];
	}
	// Move the shortest distance first
	if (x < y && x != me.locationX) return move(x, 'X');
	else if (y != me.locationY) return move(y, 'Y');
}
```
[Try it online!](https://tio.run/##dVJNT@swEDynv2K44ARC@TgC4QLXJz0JkFpFPbhh2wQldmVvoRHqby/rGMLTkzjZmh3vzM76Vb9pX7lmw2fGvtDhsNqaihtr0o5yWK7J@RyVbYzP8DFJzs/xYGGk0Jg1mhUCg6AdGcXQpo/cSSKldLhOWzJrrlEUuMjgiLfOQBlrSN0M/R6tYyx7vDSetakIWx96/@251mvrtFd4qsk66iZJSxwFUMRz6uV1muocywzFHVJdXixwcoIrnEKXl/Ge4Qzp8p/Kcqxk0cR9az352DzKsHZrOb505O1NxHcCxdoP1v9gl4INHZ@5afGdJlbWobNv1JHhSTLCAUo3ViLmfkNDwkPDgD8JEub8wOwapSLtWeVQ72JTLXLMA@jtluuAGomhVgvsxdGQvTTFHToqVWsrHcTUadBYjDsYNcoBj9Mk1Hr6jRFmS/ZxvD9SC8uHr0U6RDfub9U4z/EH7HAr4RwfS2pHhbiZfpuZZXH2nXifqbCEQTi86f@jzr@ovVDngbo/HD4B "JavaScript (Node.js) – Try It Online")
Makes a **beeline** for the closest coin
[Answer]
# Livin' on the Edge, JavaScript
```
function LivinOnTheEdge (myself, others, coins) {
  x = myself.locationX;
  y = myself.locationY;
  xymax = myself.arenaLength - 1;
  if (x < xymax && y == 0) {
      return 'east';
    } else if (y < xymax && x == xymax) {
      return 'south';
    } else if (x > 0 && y == xymax) {
      return 'west';
  } else {
    return 'north';
  }
}
```
This one heard the edge of the arena is a dangerous place to be. Knowing no fear, it tirelessly circles the board clockwise, only inches away from the certain death that awaits behind the border, hoping no other bot will dare to move so closely to the edge's vicinity.
[Answer]
# Damacy, [JavaScript (Node.js)](https://nodejs.org)
```
function damacy(me, others, coin) {
  let xdist = t => Math.abs(t[0] - me.locationX)
  let ydist = t => Math.abs(t[1] - me.locationY)
  function distanceCompare(a, b, aWt, bWt) {
    aWt = aWt || 1
    bWt = bWt || 1
    return (xdist(a) + ydist(a)) / aWt - (xdist(b) + ydist(b)) / bWt
  }
  function hasThreat(loc) {
    let threat = others.filter(b => b[0] == loc[0] && b[1] == loc[1] && b[2] >= me.coins)
    return (threat.length > 0)
  }
  function inArena(loc) {  // probably unnecessary for this bot
    return loc[0] >= 0 && loc[1] >= 0 && loc[0] < me.arenaLength && loc[1] < me.arenaLength
  }
  function sortedCoins() {
    coinsWithValues = coin.map((coords, i) => coords.concat((i == 0) ? 5 : 2))
    coinsWithValues.sort((a, b) => distanceCompare(a, b, a[2], b[2]))
    return coinsWithValues.map(c => c.slice(0, 2))
  }
  othersPrev = botNotes.getData('kata_others_pos')
  botNotes.storeData('kata_others_pos', others)
  if (othersPrev) {
    for(let i = 0; i < others.length; i++) {
      let bot = others[i]
      let matchingBots = othersPrev.filter(function (b) {
        let diff = Math.abs(b[0] - bot[0]) + Math.abs(b[1] - bot[1])
        if (diff >= 2)
          return false // bot can't have jumped
        return [0, 2, 5].includes(bot[2] - b[2])
      })
      if (matchingBots.length > 0) {
        let botPrev = matchingBots.shift()
        // remove matched bot so it doesn't get matched again later
        othersPrev = othersPrev.filter(b => b[0] != botPrev[0] || b[1] != botPrev[1])
        bot[0] = Math.min(Math.max(bot[0] + bot[0] - botPrev[0], 0), me.arenaLength-1)
        bot[1] = Math.min(Math.max(bot[1] + bot[1] - botPrev[1], 0), me.arenaLength-1)
      }
    }
  }
  let eatables = others.filter(b => b[2] < me.coins && b[2] > 0)
  let targets
  if (eatables.length > 0) {
    targets = eatables.sort(distanceCompare)
  }
  else {
    targets = sortedCoins()
  }
  let done, newLoc, dir
  while (!done && targets.length > 0) {
    t = targets.shift()
    if ((xdist(t) <= ydist(t) || ydist(t) == 0) && xdist(t) != 0) {
      let xmove = Math.sign(t[0] - me.locationX)
      dir = xmove < 0 ? 'west' : 'east'
      newLoc = [me.locationX + xmove, me.locationY]
      if (!hasThreat(newLoc) && inArena(newLoc))
        done = 1
    }
    if (!done) {
      let ymove = Math.sign(t[1] - me.locationY)
      dir = ['north', 'none', 'south'][ymove + 1]
      newLoc = [me.locationX, me.locationY + ymove]
      if (!hasThreat(newLoc) && inArena(newLoc))
        done = 1
    }
  }
  if (!done)
    dir = 'none'
  return dir
}
```
[Try it online!](https://tio.run/##rVZLb9tGEL7rV4wv0RJey6JiN4FrOWjTY1L0UNQtCMJYkiuJLskVuCs7QqLf7s7sgw9Fbi81YHG5O49vvpmd4aN4Ejpvy625aFQhX15WuyY3pWqgELXI96yWHJTZyFZzyFXZRPB1AlBJA1@KUhtYAv7fwWdhNjORaWaSeQoXUMtZpXJBlv6MvML@FYX4SOEvUuhxoJJocvlR1VvRSiY4ZBzEvcHnvXFwgN7RMv1@@wax3crsVjbcaqXZtQ0wC52JCM4dKFxGcGnVL8Jp1p9m9hQtoZHDENtG6N83rRSGIfYAhUI1dhfdO@pmq7IysmUZRZ4RQ8sloAqt3rzBnbjbif3OIoW7JbFCpOtoBN9Zn1WyWZsN3ME8OgZWNj@1shEeFsDlJWxblYms2sOuaWQutRbtHlaqRaylhkyZoQuPDRHMCY4HNnzF01uCJ8jPJ4eklzw@OsanVWtk8ZFCY4E3G@h9aTZ/iGonNZJHO7NabBnLlWoLrMAyIgbdGzLTYMEwVhJ38wg@wDXcwCKKTpmbkUtmq8faeKWskHdu2Y9GlB8bI1C5hTLTVZlLNufeMYXpkv5bK5@oAJX5VRnUWUvzizCCTf/G3wcn87BVekpqnZQ2qpWn5cJFJPlyBax3QxRauJhPRvWHlMD8R3zchgp0xYJb5@eBcFeq6Lmr06RMJ4OjWph8Uzbrn5XRnQz5C/Xc5ZNuSzDqdItytUKd7ppnri@gN1zQ1RqcxOEkTqPOCEVojWDVLfrtLiUrUWlJhU0B5KKZGryOTxIed/VWFpMj6YQSxOE6nZVNXu0KiX7R4cJ6pnR7hUMUCCD/QwKG1@0oVrTkkz1S0JtyZVgPHbG2slaI0YrJwkLXCkqkS0lNIawD7Xgq1qLEqyiQ6c7GqLS@T0jfYM6WARa9YQe0NA92h1y7rIR01WXD3EJ8Yf7oPMhcDMxypIIf3fSLeGw2ft1sHMzGQ7Pxf5g9TMLvYeIHCzZD7GxSv9ZvF74h2Vvct1fXN227Fi0Sr/29CuZOZNwLoqNOyPaVo24SGoGkEj3WHPW@YRiFanDYNvL5k8o5XiDK@vOmrCSwMzoj5N7MKWg0Wf3psPIoIj/ScFreLv1MwzUWRbd2HRQddJJny2Gh24Fva9dnU5fr5rVhT38IH0Wdyi2OjQ8wfZbaTLFDT6XAhZdz4aJoMrSChWFV@ei7IB1czbN@9joTFn2Ye36rL0XL39J/BhwmHTOW2HGY@xNhnvxE6cNMpg0mdYMdGheNpKdWO9xIE2ftHOL0XwMeB0pfHqT3/wXsg@5DnvToHeYJnftuSbV3mLw8iRZhPajsEaW@dlhvIObQYbVv9mbdwDucoP2dvYH3hwnZ8DNMtJYrbMXX/CrlyRWPOd725B3H1SJNrSxZQtu99Fvq2hwS7N6xf9KMTn7gcEXPa3@OvufeRhimhBqD8oP3pv/8YMWurvch7WFEJLEH9pa/HwND9jj@dLP5O1NIgX0unM0DsjfBzxOtKsrqmnUf00QmHxDCRwFH0cs/ "JavaScript (Node.js) – Try It Online")
One last katamari-based bot for today, this time with a little bit of memory. Thanks to [@BetaDecay](https://codegolf.stackexchange.com/questions/170229/gold-collector-koth/170269#comment411210_170276) for the name suggestion - definitely a more fun name than my `simplePredictorKatamari`.
Tries to figure out how bots have moved in the last turn, and based on that, predicts where they'll try to move at the end of this turn (assuming they continue to move in the same direction). 
*(Thanks to @fəˈnɛtɪk, for noticing that I was calling the wrong function name in botNotes, and to @OMᗺ for noticing a bug in the base code.)*
[Answer]
# Proton | JavaScript
```
Proton=(myself,others,coins)=>{
  x=myself.locationX;
  y=myself.locationY;
  power=myself.coins;
  arenaSize=myself.arenaLength;
  forceX=0;
  forceY=0;
  prevState=botNotes.getData("proton_velocity");
  if(prevState){
    velocity=prevState[0];
    direction=prevState[1];
  }
  else{
    velocity=0;
    direction=0;
  }
  for(i=0;i<coins.length;i++){
    if(Math.abs(x-coins[i][0])+Math.abs(y-coins[i][1])==1){
      velocity=0;
      direction=0;
      botNotes.storeData("proton_velocity",[velocity,direction]);
      if(x-coins[i][0]==1){return "west";}
      if(coins[i][0]-x==1){return "east";}
      if(y-coins[i][1]==1){return "north";}
      if(coins[i][1]-y==1){return "south";}
    }
    else{
      dist=Math.sqrt(Math.pow(x-coins[i][0],2)+Math.pow(y-coins[i][1],2));
      if(i==0){
        forceX+=(x-coins[i][0])*5/Math.pow(dist,3);
        forceY+=(y-coins[i][1])*5/Math.pow(dist,3);
      }
      else{
        forceX+=(x-coins[i][0])*2/Math.pow(dist,3);
        forceY+=(y-coins[i][1])*2/Math.pow(dist,3);
      }
    }
  }
  for(i=0;i<others.length;i++){
    if(Math.abs(x-others[i][0])+Math.abs(y-others[i][1])==1&&power>others[i][2]){
      velocity=0;
      direction=0;
      botNotes.storeData("proton_velocity",[velocity,direction]);
      if(x-others[i][0]==1){return "west";}
      if(others[i][0]-x==1){return "east";}
      if(y-others[i][1]==1){return "north";}
      if(others[i][1]-y==1){return "south";}
    }
    else{
      dist=Math.sqrt(Math.pow(x-others[i][0],2)+Math.pow(y-others[i][1],2));
      forceX+=(x-others[i][0])*others[i][2]/Math.pow(dist,3);
      forceY+=(y-others[i][1])*others[i][2]/Math.pow(dist,3);
    }
  }
  vX=velocity*Math.cos(direction)+10*forceX/Math.max(1,power);
  vY=velocity*Math.sin(direction)+10*forceY/Math.max(1,power);
  velocity=Math.sqrt(vX*vX+vY*vY);
  if(velocity==0){return "none"}
  retval="none";
  if(Math.abs(vX)>Math.abs(vY)){
    if(vX>0){
      if(x<arenaSize-1){retval="east";}
      else{vX=-vX;retval="west";}
    }
    else{
      if(x>0){retval="west";}
      else{vX=-vX;retval="east";}
    }
  }
  else{
    if(vY>0){
      if(y<arenaSize-1){retval="south";}
      else{vY=-vY;retval="north";}
    }
    else{
      if(y>0){retval="north";}
      else{vY=-vY;retval="south";}
    }
  }
  direction=Math.atan2(-vY,vX);
  botNotes.storeData("proton_velocity",[velocity,direction]);
  return retval;
}
```
All coins (including those held by other bots) emit a repulsive force towards Protonbot. Based on this force, it builds up velocity and bounces off of walls (turns around immediately upon hitting a boundary). If it ends up next to a bot or coin it can consume, the Strong nuclear force takes over and it moves to consume it, dropping all velocity when it does so.
[Answer]
## Not so Blindly | JavaScript (Node.js)
**Important Note:** This approach is not entirely mine and has been answered in a [similar question](https://codegolf.stackexchange.com/a/167590/78039). Make sure to vote that answer as well.
Haev you ever heard about A\* pathfinding algorithm? here it is. It creates the best path from one point to the less valuable coin (as everyone is going for the most valuable, no one is going for the less) and tries to not collide with any other user.
Expects parameters as follow:
```
AI({locationX: 3, locationY: 1, arenaLength: [5,5]}, [[2,1],[2,2], ...],[[1,2],[3,1], ...])
```
---
Maybe I do one that goes hunting other bots
---
```
function AI(me, others, coins){
    var h = (a,b) => Math.abs(a[0] -b[0]) + Math.abs(a[1] -b[1])
    var s = JSON.stringify;
    var p = JSON.parse;
    var walls = others.slice(0,2).map(s);
    var start = [me.locationX, me.locationY];
    var goal = coins.pop();
    var is_closed = {};
    is_closed[s(start)] = 0;
    var open = [s(start)];
    var came_from = {};
    var gs = {};
    gs[s(start)] = 0;
    var fs = {};
    fs[s(start)] = h(start, goal);
    var cur;
    while (open.length) {
        var best;
        var bestf = Infinity;
        for (var i = 0; i < open.length; ++i) {
            if (fs[open[i]] < bestf) {
                bestf = fs[open[i]];
                best = i;
            }
        }
        cur = p(open.splice(best, 1)[0]);
        is_closed[s(cur)] = 1;
        if (s(cur) == s(goal)) break;
        for (var d of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
            var next = [cur[0] + d[0], cur[1] + d[1]];
            if (next[0] < 0 || next[0] >= me.arenaLength[0] ||
                next[1] < 0 || next[1] >= me.arenaLength[1]) {
                continue;
            }
            if (is_closed[s(next)]) continue;
            if (open.indexOf(s(next)) == -1) open.push(s(next));
            var is_wall = walls.indexOf(s(next)) > -1;
            var g = gs[s(cur)] + 1 + 10000 * is_wall;
            if (gs[s(next)] != undefined && g > gs[s(next)]) continue;
            came_from[s(next)] = cur;
            gs[s(next)] = g;
            fs[s(next)] = g + h(next, goal);
        }
    }
    var path = [cur];
    while (came_from[s(cur)] != undefined) {
        cur = came_from[s(cur)];
        path.push(cur);
    }
    var c = path[path.length - 1];
    var n = path[path.length - 2];
    if(n){
        if (n[0] < c[0]) {
            return "west";
        } else if (n[0] > c[0]) {
            return "east";
        } else if (n[1] < c[1]) {
            return "north";
        } else {
            return "south";
        }
    }else{
        return "none";
    }
}
```
[Answer]
# Coward | Python 2
```
import random
def move(me, others, coins):
    target = (me.locationX, me.locationY)
    # Identify the dangerous opponents.
    threats = [i for i, value in enumerate(others[2]) if value >= me.coins]
    # If no one scary is nearby, find a nearby coin.
    safe = True
    for x, y in self.coins:
        distance = abs(me.locationX - x) + abs(me.locationY - y)
        safe = True
        for i in threats:
            if abs(others[0][i] - x) + abs(others[1][i] - y) <= distance:
                safe = False
                break
        if safe:
            target = (x, y)
            break
    # Otherwise, just try not to die.
    if not safe:
        certain = []
        possible = []
        for x, y in [
            (me.locationX, me.locationY),
            (me.locationX + 1, me.locationY),
            (me.locationX - 1, me.locationY),
            (me.locationX, me.locationY + 1),
            (me.locationX, me.locationY - 1),
        ]:
            # Don't jump off the board.
            if x < 0 or y < 0 or x == me.arenaLength or y == me.arenaLength:
                continue
            # Check if we can get away safely.
            for i in threats:
                if abs(others[0][i] - x) + abs(others[1][i] - y) <= 1:
                    break
            else:
                certain.append((x, y))
            # Check if we can take a spot someone is leaving.
            for i in threats:
                if others[0][i] = x and others[1][i] == y:
                    for i in threats:
                        if abs(others[0][i] - x) + abs(others[1][i] - y) == 1:
                            break
                    else:
                        possible.append((x, y))
        if certain:
            target = random.choice(certain)
        elif possible:
            target = random.choice(possible)
        # Otherwise, we're doomed, so stay still and pray.
    directions = []
    x, y = target
    if x < me.locationX:
        directions.append('west')
    if x > me.locationX:
        directions.append('east')
    if y < me.locationY:
        directions.append('north')
    if y > me.locationY:
        directions.append('south')
    if not directions:
        directions.append('none')
    return random.choice(directions)
```
Avoid bots with more money if at all possible. Otherwise, grab money that's lying around.
[Answer]
# Wild Goose Chase Bot, Javascript
*A bot that's really good at dodging other bots, but very bad at getting coins.* 
---
Algorithm:
1. If there are no adjacent bots, return none
2. Otherwise:
	1. Return none with a random chance 1/500 chance (this is intended to prevent stalemates).
	2. Determine which spaces are safe to move to (ie inside the arena and not occupied by another bot)
	3. Return one at random
---
Code:
```
function wildGooseChase(me, others, coins){
    x = me.locationX;
    y = me.locationY;
    dirs = {};
    dirs[(x+1)+" "+y] = "east";
    dirs[(x-1)+" "+y] = "west";
    dirs[x+" "+(y+1)] = "south";
    dirs[x+" "+(y-1)] = "north";
    mov = {};
    mov["east"] = [x+1,y];
    mov["west"] = [x-1,y];
    mov["north"] = [x,y-1];
    mov["south"] = [x,y+1]; 
    possibleDirs = ["east","west","north","south"];
    for (i = 0; i < others.length; i++){
        if (others[i][0]+" "+others[i][1] in dirs){
            possibleDirs.splice(possibleDirs.indexOf(dirs[others[i][0]+" "+others[i][1]]),1);
        }
    }
    if (possibleDirs.length == 4 || Math.floor(Math.random() * 500) == 0){
        return "none"
    }
    for (i = 0; i < possibleDirs.length; i++){
        if (mov[possibleDirs[i]][0] == me.arenaLength || mov[possibleDirs[i]][0] < 0 
        || mov[possibleDirs[i]][1] == me.arenaLength || mov[possibleDirs[i]][1] < 0){
            var index = possibleDirs.indexOf(possibleDirs[i]);
            if (index != -1) {
                possibleDirs.splice(index, 1);
                i--;
            }
        }
    }
    if (possibleDirs.length == 0){
         return "none";
    }
    return possibleDirs[Math.floor(Math.random() * possibleDirs.length)];
}
```
[Try it Online!](https://tio.run/##vZZRb9owEMefyae48bIwTJdspCvN@rRJe9m0100oD4EYyBRs5Dht0cZn7@5sKEkJUcrotGokPtv3u7/vzvkV38b5VKUrPRAy4Q8Ps0JMdSoF3KVZ8kXKnH9axDl3l5yB1AuucgZTmYq899vp3MMNLPlFJqcxrfkROp11dehn6DidJFU5Dv/ehPZ57N73/V6/C93@OkJDl8e57u6Ng4rxjpeM98bgrnG9Meay0IsD62BrFVIZq9NZytsdAT6OrUeag2t8to5248aZHR@Ux@1O1sBw/0eDBdgZ@mjoOPhvJfM8nWT8sw1965DZ/dl2O7ZbTYQzqcBNca4XQgoft2JfZFzM9QKH@n1SvJPOwLWmcRqNvchEvB/wI0gFkBhmdoXjIl9l6ZS7lbFUJPz@@8w1@jVuHPWY38OwOxsH/xxDUtnKosLNDQzhzx/4FuvFxSyTUrnmUcUikUu3B28g8LwezfMMpOK6UIJOS/Cu3fupGDV@qpLQWZQnITIFQU4wG2PFRfzV4iHZsckfwQMS7dgU/zn7@WY/ewq3sQIjNEZUq/6T1UZmE5dd9eoGMKXB7FV7pGYaA3s@uHIwMA@bVqdVcwohLcI03g5W8BrOtcZBD5N787DkVH2PfeIaAgaPLeI62LaUa49BSdpr39s4xkAVFDnO27cgJMTJr3jKhYZVFq8xO0NIJI7rRSrmjk1Ymj8O2IeIwXjI3tPPiL2jH5959POBXUYR7i1ymVGzmrvH253tdqgs@bc@QUtAI5gyJv/itYa5tO9VhiE6PIMn0yhKnsx71dPleTxRh9o70ua97GjIgvM4oo5Yiohey34uT/eDSSI4ltxEarytJoWGyRp4MuchQL6QRZaUU6Y5N0eNudmEVkeGHJm8Qw2IhhEgxBN5i2BbLhTiqeABu6KkRUFGLQSp8YpOi9Wq4nTCEaPs1KjfrITXoEQF19QY5ol3Gi6JRBqUcIkOsM3sCm7PfVAHHhuic58FETaeg4C8lm0nPA3bcO6xTRSEva3ePfZBoxhhQ2LjqyPYoxfExs8FQ2Ngp1IJrhh@/OHVQCFoeQhfnytey1wJq6flU8CnUxvF66htG3uaKM3KtqWmejyV2nC0prZ50kw9ak09@jfqoxlyUJUtMmTUOkOuXpLalGdFdcXzdC5oQpJycPcXRe@MEVGHOv00lmmSZPhNUCglC/zyS6j36IXi3Fx5LRIoaN1Qqje/uYbo/4A@oF4e/0jVngF/@D/wzVV@Hvo9938Tv8U3wfO0fyH6mSxUFf45NXyWeGxePTeqh78)
Note to Redwolf Programs: 
This bot has the potential to cause very long rounds. I have taken some liberties to prevent stalemates, but have not tested if they are actually effective. If this bot becomes a problem during testing, please feel free to disqualify it. 
[Answer]
# KatamariWithValues, [JavaScript (Node.js)](https://nodejs.org),
```
function katamariWithValues(me, others, coin) {
  let xdist = t => Math.abs(t[0] - me.locationX)
  let ydist = t => Math.abs(t[1] - me.locationY)
  function distanceCompare(a, b, aWt = 1, bWt = 1) {
    return (xdist(a) + ydist(a)) / aWt - (xdist(b) + ydist(b)) / bWt
  }
  function hasThreat(loc) {
    let threat = others.filter(b => b[0] == loc[0] && b[1] == loc[1] && b[2] >= me.coins)
    return (threat.length > 0)
  }
  function inArena(loc) {  // probably unnecessary for this bot
    return loc[0] >= 0 && loc[1] >= 0 && loc[0] < me.arenaLength && loc[1] < me.arenaLength
  }
  function sortedCoins() {
    coinsWithValues = coin.map((coords, i) => coords.concat((i == 0) ? 5 : 2))
    coinsWithValues.sort((a, b) => distanceCompare(a, b, a[2], b[2]))
    return coinsWithValues.map(c => c.slice(0, 2))
  }
  let eatables = others.filter(b => b[2] < me.coins && b[2] > 0)
  let targets
  if (eatables.length > 0) {
    targets = eatables.sort(distanceCompare)
  }
  else {
    targets = sortedCoins()
  }
  let done, newLoc, dir
  while (!done && targets.length > 0) {
    t = targets.shift()
    if ((xdist(t) <= ydist(t) || ydist(t) == 0) && xdist(t) != 0) {
      let xmove = Math.sign(t[0] - me.locationX)
      dir = xmove < 0 ? 'west' : 'east'
      newLoc = [me.locationX + xmove, me.locationY]
      if (!hasThreat(newLoc) && inArena(newLoc))
        done = 1
    }
    if (!done) {
      let ymove = Math.sign(t[1] - me.locationY)
      dir = ['north', 'none', 'south'][ymove + 1]
      newLoc = [me.locationX, me.locationY + ymove]
      if (!hasThreat(newLoc) && inArena(newLoc))
        done = 1
    }
  }
  if (!done)
    dir = 'none'
  return dir
}
```
[Try it online!](https://tio.run/##rVVNbxoxEL3zKyaX4FUcAhSaCoVEVa7prWparVaRdzHspouNbJMUNfx2OmMbFijpqZGitT1f7z2Ph2fxImxhqoW7VHoiN5vpUhWu0gp@CifmwlSPlSu/iXopLZtLDtqV0lgOha5UAr9bALV08GtSWQdjwP9b@CJc2RG5ZS7tZnAJc9mpdSEo6/ckBqzeCegdBfyggB0mChKqkPd6vhBGMsEh5yAeKVMP12ERYAEY6ZZGAfPgmEjgIpTFZQJXPuxya80ba@6tmAuTrPerl8J@LY0UjiG6bREi4/wplg7idKZV7aRhOXHLSYPxGDCEVufneNLbnfTiST@D2zHxJlltcgA/ZO/UUs1cCbfQTY6BVeqzkUpEWABXV7AwOhd5vYKlUrKQ1gqzgqk2iLWykGu3XyJiQwRdghOB7W/RekPwBNV5CEgaz2PTMT6rjZOTe6LGtrp5ok1voXh00pmLBWOF1maCPVYlpGDYoTIKW4KxirTrJnAHQxhBP0lOpetQSeb7w@d4p3FQd@7VTw4kP05GoAoPpWPrqpCsy2PhdSv2M94Qyu15nGyCflTJp27uPFym7yFhZtJZ3FVTYNt0@7cehYuOWGjn5MkeUdw2iayt/Cvy4EL2aUy0wjeu5OuDLjiqZvD8taxqCeyMbIQ8pjkFjR50tNqymjoWZCVG8Z25BG7G8aHh@u2tWYdrxQI7z7NxkzvOmbl@kVjFTwxbzdR7M4b@ED66hpAb7OU7aL9K69rYNm0pcBH9Al10Tfez4DzwofxgHGUxhiidNQMhpPDot48xHm3BgNeWxpM/8IrHNGQ4pLk6QfPkZGxopm2Fl1q2OeBCSfpavcSDLA3ZLqCX/ZPwIVEahxT3/whH0g3lVoM@YCZzfILUeuvW5kUYRPWk82d0@r2DOqJpv4Pqd/5hjeAaX3UziEbwad2iHOFNPgnjpUq7fMgHGU8H/Jr38HvNB7zXzzLvS5kwd@P9gcMQ50Ta5yhh@NLcSD9yGNB3GO1Yu4s5cFBZXZOUM3byR5To8D1I/KBkkmz@AA "JavaScript (Node.js) – Try It Online")
*(Thanks to @OMᗺ for pointing out a bug in the original code this was based on.)*
Tries to grow by "eating" bots with less coins than itself. If that's not possible (no such bot exists), then seeks out the nearest coin. 
This version has small tweaks to (a) give higher preference to gold coins than to silver coins - with the risk that seeking a more distant gold coin may end up costing the bot's life or lead to chasing after fool's gold (b) skip bots with 0 coins - no need to waste time chasing after those. 
[Answer]
# Polite Near-Sighted Drunk Bot | JavaScript
```
function politeNearSightedDrunkBot(me, others, coins) {
  let directions = ['none','east','south','west','north']
  let drunkennessCoefficient = .2
  let nearSightedness = me.arenaLength - others.length + 2
  //drawCircle(me.locationX, me.locationY, nearSightedness*squareSize)
  function randomInt(a) {
    return Math.floor(Math.random() * a);
  }
  function getRandomDirection() {
    return ['east', 'west', 'north', 'south'][randomInt(4)]
  }
  function distanceTo(a) {
    return (Math.abs(a[0] - me.locationX) + Math.abs(a[1] - me.locationY))
  }
  function distanceBetween(a, b){
    return (Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]))
  }
  function isTargetSafe(a) {
    for (let i = 0; i < others.length; i++) {
      if (others[i][2] >= me.coins && distanceBetween(a, others[i]) <= distanceTo(a)) {
        return false //unnecessary loop, but I don't want to split out into a function
      }
    }
    return true
  }
  function amISafe() {
    for (let i = 0; i < others.length; i++) {
      if (others[i][2] >= me.coins && distanceTo(others[i]) == 1) {
        /*let num = botNotes.getData('turnsSpentAdjacentToEnemy')
        if (!num) {
          console.log('politeNearSightedDrunkBot: Woops!')
          botNotes.storeData('turnsSpentAdjacentToEnemy', 1)
        } else if (num == 1) {
          console.log('politeNearSightedDrunkBot: \'Scuse me...')
          botNotes.storeData('turnsSpentAdjacentToEnemy', 2)
        } else if (num == 2) {
          console.log('politeNearSightedDrunkBot: D\'ye mind?')
          botNotes.storeData('turnsSpentAdjacentToEnemy', 3)
        } else if (num == 3) {
          console.log('politeNearSightedDrunkBot: Bugger off!')
        }*/
        return false
      }
    }
    return true
  }
  function getSafeDirections() {
    let candidates = {'none': true, 'east': true, 'south': true, 'west': true, 'north': true}
    if (me.locationY == 0) {
      candidates['north'] = false
    } else if (me.locationY == me.arenaLength - 1) {
      candidates['south'] = false
    }
    if (me.locationX == 0) {
      candidates['west'] = false
    } else if (me.locationX == me.arenaLength - 1) {
      candidates['east'] = false
    }
    if (!amISafe()) {
      candidates['none'] = false
    }/* else {
      botNotes.storeData('turnsSpentAdjacentToEnemy', 0)
    }*/
    if (candidates['north'] && !isTargetSafe([me.locationX, me.locationY-1])) {
      candidates['north'] = false
    }
    if (candidates['south'] && !isTargetSafe([me.locationX, me.locationY+1])) {
      candidates['south'] = false
    }
    if (candidates['west'] && !isTargetSafe([me.locationX-1, me.locationY])) {
      candidates['west'] = false
    }
    if (candidates['east'] && !isTargetSafe([me.locationX+1, me.locationY])) {
      candidates['east'] = false
    }
    if (candidates['none']) {
    }
    return candidates
  }
  function getSafeCoins() {
    let safestCoins = []
    let coinSizes = [5, 2, 2, 2, 2]
    for (let i = 0; i < coins.length; i++) {
      let distanceToThisCoin = distanceTo(coins[i])
      if (distanceToThisCoin < nearSightedness && isTargetSafe(coins[i])) {
        safestCoins.push([coins[i][0], coins[i][1], coinSizes[i], distanceToThisCoin])
        //alert('Coin at (' + coins[i][0] + ', ' + coins[i][1] + ') is safe!')
      }
    }
    if (safestCoins.length == 0) {
      //alert('No safe coins!')
    }
    return safestCoins
  }
  function getAdditiveBestDirectionToTargets(targets) {
    let candidates = {'east': 0, 'south': 0, 'west': 0, 'north': 0}
    for (let i = 0; i < targets.length; i++) {
      if (targets[i][0] < me.locationX) { 
        candidates['west'] = candidates['west'] + targets[i][2]/targets[i][3]
      } else if (targets[i][0] > me.locationX) {
        candidates['east'] = candidates['east'] + targets[i][2]/targets[i][3]
      }
      if (targets[i][1] > me.locationY) { 
        candidates['south'] = candidates['south'] + targets[i][2]/targets[i][3]
      } else if (targets[i][1] < me.locationY) {
        candidates['north'] = candidates['north'] + targets[i][2]/targets[i][3]
      }
    }
    for (let key in candidates) {
      //alert(key + ': ' + candidates[key])
    }
    return candidates
  }
    let targetCoins = getSafeCoins()
    let safeDirections = getSafeDirections()
    let chosenDir = null
    if (targetCoins.length > 0) {
      //alert('Coins found! Exactly ' + targetCoins.length)
      let weightedDirections = getAdditiveBestDirectionToTargets(targetCoins)
      let bestOptionWeight = 0
      let choices = []
      for (let key in safeDirections) {
        if (safeDirections[key] && key != 'none') {
          if (weightedDirections[key] == bestOptionWeight) {
            choices.push(key)
          } else if (weightedDirections[key] > bestOptionWeight) {
            choices = [key]
            bestOptionWeight = weightedDirections[key]
          }
        }
      }
      if (choices.length > 0) {
        //alert('Picking from choices, ' + choices.length + ' options and best weight is ' + bestOptionWeight)
        chosenDir = choices[randomInt(choices.length)]
      } else {
        //alert('No safe choices!')
      }
    } else {
      let lastDir = botNotes.getData('direction') || 'none'
      if (safeDirections[lastDir] && Math.random() >= drunkennessCoefficient) {
        chosenDir = lastDir
      }
    }
    if (!chosenDir) {
      //alert('indecisive!')
      let choices = []
      for (key in safeDirections) {
        if (safeDirections[key]) {
          choices.push(key)
        }
      }
      if (choices.length > 0) {
        chosenDir = choices[randomInt(choices.length)]
      } else {
        chosenDir = getRandomDirection()
      }
    }
    botNotes.storeData('direction', chosenDir)
    //alert('Moving ' + chosenDir)
    return chosenDir
}
```
Staggers around picking up nearby coins, but randomly switches directions every so often. Does what he can to avoid bumping into anyone, but he gets... belligerent... when aggravated. Tends to sober up as the competition goes on.
May need some debugging, when the controller is fully done I'll work on it.
[Answer]
# Weighted Motion | JavaScript
```
WeightedMotion=(myself,others,coins)=>{
  x=myself.locationX;
  y=myself.locationY;
  power=myself.coins;
  arenaSize=myself.arenaLength;
  dirX=0;
  dirY=0;
  for(i=0;i<coins.length;i++){
    if(i==0){
      dirX+=5/(x-coins[i][0]);
      dirY+=5/(y-coins[i][1]);
    }
    else{
      dirX+=2/(x-coins[i][0]);
      dirY+=2/(y-coins[i][1]);
    }
  }
  for(i=0; i<others.length;i++){
    dirX+=(power-others[i][2])/(2*(x-others[i][0]));
    dirY+=(power-others[i][2])/(2*(y-others[i][1]));
  }
  if(Math.abs(dirX)>Math.abs(dirY)){
    if(dirX>0){
      if(x>0){return "west";}
      else{
        if(dirY>0){if(y>0)return "north";}
        else if(dirY<0){if(y<arenaSize-1)return "south";}
      }
    }
    else if(x<arenaSize-1){return "east";}
    else{
      if(dirY>0){if(y>0)return "north";}
      else if(dirY<0){if(y<arenaSize-1)return "south";}
    }
  }
  else{
    if(dirY>0){
      if(y>0){return "north";}
      else{
        if(dirX>0){if(x>0)return "west";}
        else if(dirX<0){if(x<arenaSize-1)return "east";}
      }
    }
    else if(y<arenaSize-1){return "south";}
    else{
      if(dirX>0){if(x>0)return "west";}
      else if(dirX<0){if(x<arenaSize-1){return "east";}
    }
  }
  return "none";
}
```
Moves in the direction it has assigned the highest value while avoiding running off the edge of the board.
Value is calculated as such:
* Coin = power of coin / distance to coin
* Bot = Difference in power of bots / 2 \* distance to bot
[Answer]
## Blindy |  [JavaScript (Node.js)](https://nodejs.org/en/)
This definitely wont win but at least participate. First try at KoH challenge. It sorts the coins and goes to the closest one. He doesn't look for players so he doesn't care if he collide with other.
```
function(myself, others, coins){
    mx = myself.locationX
    my = myself.locationY
    l="west"
    r="east"
    u="north"
    d="south"
    n="none"
    if(coins.length == 0)
        return n
    var closestCoin = coins.sort(a=>Math.sqrt(Math.pow(mx-a[0],2) + Math.pow(my-a[1],2))).pop()
    cx = closestCoin[0]
    cy = closestCoin[1]
    return mx>cx?l:mx<cx?r:my>cy?u:my<cy?d:n
}
```
[Answer]
# Feudal Noble | JavaScript
**Preferred color:** `#268299`
```
function (noble, peasants, coins) {
    var center = (noble.arenaLength - 1) / 2, centerSize = noble.arenaLength / 4, peasantsCount = peasants.length,
        centerMin = center - centerSize, centerMax = center + centerSize, apocalypse = 2e4 - ((noble.arenaLength * 2) + 20), inDanger = false;
    var round = botNotes.getData('round');
    if (round === null || !round) round = 0;
    round++;
    botNotes.storeData('round', round);
    var getDistance = function (x1, y1, x2, y2) {
        return (Math.abs(x1 - x2) + Math.abs(y1 - y2)) + 1;
    };
    var isAtCenter = function (x, y) {
        return (x > centerMin && x < centerMax && y > centerMin && y < centerMax);
    };
    var getScore = function (x, y) {
        var score = 0, i, centerFactor = 10;
        for (i = 0; i < peasantsCount; i++) {
            var peasantCoins = peasants[i][2], peasantDistance = getDistance(x, y, peasants[i][0], peasants[i][1]);
            if (noble.coins > peasantCoins && isAtCenter(x, y)) {
                score += Math.min(100, peasantCoins) / peasantDistance;
            } else if (noble.coins <= peasantCoins && peasantDistance <= 3) {
                score -= (peasantDistance === 3 ? 50 : 2000);
                inDanger = true;
            }
        }
        for (i = 0; i < coins.length; i++) {
            if (isAtCenter(coins[i][0], coins[i][1])) {
                var coinDistance = getDistance(x, y, coins[i][0], coins[i][1]),
                    coinValue = (i === 0 ? 500 : 200),
                    coinCloserPeasants = 1;
                for (var j = 0; j < peasantsCount; j++) {
                    var coinPeasantDistance = getDistance(peasants[j][0], peasants[j][1], coins[i][0], coins[i][1]);
                    if (coinPeasantDistance <= coinDistance && peasants[j][2] >= noble.coins) coinCloserPeasants++;
                }
                score += (coinValue / coinCloserPeasants) / (coinDistance / 3);
            }
        }
        if (round >= apocalypse) centerFactor = 1000;
        score -= getDistance(x, y, center, center) * centerFactor;
        return score;
    };
    var possibleMoves = [{x: 0, y: 0, c: 'none'}];
    if (noble.locationX > 0) possibleMoves.push({x: -1, y: 0, c: 'west'});
    if (noble.locationY > 0) possibleMoves.push({x: -0, y: -1, c: 'north'});
    if (noble.locationX < noble.arenaLength - 1) possibleMoves.push({x: 1, y: 0, c: 'east'});
    if (noble.locationY < noble.arenaLength - 1) possibleMoves.push({x: 0, y: 1, c: 'south'});
    var topCommand, topScore = null;
    for (var i = 0; i < possibleMoves.length; i++) {
        var score = getScore(noble.locationX + possibleMoves[i].x, noble.locationY + possibleMoves[i].y);
        if (topScore === null || score > topScore) {
            topScore = score;
            topCommand = possibleMoves[i].c;
        }
    }
    if (round >= apocalypse) {
        var dg = botNotes.getData('dg');
        if (dg === null || !dg) dg = [];
        if (dg.length >= 20) dg.shift();
        dg.push(inDanger);
        botNotes.storeData('dg', dg);
        if (dg.length >= 20) {
            var itsTime = true;
            for (i = 0; i < dg.length; i++) if (!dg[i]) itsTime = false;
            if (itsTime) return 'none';
        }
    }
    return topCommand;
}
```
This feudal noble stays at the center of the field and claims it as his own palace. Collects anything at the center for himself, but anything in farms far away should be brought to him by peasants. Of course if an angry powerful peasant appears at the palace, noble might run away to save his life, but he comes back as soon as it is safe!
As the time goes on, peasants get stronger and stronger. Professional fighters and powerful heros start to rise from the peasantry. Power of the noble keeps decaying. He tries to keep his wealth and his Feudalism system together as long as he can. But finally a time comes that he should accept his faith, he should accept that people don't want Feudalism anymore. That is the day that feudal noble gives up everything, doesn't run from powerful peasants anymore and gets killed by one of them.
[Answer]
# Quantum Gnat Bot | JavaScript
```
function quantumGnatBot(me, others, coins) {
  let quantumCoefficient = .2
  let turn = botNotes.getData('turn')
  botNotes.storeData('turn', turn+1)
  botNotes.storeData('test', [2, 5, 7])
  botNotes.getData('test')
  let dG = {'none': [0, 0, -2, -2], 'east': [1, 0, me.arenaLength-1, -2], 'south': [0, 1, -2, me.arenaLength-1], 'west': [-1, 0, 0, -2], 'north': [0, -1, -2, 0]}
  function randomInt(a) {
    return Math.floor(Math.random() * a);
  }
  function getRandomDirection() {
    return ['east', 'west', 'north', 'south'][randomInt(4)]
  }
  function distanceBetween(a, b){
    return (Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]))
  }
  function isTargetSafe(a) {
    for (let i = 0; i < others.length; i++) {
      if (others[i][2] >= me.coins && distanceBetween(a, others[i]) <= 1) {
        return false
      }
    }
    return true
  }
  function isEnemySquare(a) {
    for (let i = 0; i < others.length; i++) {
      if (distanceBetween(a, others[i]) == 0) {
        return true
      }
    }
    return false
  }
  function getSafeDirections() {
    let candidates = {'none': true, 'east': true, 'south': true, 'west': true, 'north': true}
    for (let key in dG) {
      if (me.locationX == dG[key][2] || me.locationY == dG[key][3] || !isTargetSafe([me.locationX+dG[key][0], me.locationY+dG[key][1]])) {
        candidates[key] = false
      }
    }
    //alert('Safe: ' + candidates['north'] + ', ' + candidates['east'] + ', ' + candidates['south'] + ', ' + candidates['west'])
    return candidates
  }
  function getThreatDirections() {
    let candidates = {'none': false, 'east': false, 'south': false, 'west': false, 'north': false}
    for (let key in dG) {
      if (isEnemySquare([me.locationX+dG[key][0], me.locationY+dG[key][1]])) {
        candidates[key] = true
      }
    }
    return candidates
  }
  function getTargetDirections() {
    let targetBot = null
    let candidates = {'none': false, 'east': false, 'south': false, 'west': false, 'north': false}
    for (let i = 0; i < others.length; i++) {
      if (distanceBetween([me.locationX, me.locationY], others[i]) > 2 && (!targetBot || targetBot[2] < others[i][2])) {
        targetBot = others[i]
      }
    }
    if (targetBot[0] < me.locationX) {
      candidates['west'] = true
    } else if (targetBot[0] > me.locationX) {
      candidates['east'] = true
    }
    if (targetBot[1] > me.locationY) {
      candidates['south'] = true
    } else if (targetBot[1] < me.locationY) {
      candidates['north'] = true
    } 
    //alert('Chasing ' + targetBot[0] + ', ' + targetBot[1] + ' (' + targetBot[2] + ')')
    //alert('Path: ' + candidates['north'] + ', ' + candidates['east'] + ', ' + candidates['south'] + ', ' + candidates['west'])
    return candidates
  }
  let safeDirections = getSafeDirections()
  let threatDirections = getThreatDirections()
  let targetDirections = getTargetDirections()
  let chosenDir = null
  let choices = []
  for (key in safeDirections) {
    if (safeDirections[key] && targetDirections[key]) {
      choices.push(key)
    }
  }
  if (choices.length == 0) {
    //alert('Best options are blocked...')
    for (key in safeDirections) {
      if (safeDirections[key]) {
        choices.push(key)
      }
    }
  }
  for (key in threatDirections) {
    if (threatDirections[key] && Math.random() < quantumCoefficient) {
      //alert('Chance for quantum swap!')
      choices.push(key)
    }
  }
  if (choices.length > 0) {
    chosenDir = choices[randomInt(choices.length)]
  } else {
    //alert('No options? Guess we spin the wheel.')
    chosenDir = getRandomDirection()
  }
  return chosenDir
}
```
This annoying bot tries to buzz around the strongest bot without getting swatted and has a slight chance of phasing through those who try to hunt it. It has a tendency to draw the two most powerful bots into close proximity... ;)
[Answer]
# Retired ICE Agent, JavaScript
**Preferred colour: `indianred`**
```
function(me, others, coins) {
    me.arenaLength = me.arenaLength - 1;
    // Calculate the average coin value of bots
    var avg = 2;
    for (var i = 0; i < others.length; i++) {
    avg += others[i][2];
    }
    avg /= others.length;
    // Find nearest coins
    var min = [];
    var min_distance = 100000
    for (var j = 0; j < coins.length; j++) {
    var distance = Math.sqrt(Math.pow(me.locationX - coins[j][0],2) + Math.pow(me.locationY - coins[j][1],2));
    if (distance < min_distance) {
        min_distance = distance;
        min = coins[j];
    }
    }
    if (me.coins <= avg || min_distance < 5) {
    // If own coinage is lower than the average or a coin is very close, find some coins
    // Move straight to the nearest coin
    if (me.locationY != min[1]) {
        if (me.locationY - min[1] > 0) {
        return "north";
        } else {
        return "south";
        }
    } else {
        if (me.locationX - min[0] > 0) {
        return "west";
        } else {
        return "east";
        }
    }
    } else {
        // You have enough money to eat most bots
        // Find the weakest bot
        var weakling = [];
        var weakling_money = 1000000;
        for (var k = 0; k < others.length; k++) {
            if (others[k][2] < weakling_money) {
                weakling_money = others[k][2];
                weakling = others[k];
            }
        }
        // Move to the weakest bot
        if (me.locationY != weakling[1]) {
            if (me.locationY - weakling[1] > 0) {
                return "north";
            } else {
                return "south";
            }
        } else {
            if (me.locationX - weakling[0] > 0) {
                return "west";
            } else {
                return "east";
            }
        }
    }
}
```
Now retired, this ICE agent is bitter about humanity. As a result, Retired ICE now targets the weakest bot, while keeping its coin value above the average (as per ICE policy).
[Answer]
# Greedy-Pursuit | [Haskell](https://www.haskell.org/)
**Preferred colour:** [`#62bda4`](https://duckduckgo.com/?q=color+picker+%2362bda4)
```
import Data.List
f x y c _ bs _
  | [bx,by,_]:_ <- sortByDist x y $ filter ((c>).last) bs = toDir (bx-x,by-y)
f x y _ _ _ cs
  | [cx,cy,_]:_ <- sortByDist x y cs = toDir (cx-x,cy-y)
f _ _ _ _ _ _ = "none"
sortByDist x y = sortOn (\[bx,by,_]-> abs (bx-x) + abs (by-y))
toDir (dx,dy)
  | dx > 0 = "east"
  | dx < 0 = "west"
  | dy > 0 = "south"
  | dy < 0 = "north"
  | otherwise = "none"
```
[Try it online!](https://tio.run/##dZBBa4QwEIXv@RUP6UFpXLReSlk9FI8LPfRoRWLMotTVxWTZCP3vNnGzshTKXDKPN9@8Scvkt@j7ZelO53FSyJliu0MnFSFHaMzgqFBLVAT4QVFrWs@0Kt8q7ENIM/E@58a9Wp9w7HolJvg@z4Jdz6QK7GwKNeadkWsd2vlwDhy8WovLG5xryv@F8wcOtxzuONVDpfCGcRAeIeTPeLryPgb4X9sRYQZm4q2xAjy7xmIDQtyqRtPGrLH5Go0Mkd0hzGXeXdvftKvYtPnuk@NFtZvojIPJ4cRRtWK6dlJswZcT6wbTnS/qU02Hwf4pXpAgxiuKIqEJjcrSvGIa0bgsl18 "Haskell – Try It Online")\*
Pretty simple strategy, picks the first decision from:
* if there are bots with less coins: pick closest and move towards it
* if there are coins: pick closest and move towards it
* default: stay
The bot only tries to catch other bots or coins without caring about potentially stronger bots that might try to catch it.
\* I don't really know JavaScript but I did the thing with google (might be inaccurate): [Try it online!](https://tio.run/##jVRNj9owEL3nV4xQD/GuE74uK9gEFXGk6qGXrmiEHGOWtGCjxCyJ6P52OnacbFip7ZIDnq83b/wm@cleWMHz7KgDqTbieu3fQXY4qlzDgmkWLrNCe94WSqiAwxrSAtYewG9YpSVNK7pOJmt4DKDAinm1wGyb@gm22V6LHHyfxyTcs0ITUxuBVosM3WkZmPqgIg58bR9e1OC8pPyv4LyDww0OdzjrzhNBTyopep7nvSuPLN5XCf6PdoggBob0LC0C984wsMTzXKtNSTfYxvDblBDDwPQQOFmv8T3WvrNofVWTV6iT3rVOlyiRh3MqvRP5OStEh/hdH8lvT5LrTEnYgr9Vp5ymSheUq0wWBC5Y@8JyHCsCEwz3ijOT/X3qItX7yFMT4U3EYk095z4L9gt1i8D0CWsV/RSiGC6QC33KJaSrUYIj8Cm8khZNHY4sN@x9RlNyk88wOYUZBEOYoBFbw5wHiNAAnLG0rPhN4RemdyFK4WNgNUhQmvsb1zBBgToYHaEjcycxlKHx@Sb7MzX4c0vNsfXPNkCo/Z8T0o5TSx41ot@wcuLP3sT/92/SrMbsbTX@W1E1PdrF@UCJa1KvFbrqRcIL8joXJDZmyAzXqZY63Av5rHemH7Ft6iT3mtTX6VIJimCKxR731CDY1fkQQL2wnXqb6W7U8TQsncfev18DWeWpO9eSe6/Xfh8OLJPY5XjS33S@lOajAyMY42o9wGo1pmM6SBI8DemADpPE40oWai/wVXj2Yetfeu3b0pvAiEJrP6E9RtuSxvMQz7guki3tpOh5eKVtB9p2IECm1z8 "JavaScript (Node.js) – Try It Online")
[Answer]
# Coin Magnet | JavaScript
```
CoinMagnet=(myself,others,coins)=>{
  x=myself.locationX;
  y=myself.locationY;
  power=myself.coins;
  arenaSize=myself.arenaLength;
  dirX=0;
  dirY=0;
  for(i=0;i<coins.length;i++){
    if(i==0){
      dirX+=(coins[i][0]-x)*3
      dirY+=(coins[i][1]-y)*3
    }
    dirX+=(coins[i][0]-x)*2
    dirY+=(coins[i][1]-y)*2
  }
  for(i=0;i<others.length;i++){
    dirX+=Math.ceil(0.85*others[i][2])*(others[i][0]-x)
    dirX+=Math.ceil(0.85*others[i][2])*(others[i][1]-y)
  }
  if(Math.abs(dirX)>Math.abs(dirY)){
    if(dirX>0){return "east";}
    else{return "west";}
  }
  else if(dirY!=0){
    if(dirY>0){return "south";}
    else{return "north";}
  }
  return "none";
}
```
This bot is rather dumb, it heads in the direction of the most acquirable coins. This includes coins that it can not get because other bots have higher power than itself.
[Answer]
# ICE Agent | Javascript
```
function(me, others, coins) {
    me.arenaLength = me.arenaLength - 1;
    // Calculate the average coin value of bots
    var avg = 2;
    for (var i = 0; i < others.length; i++) {
        avg += others[i][2];
    }
    avg /= others.length;
    // Find nearest coins
    var min = [];
    var min_distance = 100000
    for (var j = 0; j < coins.length; j++) {
        var distance = Math.sqrt(Math.pow(me.locationX - coins[j][0],2) + Math.pow(me.locationY - coins[j][1],2));
        if (distance < min_distance) {
            min_distance = distance;
            min = coins[j];
        }
    }
    if (me.coins <= avg || min_distance < 5) {
        // If own coinage is lower than the average or a coin is very close, find some coins
        // Move straight to the nearest coin
        if (me.locationY != min[1]) {
            if (me.locationY - min[1] > 0) {
                return "north";
            } else {
                return "south";
            }
        } else {
            if (me.locationX - min[0] > 0) {
                return "west";
            } else {
                return "east";
            }
        }
    } else {
        // You have enough money to eat most bots
        // Check if already on border
        if (me.locationX == 0 || me.locationX == me.arenaLength || me.locationY == 0 || me.locationY == me.arenaLength) {
            // Move anticlockwise around the border
            if (me.locationX == 0 && me.locationY != 0 && me.locationY != me.arenaLength) {
                return "south";
            }
            if (me.locationX == 0 && me.locationY == 0) {
                return "south";
            }
            if (me.locationY == me.arenaLength && me.locationX != 0 && me.locationX != me.arenaLength) {
                return "east";
            }
            if (me.locationX == 0 && me.locationY == me.arenaLength) {
                return "east";
            }
            if (me.locationX == me.arenaLength && me.locationY != 0 && me.locationY != me.arenaLength) {
                return "north";
            }
            if (me.locationX == me.arenaLength && me.locationY == me.arenaLength) {
                return "north";
            }
            if (me.locationY == 0 && me.locationX != 0 && me.locationX != me.arenaLength) {
                return "west";
            }
            if (me.locationX == me.arenaLength && me.locationY == 0) {
                return "west";
            }
        } else {
            // Find the nearest border and move to it
            if (me.locationX <= me.arenaLength - me.locationX) {
                // Move to left border
                return "west";
            } else {
                // Move to right border
                return "east";
            }
        }
    }
}
```
What's the point of a border if isn't being patrolled? ICE moves anticlockwise around the border, picking up any bots which stray into its path.
Before it can do that, it needs to be able to eat other bots first. For that reason ICE, keeps its coins above the average of all of the bots.
Guaranteed to steal kids from their parents™
[Answer]
# X Marks The Spot | JavaScript
```
function(me, others, coins){
    if (me.locationY != 0) {
        // If not on X axis
        if (others.every(other => other[1]==me.locationY-1)) {
            // If any in my way
            if (!others.every(other => other[0]==me.locationX-1)) {
                if (me.locationX != 0) {
                    // If no one to my left and not on edge of board
                    return "west"
                } else {
                    return "none"
                }
            } else if (!others.some(other => other[0]==me.locationX+1)) {
                if (me.locationX != me.arenaLength-1) {
                    // If no one to my right and not on edge of board
                    return "east"
                } else {
                    return "none"
                }
            } else {
                // I'm surrounded
                return "none"
            }
        } else {
            // No one in my way
            return "north"
        }
    } else {
        // If on the x axis
        if (!others.some(other => Math.abs(other[0]-me.locationX)==1 && other[1] == me.locationY)) {
            // If no one next to me
            move = ["east","west"][Math.floor(Math.random()*2)]
            // Prevent from falling off the board
            if (move == "east" && me.locationX == me.arenaLength-1) {
                return "west"
            } else if (move == "west" && me.locationX == 0) {
                return "east"
            } else {
                return move
            }
        } else {
            // I'm surrounded
            return "none"
        }
    }
}
```
X marks the spot, so all the gold must be on the x axis, right? My bot makes a beeline for the y=0 line then stays there, moving about randomly.
[Answer]
# Firebird
```
    function(me,others,coins) {
        var x = me.locationX;
        var y = me.locationY;
        var safe = [true, true, true, true];
        var threats = [];
        var targets = [];
        var opps = [];
        
        var meTo = (loc) => (Math.abs(x - loc[0]) + Math.abs(y - loc[1]));
        var inSquare = (loc, r) => (Math.abs(loc[0] - x) <= r && Math.abs(loc[1] - y) <= r);
        var distance = (from, loc) => (Math.abs(from[0] - loc[0]) + Math.abs(from[1] - loc[1]));
        var attackRange = (from, check, r) => {
            for (var i = 0; i < check.length; i++) {
                if (distance(check[i], from) == (r || 1)) {
                    return true;
                }
            }
            return false;
        };
        var dirStr = (dir) => (['north','east','south','west'][dir]);
        
        var i, n, o, p;
        for (i = 0; i < others.length; i++) {
            o = others[i];
            if (o[2] >= me.coins) {
                threats.push(o);
            } else {
                targets.push([o[0], o[1], Math.floor(o[2] * 0.55)]);
            }
        }
        for (i = 1; i < 5; i++) {
            targets.push([coins[i][0], coins[i][1], 2]);
        }
        targets.push([coins[0][0], coins[0][1], 5]);
        if (y === 0 || attackRange([x, y - 1], threats)) {
            safe[0] = false;
        }
        if (x == me.arenaLength - 1 || attackRange([x + 1, y], threats)) {
            safe[1] = false;
        }
        if (y == me.arenaLength - 1 || attackRange([x, y + 1], threats)) {
            safe[2] = false;
        }
        if (x === 0 || attackRange([x - 1, y], threats)) {
            safe[3] = false;
        }
        if (safe.includes(false)) {
            if (!(safe[0]) && safe[2]) {
               opps.push(2);
            }
            if (!(safe[1]) && safe[3]) {
                opps.push(3);
            }
            if (!(safe[2]) && safe[0]) {
                opps.push(0);
            }
            if (!(safe[3]) && safe[1]) {
                opps.push(1);
            }
        } else {
            targets.sort((a,b)=>(meTo(a) - meTo(b)));
            o = targets[0];
            if (o[0] == x) {
                if (o[1] < y) {
                    return 'north';
                } else {
                    return 'south';
                }
            } else if (o[1] == y) {
                if (o[0] < x) {
                    return 'west';
                } else {
                    return 'east';
                }
            } else if (Math.abs(o[0] - x) < Math.abs(o[1] - y)) {
                if (o[1] < y) {
                    return 'north';
                } else {
                    return 'south';
                }
            } else if (Math.abs(o[0] - x) > Math.abs(o[1] - y)) {
                if (o[0] < x) {
                    return 'west';
                } else {
                    return 'east';
                }
            }
        }
        console.log(safe[opps[0]]);
        var lx, ly;
        for (i = 0; i < opps.length; i++) {
            if (opps[i] === 0) {
                lx = x;
                ly = y - 1;
            }
            if (opps[i] == 1) {
                lx = x + 1;
                ly = y;
            }
            if (opps[i] == 2) {
                lx = x;
                ly = y + 1;
            }
            if (opps[i] == 3) {
                lx = x - 1;
                ly = y;
            }
            if (attackRange([lx, ly], targets, 0)) {
                return dirStr(opps[i]);
            }
        }
        return dirStr(opps[0]);
    }
```
Completely revamped to be more deadly than before (:
[Answer]
# A-Path-y | JavaScript
Preferred color for this bot is `#0077b3`.
```
 run: function (me, others, coins)
{
    var X_INDEX = 0;
    var Y_INDEX = 1;
    var COIN_INDEX = 2;
    var GOLD_POINTS = 5;
    var SILVER_POINTS = 2;
    var NORTH = 0;
    var SOUTH = 1;
    var WEST = 2;
    var EAST = 3;
    var IDLE = 4;
    var MOVE_COMMANDS_COUNT = IDLE+1;
    var MAP_TYPE_BLANK = 0;
    var MAP_TYPE_BOT = 1;
    var MAP_TYPE_GOLD_COIN = 2;
    var MAP_TYPE_SILVER_COIN = 3;
    var MIDGAME_THRESHOLD = 25;
    var PATH_FINDING_MAX_STEPS = 10000;
    var offsets = [[0,-1],[1,0],[0,1],[-1,0]];
function randInt(min,max)
    {
        return  Math.floor(Math.random() * ((max - min) + 1)) + min;
    }
    /**
     * Find a path using a*, returns the direction to take from the starting position coupled with a metric describing the cost of the path
     */
function pathFind(startX,startY,targetX,targetY,map,mapSize)
    {
        var i;
        var j;
        // shuffleIndecies to make path selection slightly random
        var indecies = [0,1,2,3];
        var shuffleIndecies = new Array(4);
        for (j=0;j<4;j++)
        {
            var randomIndex = randInt(0,3-j);
            shuffleIndecies[j] = indecies[randomIndex];
            indecies[randomIndex] = indecies[0];
            var lastElementIndex = 4-j-1;
            indecies[0] = indecies[lastElementIndex];
        }
        // A*
        if (!(startX===targetX && startY===targetY))
        {
            var tileX = new Array(PATH_FINDING_MAX_STEPS);
            var tileY = new Array(PATH_FINDING_MAX_STEPS);
             var fscore = new Array(PATH_FINDING_MAX_STEPS);
             var gscore = new Array(PATH_FINDING_MAX_STEPS);
             var openList = new Array(PATH_FINDING_MAX_STEPS);
             var tileParent = new Array(PATH_FINDING_MAX_STEPS);
             var tileIsClosed = new Array(mapSize);
             for (i = 0;i<PATH_FINDING_MAX_STEPS;i++)
             {
                 tileX[i]=0;
                 tileY[i]=0;
                 fscore[i]=0;
                 gscore[i]=0;
                 openList[i]=0;
                 tileParent[i]=0;
             }
             for (i = 0;i<mapSize;i++)
             {
                 var newArray = new Array(mapSize);
                 tileIsClosed[i] = newArray;
                 for (j = 0;j<mapSize;j++)
                 {
                     tileIsClosed[i][j] = 0;
                 }
             }
             var currentIndex = -1;     
            var openListSize=1;
            var tileId=1;
            tileX[0]=targetX;
            tileY[0]=targetY;
            fscore[0]=1;
            gscore[0]=map[targetX][targetY].negativeWeight;
            do
            {
              var currentBestIndex=-1;
              var currentBestScore=2147483647;
              //  Look for the lowest F cost square on the open list
              for (var ii=0;ii<openListSize;ii++)
              {
                if (fscore[openList[ii]]<currentBestScore)
                {
                  currentBestScore=fscore[openList[ii]];
                  currentBestIndex=ii;
                }
              }
              if (currentBestIndex===-1)
              {
                break;
              }
              currentIndex=openList[currentBestIndex];
              var currentTileX=tileX[currentIndex];
              var currentTileY=tileY[currentIndex];
              // found path
              if (startX===currentTileX && startY===currentTileY)
              {
                break;
              }
              // if not in closed list
              if (tileIsClosed[currentTileX][currentTileY]===0)
              {
                    // Switch it to the closed list.
                    tileIsClosed[currentTileX][currentTileY]=1;
                    // remove from openlist
                    openList[currentBestIndex]=openList[--openListSize];   
                    // add neighbours to the open list if necessary
                    for (j=0;j<4;j++)
                    {
                        i = shuffleIndecies[j];
                        var surroundingCurrentTileX=currentTileX+offsets[i][0];
                        var surroundingCurrentTileY=currentTileY+offsets[i][1];
                        if (surroundingCurrentTileX>=0 && surroundingCurrentTileX<mapSize &&
                            surroundingCurrentTileY>=0 && surroundingCurrentTileY<mapSize )
                        {
                          tileX[tileId]=surroundingCurrentTileX;
                          tileY[tileId]=surroundingCurrentTileY;
                          var surroundingCurrentGscore=gscore[currentIndex] + map[surroundingCurrentTileX][surroundingCurrentTileY].negativeWeight;
                          gscore[tileId]=surroundingCurrentGscore;
                          fscore[tileId]=surroundingCurrentGscore+Math.abs( surroundingCurrentTileX-startX)+Math.abs( surroundingCurrentTileY-startY);
                          tileParent[tileId]=currentIndex;
                          openList[openListSize++]=tileId++;
                        }
                    }
              }
              else
              {
              // remove from openlist
              openList[currentBestIndex]=openList[--openListSize];    
              }
            } while(true);
            if (tileX[tileParent[currentIndex]]<startX) return {moveDirection:WEST, pathLength:currentIndex, pathScore:gscore[currentIndex]+currentIndex/4};
            else if (tileX[tileParent[currentIndex]]>startX) return {moveDirection:EAST, pathLength:currentIndex, pathScore:gscore[currentIndex]+currentIndex/4};
            else if (tileY[tileParent[currentIndex]]<startY) return {moveDirection:NORTH, pathLength:currentIndex, pathScore:gscore[currentIndex]+currentIndex/4};
            else if (tileY[tileParent[currentIndex]]>startY) return {moveDirection:SOUTH, pathLength:currentIndex, pathScore:gscore[currentIndex]+currentIndex/4};
        }
        console.log("Path finding failed");
        return {moveDirection:IDLE, pathLength:0, pathScore:2147483647};
     }
function process(info,bots,coins)
    {
        var i;
        var j;
        var k;
        var x;
        var y;
        // initialise map
        var mapSize = info.arenaLength;
        var map = new Array(mapSize);
        for (i = 0;i < info.arenaLength;i++)
        {
            var newArray = new Array(info.arenaLength);
            map[i] =  newArray;
            for (j = 0;j < mapSize;j++)
            {
                map[i][j] = {type:MAP_TYPE_BLANK, coins: 0 , negativeWeight:i===0||i===mapSize-1||j===0||j===mapSize-1?3:1};
            }
        }
        // populate map with bots
        for (i = 0 ; i<bots.length;i++)
        {
            map[bots[i][X_INDEX]][bots[i][Y_INDEX]].type = MAP_TYPE_BOT;
            map[bots[i][X_INDEX]][bots[i][Y_INDEX]].coins = bots[i][COIN_INDEX];
            for (j=-1;j<2;j++)
            {
                x = bots[i][X_INDEX] + j;
                if (x>=0 && x < mapSize)
                {
                    for(k=-1;k<2;k++)
                    {
                        if (Math.abs((k+j)%2) === 1)
                        {
                            y = bots[i][Y_INDEX] + k;
                            if (y>=0 && y< mapSize )
                            {
                                // are we adjacent the bot or potentially will be?
                                if (Math.abs(info.locationX-x)<=1 && Math.abs(info.locationY-y)<=1)
                                {
                                    // make the cell significantly less attractive when the bot is stronger than us, or
                                    // make the cell slightly more attactive when the bot is weaker than us, or
                                    // not change if the bot has no coins
                                    map[x][y].negativeWeight+= bots[i][COIN_INDEX] >= info.coins?100000:(bots[i][COIN_INDEX]===0?0:-1);
                                }
                                // another bot is not a direct threat/target
                                else
                                {
                                    // make the cell moderately less attractive when the bot is stronger than us, or
                                    // make the cell slightly more attactive when the bot is weaker than us, or
                                    // not change if the bot has no coins
                                    map[x][y].negativeWeight+= bots[i][COIN_INDEX] >= info.coins?3:(bots[i][COIN_INDEX]===0?0:-1);
                                }
                            }
                        }
                    }
                }
            }
        }
        // populate map with coins
        for (i = 0 ; i<coins.length;i++)
        {
            map[coins[i][X_INDEX]][coins[i][Y_INDEX]].type = i === 0?MAP_TYPE_GOLD_COIN:MAP_TYPE_SILVER_COIN;
            map[coins[i][X_INDEX]][coins[i][Y_INDEX]].coins = i === 0?GOLD_POINTS:SILVER_POINTS;
            // check to see whether bots are adjacent to the coin
            for (j=-1;j<2;j++)
            {
                x = coins[i][X_INDEX] + j;
                if (x>=0 && x < mapSize)
                {
                    for(k=-1;k<2;k++)
                    {
                        if ((k+j)%2 === 1)
                        {
                            y = coins[i][Y_INDEX] + k;
                            if (y>=0 && y< mapSize )
                            {
                                if (map[x][y].type === MAP_TYPE_BOT)
                                {
                                    // this coin looks like a trap as a stronger bot is adjacent to it
                                    if (map[x][y].coins >= info.coins)
                                    {
                                        map[coins[i][X_INDEX]][coins[i][Y_INDEX]].negativeWeight+=100000;
                                    }
                                    else
                                    {
                                        // are we adjacent the coin? we might be able to kill another bot if it trys to get the coin
                                        if (Math.abs(info.locationX-coins[i][X_INDEX])<=1 && Math.abs(info.locationY-coins[i][Y_INDEX])<=1)
                                        {
                                            map[coins[i][X_INDEX]][coins[i][Y_INDEX]].negativeWeight+=-20;
                                        }
                                        // another bot is likely to get this coin... make it less attractive
                                        else
                                        {
                                            map[coins[i][X_INDEX]][coins[i][Y_INDEX]].negativeWeight=+100;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            // add the coin attractiveness, more for gold coins
            map[coins[i][X_INDEX]][coins[i][Y_INDEX]].negativeWeight += i === 0?-20:-10;
        }
        var pathBest = {moveDirection:IDLE, pathLength: 2147483647, pathScore: 2147483647};
        if (info.coins > MIDGAME_THRESHOLD)
        {
            var viableCoinCount =0;
            var viableCoins = new Array(5); 
            // find coins that are reachable before any other bot
            outer1:
            for (j = 0 ; j<coins.length;j++)
            {
                var contention = 0;
                var myDistanceToCoin = Math.abs(info.locationX-coins[j][X_INDEX]) + Math.abs(info.locationY-coins[j][Y_INDEX]);
                for (i = 0 ; i<bots.length;i++)
                {
                    var dist = Math.abs(bots[i][X_INDEX]-coins[j][X_INDEX]) + Math.abs(bots[i][Y_INDEX]-coins[j][Y_INDEX]);
                    if (dist < myDistanceToCoin)
                    {
                        continue outer1;
                    }
                }
                viableCoins[viableCoinCount++] = j;
            }
            // no coins are reachable before another bot so find the cell that is furthest away from any bot and head there
            if (viableCoinCount ===0)
            {
                var mostIsolatedCellX = mapSize/2;
                var mostIsolatedCellY = mapSize/2;
                var mostIsolatedCellMinBotDistance = 0;
                for (x=5;x<mapSize-5;x++)
                {
                    for (y=5;y<mapSize-5;y++)
                    {
                        if (x!= info.locationX && y!=info.locationY)
                        {
                            // ignore coin attractiveness
                            map[x][y].negativeWeight = map[x][y].negativeWeight<-4?map[x][y].negativeWeight:1;
                            var currentCellMinBotDistance = 2147483647;
                            for (i = 0 ; i<bots.length;i++)
                            {
                                var dist = Math.abs(bots[i][X_INDEX]-x) + Math.abs(bots[i][Y_INDEX]-y) + Math.abs(info.locationX-x) + Math.abs(info.locationY-y);
                                if (dist < currentCellMinBotDistance )
                                {
                                    {
                                        currentCellMinBotDistance = dist;                           
                                        if (currentCellMinBotDistance>mostIsolatedCellMinBotDistance)
                                        {
                                            mostIsolatedCellMinBotDistance = currentCellMinBotDistance;
                                            mostIsolatedCellX=x;
                                            mostIsolatedCellY=y;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                // attempt to find path to most isolated cell
                pathBest = pathFind(info.locationX, info.locationY, mostIsolatedCellX,mostIsolatedCellY, map, mapSize);
            }
            // attempt to find paths to each viable coin, keeping the best result
            for (i = 0 ; i<viableCoinCount;i++)
            {
                var path = pathFind(info.locationX, info.locationY, coins[viableCoins[i]][X_INDEX],coins[viableCoins[i]][Y_INDEX], map, mapSize);
                if (path.pathScore < pathBest.pathScore)
                {
                    pathBest = path;
                }
            }
        }
        else
        {
            // attempt to find paths to each coin, keeping the best result
            for (i = 0 ; i<coins.length;i++)
            {
                var path = pathFind(info.locationX, info.locationY, coins[i][X_INDEX],coins[i][Y_INDEX], map, mapSize);
                if (path.pathScore < pathBest.pathScore)
                {
                    pathBest = path;
                }
            }
        }
        var move = IDLE;
        if (pathBest.pathLength === 2147483647)
        {
            outer:
            for (i=0;i<MOVE_COMMANDS_COUNT;i++)
            {
                switch (i)
                {
                    case NORTH:
                        if (info.locationY-1 < 0)
                        {
                            continue;
                        }
                        move = i;
                        break outer;
                    case SOUTH:
                        if (info.locationY+1 === info.arenaLength)
                        {
                            continue;
                        }
                        move = i;
                        break outer;
                    case WEST:
                        if (info.locationX-1 < 0)
                        {
                            continue;
                        }
                        move = i;
                        break outer;
                    case EAST:
                        if (info.locationX+1 === info.arenaLength)
                        {
                            continue;
                        }
                        move = i;
                        break outer;
                    case IDLE:
                        move = i;
                        break;
                    default:
                }
            }
        }
        else
        {
            move = pathBest.moveDirection;
        }
        switch (move)
        {
        case NORTH:
            return "north";
        case SOUTH:
            return "south";
        case EAST:
            return "east";
        case WEST:
            return "west";
        default:
            return "none";
        }
    }
    return process(me, others, coins);
}
```
This bot uses path finding coupled with a map of cell desirabilities to avoid bots that could kill us, go for coins that are close, not traps and are least risky.
It does not seem to be a contender for the winning place, but it does hold its own and will be alive at the end of the match if it survives the initial melee.
Bot now has mid-to-late game strategy that ignores coins it cannot reach before other bots and if cannot go to any coin then moves to the closest cell that is furthest away from all other bots that are stronger than itself.
It now has a chance of winning.
*Note* sorry for the crappy code, I have automatically converted it from Java
]