Gold Collector KoTH

47

14

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 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

Redwolf Programs

Posted 2018-08-08T14:06:30.647

Reputation: 2 561

6"Note that all submissions will be converted to Javascript, to make things simple." How is this supposed to work? Do you make the conversion? – Laikoni – 2018-08-08T14:20:37.470

21There's nothing wrong with a koth only allowing a single language, especially one as widely spread as JavaScript. Rather than ambiguously "converting" answers to JavaScript (presumably yourself and by hand) you should simply limit the challenge to JS only. We've had plenty of Java-only and Python-only koths in the past, after all. – Skidsdev – 2018-08-08T14:23:25.340

@Laikoni Well, most object oriented languages that have arrays, integers, strings, and functions are similar, just with slightly different syntax, so I will just convert it myself – Redwolf Programs – 2018-08-08T16:05:59.767

@Mayube yes, but limiting people to one language isn't neccessary with this challenge – Redwolf Programs – 2018-08-08T16:07:49.663

1

You should add a rule about external libraries, I imagine that a bot using scikit (or whatever other lib) would be time consuming to translate, even if it's only a few ten lines.

– ბიმო – 2018-08-08T16:28:36.993

Gold coin will always be first position in array? even when someone collects it? – Luis felipe De jesus Munoz – 2018-08-08T18:14:35.817

@LuisfelipeDejesusMunoz When a coin is collected, a new one is generated elsewhere – Redwolf Programs – 2018-08-08T18:15:08.430

2A version of this where you control acceleration instead of position would be pretty cool. – akozi – 2018-08-08T19:21:56.923

@RedwolfPrograms Please upload the link to the controller for some testing. Also I notice this challenge looks like the game agar.io – Luis felipe De jesus Munoz – 2018-08-08T19:38:28.540

@LuisfelipeDejesusMunoz Never heard of it. Also, how do I upload a link? Is there some sort of imgur for other files? – Redwolf Programs – 2018-08-08T19:43:14.580

@RedwolfPrograms For uploading, Github Gist (https://gist.github.com/) is a pretty standard place for code.

– sundar - Reinstate Monica – 2018-08-08T20:02:24.653

12To everyone: There are waaaay too many comments already. Do not leave useless comments such as "+1, nice bot", and please delete your comments if it's redundant. We are not a typical Q&A site, but nobody likes reading hundreds of commands. – user202729 – 2018-08-09T05:16:29.900

Is this trying to get a bot to play hole.io? :) – beirtipol – 2018-08-09T13:39:40.643

@beirtipol What is hole.io? – Redwolf Programs – 2018-08-09T13:43:49.547

1Are bot moves sequential? Or turn based/all at once? In other words, does each bot, in turn, get the current game state, execute its move alone, and the updated game state is sent to the next bot? Or do you submit the same current game state to all bots, get all planned moves, then execute them all simultaneously? (This would matter, for example, if you and a slightly larger bot were on opposite sides of a gold coin: could you eat the gold coin and grow before the other bot moved?) – BradC – 2018-08-09T20:40:56.487

5(To my own question): According to N.P. in chat, it is the latter: all the bots move, then all the conflicts get resolved, then all the coin pickups occur, then all the new coins are placed. – BradC – 2018-08-09T20:59:18.077

@RedwolfPrograms hole.io is a game for Android - you control a hole that moves around a map swallowing up people/objects/cars and smaller holes/players: https://play.google.com/store/apps/details?id=io.voodoo.holeio&hl=en

– beirtipol – 2018-08-21T17:52:57.540

Answers

25

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!

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)]

}

Cain

Posted 2018-08-08T14:06:30.647

Reputation: 1 149

1Ha, that's a neat idea, I like it. – sundar - Reinstate Monica – 2018-08-08T21:37:33.780

This approach is pretty cool...no, very cool – Redwolf Programs – 2018-08-09T00:45:55.133

1BaitBot needs nearestThreat && distanceTo(nearestThreat) rather than just distanceTo(nearestThreat). It fails when there is no threat. – Redwolf Programs – 2018-08-10T13:55:30.110

1Yes, nearestThreat is undefined if all other bots have more points than yours. – Night2 – 2018-08-10T17:35:19.687

@RedwolfPrograms Thanks, nice catch, I have fixed it. – Cain – 2018-08-13T15:02:23.980

@Cain While your at it, you should probably teach BaitBot how not to jump off the edge of the arena – Redwolf Programs – 2018-08-13T15:04:35.023

@RedwolfPrograms Is that happening? I though since it only ever moves towards a coin, that wouldn't be an issue. – Cain – 2018-08-13T15:13:03.630

1Well, I get notices like [10] Bot Bait Bot tired of this world, and jumped off its edge in my event log – Redwolf Programs – 2018-08-13T15:17:33.427

And it never makes it very far – Redwolf Programs – 2018-08-13T15:17:57.053

@RedwolfPrograms Sigh, sounds like debugging time. Is there a controller loaded with the existing bots available? – Cain – 2018-08-13T15:21:29.577

@Cain Yes, I'll link to it in the Chatroom – Redwolf Programs – 2018-08-13T15:22:34.950

The code I added should be placed in the developer console, and activated with activateCanvas(fps, 500). Be sure to also Ctrl + C and Ctrl + V the BotData file – Redwolf Programs – 2018-08-13T15:26:18.483

Let us continue this discussion in chat.

– Cain – 2018-08-13T17:57:46.700

17

First Gen Learning Algorithm | JavaScript (Node.js)

function run() {
	return ['north','east','south','west'][(Math.random()*4)|0];
}

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...

Skidsdev

Posted 2018-08-08T14:06:30.647

Reputation: 9 656

LOL...It might still work! – Redwolf Programs – 2018-08-08T16:10:01.063

2Most learning algorithms work literally random for the first few iterations, not just almost randomly. – fəˈnɛtɪk – 2018-08-08T16:56:07.253

Guess what? This bot got almost 0.22 coins per round on average! – Redwolf Programs – 2018-08-16T22:44:36.390

17

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!

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.

Mike Nichols

Posted 2018-08-08T14:06:30.647

Reputation: 271

1Welcome to PPCG! That's a very good bot =D – Luis felipe De jesus Munoz – 2018-08-10T20:55:31.750

This is what I was thinking for a bot. Good job. – Jo. – 2018-08-11T18:48:33.950

16

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.

Ramillies

Posted 2018-08-08T14:06:30.647

Reputation: 1 923

I think this bot probably beats out my weighted movement too. – fəˈnɛtɪk – 2018-08-08T18:18:49.177

@fəˈnɛtɪk — So do I :—). I think that this one can handle the other bots quite a bit better. (Also it's not "blinded" by a coin at an adjacent square.) But I definitely gave you +1 because I'm a huge fan of this idea. – Ramillies – 2018-08-08T18:25:11.920

Could you apply the principle of least action to this? – Beta Decay – 2018-08-08T19:05:57.067

@BetaDecay: Not quite I'm afraid, calculus doesn't respond well to discrete problems like this. Even in the continuous case it wouldn't be trivial (because of fixed magnitude of velocity), but it could be doable after some polar coordinates magic I guess. – Ramillies – 2018-08-08T19:10:49.450

That's a shame :D I look forward to seeing how this does – Beta Decay – 2018-08-08T19:12:38.980

@BetaDecay: Acutally even the continuous case would be more like the hell on earth. Probably your best bet would be using 2 ODEs: dz/dt = v exp[ i φ(t) ] (z = coordinates of the bot in the complex plane) and dφ/dt = ... hm... some monster-expression in φ and z :—). Definitely not anything I would like to solve. – Ramillies – 2018-08-08T19:18:10.690

This isn't a bot, this is a step towards unification! – theREALyumdub – 2018-08-08T21:10:27.183

@theREALyumdub, unification of what? Bots? :—) (Actually there are more people fond of this method here, see the Weighted Motion and The Bot ... Can Play Aggressive.) – Ramillies – 2018-08-08T21:14:27.657

@Ramilles No, a step towards unifying physics of course. Since the bot is made to move, of course this is the natural path. – theREALyumdub – 2018-08-08T21:36:24.807

4This is cool. I'm not sure risk is monotonically related to distance though, it seems like a threat 2 spaces away is more likely to kill you than one adjacent, since in the latter case one of you needs to stay stationary for a collision to occur. – Cain – 2018-08-08T21:45:09.160

@Cain agreed, to my math mind this is more like chess than driving a robot. The latter would require A* – theREALyumdub – 2018-08-08T21:46:32.613

@Cain, that sounds like a good idea. I'll be testing this thing in the controller tomorrow (until now it has been just a pure speculation), and I'll see what I can think up about it. (However, now when I see your BaitBot, I'll maybe leave the potentials alone :-D.) – Ramillies – 2018-08-08T22:03:26.430

@Cain: actually, I won't modify them even though I think you're right. The reason is that if U(r = 1) < U(r = 2) (where r = distance from a bot that would eat me), then there would be a local minimum in the r = 1. My bot could then very well be the one to stay stationary (and to be eaten). – Ramillies – 2018-08-08T22:13:39.947

@Ramillies Fair enough, maybe bots staying stationary isn't as uncommon as I thought it would be. Very interested in seeing the preliminary results. – Cain – 2018-08-08T22:50:53.800

@Cain, I just meant that my bot would be trapped in the local minimum in this case. Staying put will probably be rare. I have a better argument: the steeply ascending exponential just makes sure that I will be running away from the dangerous bot. And if running straight away from him, I can't be eaten. So (at least in this case) the exponential does what it should (though it's not a good measure of the risk). – Ramillies – 2018-08-08T22:58:23.897

Uh-oh. Your bot has a few problems with the return Infinity part. (0 <= x < me.arenaLength) always returns true, so you can fall off the board! Let me fix that in the controller for you... – Redwolf Programs – 2018-08-10T14:05:32.213

11

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 and Big King Little Hill.

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".

Night2

Posted 2018-08-08T14:06:30.647

Reputation: 5 484

It seems a lot more...intelligent than the others – Redwolf Programs – 2018-08-08T16:15:17.443

5I like the monsters part of the params – Redwolf Programs – 2018-08-10T22:25:21.827

10

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.

fəˈnɛtɪk

Posted 2018-08-08T14:06:30.647

Reputation: 4 166

1Huh, this may actually win. Although, there are a lot of bots – Redwolf Programs – 2018-08-08T18:00:17.660

This method will have more problems the more unevenly the enemy bots with higher or equal value are placed. – fəˈnɛtɪk – 2018-08-08T18:01:37.347

1Well, they'll probably all be making a beeline to the coin nearest to them/gold coin. – Redwolf Programs – 2018-08-08T18:03:29.197

It will only do well if it manages to get some coins right at the start. – fəˈnɛtɪk – 2018-08-08T18:06:48.473

And that is mostly luck, since the place where coins generate is randomly decided – Redwolf Programs – 2018-08-08T18:13:12.513

If the bot is moving it theoretically can not be killed as that would require another bot to be able to get to the coin before it. It only dies if it stops moving :P – fəˈnɛtɪk – 2018-08-08T19:28:41.323

if(other[j][2]+2>=power){ should be others not other. – Night2 – 2018-08-10T17:19:40.087

One more: it should be Math.abs(... not abs(.... Error: Uncaught ReferenceError: abs is not defined – Night2 – 2018-08-10T17:24:04.077

I was aware that I had abs instead of Math.abs all over the place but I thought I had fixed all of them. – fəˈnɛtɪk – 2018-08-10T21:10:33.620

9

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 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).

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?

Beta Decay

Posted 2018-08-08T14:06:30.647

Reputation: 21 478

5Your Nobel Prize was lost in the mail, but we could give you an Emmy Award instead – Redwolf Programs – 2018-08-09T12:47:22.783

1Looks like physics starts getting popular in this challenge. :—D. And of course I'm very curious about how well it will do. – Ramillies – 2018-08-09T13:27:40.730

1(By the way, you could have saved the hassle with Euler-Lagrange equations, because they reduce to the well-known fact that F = c_me a = - grad U :—).) – Ramillies – 2018-08-09T13:34:59.067

@Ramillies Meh, it was more fun doing it this way :D – Beta Decay – 2018-08-09T13:48:55.010

Shouldn't the velocity be deciding which direction to go and not acceleration? – fəˈnɛtɪk – 2018-08-09T23:42:59.090

@fəˈnɛtɪk Possibly, but I don’t want to have to solve for that :D – Beta Decay – 2018-08-10T00:29:47.683

1Are you sure that you want to be using the k = coins of something else / your coins? You start with no coins... and with NaN everywhere, you're not too likely to get any. – Ramillies – 2018-08-10T14:38:16.570

@Ramillies Good point... – Beta Decay – 2018-08-10T14:39:21.067

9

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();
}

Michael Kunst

Posted 2018-08-08T14:06:30.647

Reputation: 513

8

Goldilocks, JavaScript (Node.js)

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!

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.)

sundar - Reinstate Monica

Posted 2018-08-08T14:06:30.647

Reputation: 5 296

This is a pretty nice, simple bot. I like it. – Redwolf Programs – 2018-08-08T16:09:14.370

2By the way, I'm using this bot as a test for my controller (: – Redwolf Programs – 2018-08-08T19:44:07.693

8

Third Gen Learning Algorithm | JavaScript (Node.js)

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!

After a few generations of learning, this bot has learned that leaving the arena = bad

Skidsdev

Posted 2018-08-08T14:06:30.647

Reputation: 9 656

Oh, good. I've heard this is called "Natural Selection" – Redwolf Programs – 2018-08-08T16:16:11.970

5Where is the second gen – Luis felipe De jesus Munoz – 2018-08-08T17:15:45.410

11@LuisfelipeDejesusMunoz He left the arena. – Jo. – 2018-08-09T00:13:54.323

This is a good bot for debugging the controller – Redwolf Programs – 2018-08-09T13:50:14.170

3Oh, by the way, the arena starts at 0, so it should be arenaLength - 1. This killed your bot a fair few times to many – Redwolf Programs – 2018-08-09T13:52:48.670

7

B33-L1N3 | JavaScript (Node.js)

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!

Makes a beeline for the closest coin

Skidsdev

Posted 2018-08-08T14:06:30.647

Reputation: 9 656

Oh, I thought B33-L1N3 was some sort of model number – Redwolf Programs – 2018-08-08T16:16:39.600

+1 for the name – Cain – 2018-08-08T21:47:23.997

let coins = ... Uncaught SyntaxError: Identifier 'coins' has already been declared – Night2 – 2018-08-10T17:20:48.700

Delete the let – Redwolf Programs – 2018-08-10T18:24:05.700

5

Damacy, JavaScript (Node.js)

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!

One last katamari-based bot for today, this time with a little bit of memory. Thanks to @BetaDecay 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.)

sundar - Reinstate Monica

Posted 2018-08-08T14:06:30.647

Reputation: 5 296

This is probably the only one right now that can catch others outside of just being lucky. – Cain – 2018-08-08T22:56:56.180

Isn't botnotes supposed to be "storedata" not setdata? – fəˈnɛtɪk – 2018-08-09T01:26:09.420

@fəˈnɛtɪk See, already needs a bugfix! :) Thanks, corrected now. – sundar - Reinstate Monica – 2018-08-09T07:59:12.953

You should replace aWt = 1 in the params with aWt, and put aWt = aWt || 1 underneath (Same with bWt). This prevents errors. – Redwolf Programs – 2018-08-10T14:57:14.333

5

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.

Laikoni

Posted 2018-08-08T14:06:30.647

Reputation: 23 676

1This wouldn't end well if another bot were created that had 1 more coin, and patrolled the border in the opposite direction (: – Redwolf Programs – 2018-08-08T23:07:04.223

8I would make a border control joke, but I'm leaving that one up to @BetaDecay – Redwolf Programs – 2018-08-09T00:53:28.160

5

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.

fəˈnɛtɪk

Posted 2018-08-08T14:06:30.647

Reputation: 4 166

Hmm, nuclear physics applied to treasure hunting? This beats Science Channel any day! – Redwolf Programs – 2018-08-09T02:41:32.707

You need to replace sin with Math.sin, cos with Math.cos, and so on – Redwolf Programs – 2018-08-10T15:23:22.743

4

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

fəˈnɛtɪk

Posted 2018-08-08T14:06:30.647

Reputation: 4 166

1Well, this looks like a pretty awesome bot. Be sure to check the directions, since it would be a real loss if your bot was a master at running away from coins (: – Redwolf Programs – 2018-08-08T17:17:01.970

Well, still. I've got to be nice, right? – Redwolf Programs – 2018-08-08T17:32:33.747

Well, post it! It'll make up for the smaller, faster bots that also exist at the moment in large quantities. – Redwolf Programs – 2018-08-08T17:37:23.997

for(i=0;i<6;i++){ there are only 5 coins in total, 1 gold and 4 silver. Your loop is running 6 times from 0 to 5. – Night2 – 2018-08-10T17:22:34.603

4

Not so Blindly | JavaScript (Node.js)

Important Note: This approach is not entirely mine and has been answered in a similar question. 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";
    }
}

Luis felipe De jesus Munoz

Posted 2018-08-08T14:06:30.647

Reputation: 9 639

1Wow...a pathfinding algorithm already? It's only been 3 hours! – Redwolf Programs – 2018-08-08T17:55:04.193

@RedwolfPrograms As i said, it is copied from another similar challenge. Just needed to adapt it to this one. – Luis felipe De jesus Munoz – 2018-08-08T17:55:39.530

My algorithms go for whichever coins are safest to go to. – fəˈnɛtɪk – 2018-08-08T17:59:25.423

4

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.

user48543

Posted 2018-08-08T14:06:30.647

Reputation:

This is the most basic bot that has a chance at winning – Redwolf Programs – 2018-08-09T00:51:29.370

4

KatamariWithValues, JavaScript (Node.js),

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!

(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.

sundar - Reinstate Monica

Posted 2018-08-08T14:06:30.647

Reputation: 5 296

A smart hunter...well, that's even better! – Redwolf Programs – 2018-08-08T19:35:57.723

@RedwolfPrograms Let's hope so! :) – sundar - Reinstate Monica – 2018-08-08T19:41:55.913

Should've called this Damacy ;) – Beta Decay – 2018-08-08T21:31:26.877

1

@BetaDecay Wish granted.

– sundar - Reinstate Monica – 2018-08-08T21:55:52.837

4

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.

N. P.

Posted 2018-08-08T14:06:30.647

Reputation: 161

3Hmm, it raises the bar, then gets drunk at it – Redwolf Programs – 2018-08-09T21:51:00.287

4

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!

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.

Zachary Cotton

Posted 2018-08-08T14:06:30.647

Reputation: 679

Thanks for the note. An expert evader has an okay chance if it happens to collect enough coins while being chased – Redwolf Programs – 2018-08-10T00:50:26.607

I like this. It's almost like bait for the hunting robots – Beta Decay – 2018-08-12T10:58:09.757

3

Blindy | JavaScript (Node.js)

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
}

Luis felipe De jesus Munoz

Posted 2018-08-08T14:06:30.647

Reputation: 9 639

Hmm, it might work since other bots will be seeking the gold primarily, potentially leaving you to get the silver without any fighting – Redwolf Programs – 2018-08-08T16:13:17.180

3

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.

Night2

Posted 2018-08-08T14:06:30.647

Reputation: 5 484

2

Greedy-Pursuit | Haskell

Preferred colour: #62bda4

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!*

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!

ბიმო

Posted 2018-08-08T14:06:30.647

Reputation: 15 345

6I wonder how he is going to translate haskell into js – Luis felipe De jesus Munoz – 2018-08-08T15:16:22.677

3@LuisfelipeDejesusMunoz: Yeah me too. But luckily, it's not very sophisticated code. – ბიმო – 2018-08-08T15:19:40.673

@LuisfelipeDejesusMunoz Just use Node.JS and process.open (or child_process.spawn, or similar) with some parsing. – user202729 – 2018-08-08T15:20:21.987

@LuisfelipeDejesusMunoz: I tried to translate it and added a link, but I don't feel very confident writing JavaScript, so it might be buggy. – ბიმო – 2018-08-08T15:48:03.197

4@LuisfelipeDejesusMunoz It would be one thing if this was a 10,000 line AI learning program, but I think I can manage this (: – Redwolf Programs – 2018-08-08T16:14:31.653

2

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... ;)

N. P.

Posted 2018-08-08T14:06:30.647

Reputation: 161

If he can't find a suitable target in getTargetDirections(), then interesting things start to happen. (Such as breaking down everything due to undefined has no property 0 error.) – Ramillies – 2018-08-11T10:40:49.810

2

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).

Beta Decay

Posted 2018-08-08T14:06:30.647

Reputation: 21 478

1

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.

Beta Decay

Posted 2018-08-08T14:06:30.647

Reputation: 21 478

Huh, an interesting method indeed – Redwolf Programs – 2018-08-08T16:15:39.937

1Python (2, unfortunately) interpreter in JS (...) – user202729 – 2018-08-08T16:21:07.100

1Given that The arena starts at (0,0) in the upper left corner, are you sure you want to move south to get to y=0? – AdmBorkBork – 2018-08-08T16:45:19.073

@AdmBorkBork Thanks, that could've been bad – Beta Decay – 2018-08-08T16:50:03.647

1

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.

fəˈnɛtɪk

Posted 2018-08-08T14:06:30.647

Reputation: 4 166

1

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 (:

Redwolf Programs

Posted 2018-08-08T14:06:30.647

Reputation: 2 561

2My absolute loser of a bot – Redwolf Programs – 2018-08-10T22:09:53.627

It's not targeting them, it's avoiding them – Redwolf Programs – 2018-08-10T22:24:10.187

Oh sorry, I misunderstood – Beta Decay – 2018-08-10T22:35:30.317

1

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™

Beta Decay

Posted 2018-08-08T14:06:30.647

Reputation: 21 478

This would be more funny if it weren't so relevant – Don Thousand – 2018-08-13T02:05:47.417

1

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

Moogie

Posted 2018-08-08T14:06:30.647

Reputation: 1 505

Be sure to get any bugs and/or updates out of the way soon, 18 hours to due date! – Redwolf Programs – 2018-08-14T17:55:20.573

@RedwolfPrograms Have you observed a bug? if so please let me know so I can correct. Thanks – Moogie – 2018-08-14T22:42:27.077

No, but you never know. Just be sure to double check, since I've seen many bots lose due to one wrong number, or wrong function, or mistype more times than I can count – Redwolf Programs – 2018-08-14T22:44:53.347

0

Seeker.Armageddon

function(me, others, coins) {
    var x = me.locationX;
    var y = me.locationY;
    var dirs = [];
    for (var i = 0; i < others.length; i++) {
        if (Math.abs(others[i][0] - x) <= 3 && Math.abs(others[i][0] - y) <= 3) {
            if (others[i][3] < me.coins && others[i][3] * 0.85 > 5) {
                if (Math.abs(others[i][0] - x) > Math.abs(others[i][1] - y)) {
                    if (others[i][0] - x < 0) {
                        dirs.push('west');
                    } else {
                        dirs.push('east');
                    }
                } else if (Math.abs(others[i][0] - x) < Math.abs(others[i][1] - y)) {
                    if (others[i][1] - y < 0) {
                        dirs.push('north');
                    } else {
                        dirs.push('south');
                    }
                } else {
                    if (Math.random() < 0.5) {
                        if (others[i][0] - x < 0) {
                            dirs.push('west');
                        } else {
                            dirs.push('east');
                        }
                    } else {
                        if (others[i][1] - y < 0) {
                            dirs.push('north');
                        } else {
                            dirs.push('south');
                        }
                    }
                }
            } else if (others[i][3] >= me.coins) {
                if (Math.abs(others[i][0] - x) < Math.abs(others[i][1] - y)) {
                    if (others[i][0] - x < 0) {
                        dirs.push('east');
                    } else {
                        dirs.push('west');
                    }
                } else if (Math.abs(others[i][0] - x) > Math.abs(others[i][1] - y)) {
                    if (others[i][1] - y < 0) {
                        dirs.push('south');
                    } else {
                        dirs.push('north');
                    }
                } else {
                    if (Math.random() < 0.5) {
                        if (others[i][0] - x < 0) {
                            dirs.push('east');
                        } else {
                            dirs.push('west');
                        }
                    } else {
                        if (others[i][1] - y < 0) {
                            dirs.push('south');
                        } else {
                            dirs.push('north');
                        }
                    }
                }
            }
        }
    }
    for (i = 1; i < 5; i++) {
        if (Math.abs(coins[i][0] - x) <= 5 && Math.abs(coins[i][1] - y) <= 5) {
            if (Math.abs(coins[i][0] - x) > Math.abs(coins[i][1] - y)) {
                if (coins[i][0] - x < 0) {
                    dirs.push('west');
                } else {
                    dirs.push('east');
                }
            } else if (Math.abs(coins[i][0] - x) < Math.abs(coins[i][1] - y)) {
                if (coins[i][1] - y < 0) {
                    dirs.push('north');
                } else {
                    dirs.push('south');
                }
            } else {
                if (Math.random() < 0.5) {
                    if (coins[i][0] - x < 0) {
                        dirs.push('west');
                    } else {
                        dirs.push('east');
                    }
                } else {
                    if (coins[i][1] - y < 0) {
                        dirs.push('north');
                    } else {
                        dirs.push('south');
                    }
                }
            }
        }
    }
    var fin = [];
    for (i = 0; i < dirs.length; i++) {
        if (dirs[i] == 'north' && y !== 0 && fin.indexOf('north') == -1) {
            fin.push('north');
        } else if (dirs[i] == 'east' && x != me.arenaLength - 1 && fin.indexOf('east') == -1) {
            fin.push('east');
        } else if (dirs[i] == 'west' && x !== 0 && fin.indexOf('west') == -1) {
            fin.push('west');
        } else if (dirs[i] == 'south' && y != me.arenaLength && fin.indexOf('south') == -1) {
            fin.push('south');
        }
    }
    if (fin.length > 1) {
        return fin[randInt(0, fin.length - 1)];
    } else if (fin.length == 1) {
        return fin[0];
    } else {
        return 'none';
    }
}

Seeker.Armageddon is a bot of few thoughts. He checks for nearby coins, and darts over to them. He carefully avoids the gold coins, since they attract big, bad Uberbots. He can run from bots, but not well. He also pursues smaller bots that will earn him profit.

Redwolf Programs

Posted 2018-08-08T14:06:30.647

Reputation: 2 561

0

Hunter.Armageddon

One last bot for now

    //Hunter.Armageddon: Designed to move carefully around, and hunt bots and coins. Made to win
    function(me, others, coins) {
        var x = me.locationX;
        var y = me.locationY;

        var enemies = [];
        var threats = [];
        var targets = [];
        var dirs = [];
        var secure = [true, true, true, true];

        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) => {
            for (var i = 0; i < check.length; i++) {
                if (distance(check[i], from) == 1) {
                    return true;
                }
            }
            return false;
        };
        var dirStr = (dir) => (['north','east','south','west'][dir]);

        var i, n, l, o;
        for (i = 0; i < others.length; i++) {
            o = others[i];
            if (o[2] >= me.coins) {
                enemies.push(o);
            } else {
                targets.push([o[0], o[1], Math.ceil(o[2] * 0.85), meTo(o), true]);
            }
        }
        for (i = 1; i < 5; i++) {
            o = coins[i];
            targets.push([o[0], o[1], 2, meTo(o), false]);
        }
        targets.push([coins[0][0], coins[0][1], 5, meTo([coins[0][0], coins[0][1]]), false]);
        targets.sort((a, b) => ((-(b[3]) + b[2]) - (-(a[3]) + a[2])));
        for (i = 0; i < enemies.length; i++) {
            o = enemies[i];
            if (inSquare(o, 3)) {
                threats.push(o);
            }
        }
        for (i = 0; i < targets.length; i++) {
            o = targets[i];
            if (o[5] && Math.random() < 0.35 && threats.length === 0) {
                return 'none';
            }
            if (o[0] == x && o[1] + 1 == y && !(attackRange(o, threats))) {
                return 'north';
            }
            if (o[0] == x && o[1] - 1 == y && !(attackRange(o, threats))) {
                return 'south';
            }
            if (o[0] + 1 == x && o[1] == y && !(attackRange(o, threats))) {
                return 'west';
            }
            if (o[0] - 1 == x && o[1] == y && !(attackRange(o, threats))) {
                return 'east';
            }
        }
        if (x === 0 || attackRange([x - 1, y], threats)) {
            secure[3] = false;
        }
        if (y === 0 || attackRange([x, y - 1], threats)) {
            secure[0] = false;
        }
        if (y == me.arenaLength - 1 || attackRange([x, y + 1], threats)) {
            secure[2] = false;
        }
        if (x == me.arenaLength - 1 || attackRange([x + 1, y], threats)) {
            secure[1] = false;
        }
        for (i = 0; i < targets.length; i++) {
            o = targets[i];
            if (o[0] == x) {
                if (o[1] < y) {
                    dirs.push(0);
                } else {
                    dirs.push(2);
                }
            } else if (o[1] == y) {
                if (o[0] < x) {
                    dirs.push(3);
                } else {
                    dirs.push(1);
                }
            } else if (Math.abs(o[0] - x) < Math.abs(o[1] - y)) {
                if (o[1] < y) {
                    dirs.push(0);
                } else {
                    dirs.push(2);
                }
                if (o[0] < x) {
                    dirs.push(3);
                } else {
                    dirs.push(1);
                }
            } else if (Math.abs(o[0] - x) > Math.abs(o[1] - y)) {
                if (o[0] < x) {
                    dirs.push(3);
                } else {
                    dirs.push(1);
                }
                if (o[1] < y) {
                    dirs.push(0);
                } else {
                    dirs.push(2);
                }
            }
        }
        dirs.push(0, 1, 2, 3);
        for (i = 0; i < dirs.length; i++) {
            o = dirs[i];
            if (secure[o]) {
                return dirStr(o);
            }
        }
        return 'none';
    }

A master warrior that seeks coins while avoiding enemies, and hunts victims for coins

Redwolf Programs

Posted 2018-08-08T14:06:30.647

Reputation: 2 561

Just updated him to be a winner – Redwolf Programs – 2018-08-14T18:01:45.753

0

CloseCoin

Conquerer of Battles

    function(me,others,coins){
        var x = me.locationX;
        var y = me.locationY;
        var pref = [];
        var safe = [true, true, true, true];
        var dist = (loc) => (Math.abs(loc[0] - x) + Math.abs(loc[1] - y));
        var inSquare = (loc, r) => (Math.abs(loc[0] - x) <= r && Math.abs(loc[1] - y) <= r);
        var b = (loc, X, Y) => (Math.abs(loc[0] - X) + Math.abs(loc[1] - Y));
        var a = (loc, v) => (Math.abs(loc - v));
        var i, n, o, p, t;
        for (i = 0; i < coins.length; i++) {
            o = coins[i];
            if (t === undefined || dist(o) <= dist(t)) {
                t = o;
            }
        }
        if (x == 0) {
            safe[3] = false;
        }
        if (y == 0) {
            safe[0] = false;
        }
        if (x == me.arenaLength - 1) {
            safe[1] = false;
        }
        if (y == me.arenaLength - 1) {
            safe[2] = false;
        }
        for (i = 0; i < others.length; i++) {
            o = others[i];
            if (o[2] >= me.coins) {
                if (b(o, x + 1, y) == 1) {
                    safe[1] = false;
                }
                if (b(o, x, y + 1) == 1) {
                    safe[2] = false;
                }
                if (b(o, x, y - 1) == 1) {
                    safe[0] = false;
                }
                if (b(o, x - 1, y) == 1) {
                    safe[3] = false;
                }
            } else {
                if (arrayEqual([x + 1, y], o.slice(0,1))) {
                    pref.push(1);
                }
                if (arrayEqual([x, y + 1], o.slice(0,1))) {
                    pref.push(2);
                }
                if (arrayEqual([x, y - 1], o.slice(0,1))) {
                    pref.push(0);
                }
                if (arrayEqual([x - 1, y], o.slice(0,1))) {
                    pref.push(3);
                }
                if (inSquare(o, 3) && Math.ceil(o[2] * 0.85) > 2) {
                    p = botNotes.getData("chase");
                    if (p) {
                        if (p < 4) {
                            t = o;
                            botNotes.storeData("chase", p + 1);
                        } else {
                            botNotes.storeData("chase", false);
                        }
                    }
                }
            }
        }
        if (x !== t[0]) {
            if (x < t[0]) {
                pref.push(1);
            } else {
                pref.push(3);
            }
            if (y < t[1]) {
                pref.push(2);
            } else if (y > t[1]) {
                pref.push(0);
            }
        } else if (y !== t[1]) {
            if (y < t[1]) {
                pref.push(2);
            } else if (y > t[1]) {
                pref.push(0);
            }
            if (x < t[0]) {
                pref.push(1);
            } else {
                pref.push(3);
            }
        }
        pref.push(0, 1, 2, 3);
        for (i = 0; i < 3; i++) {
            if (safe[pref[i]]) {
                return ['north','east','south','west'][pref[i]]
            }
        }
        return 'none';
    }

He will crush you under his boot, and then collect the coins that "Should have been yours"

Redwolf Programs

Posted 2018-08-08T14:06:30.647

Reputation: 2 561

0

BotsAreMoney

function(me,maybecoins,coins) {
    function dist(a,b){
        return Math.abs(a[0]-b[0])+Math.abs(a[1]-b[1]);
    }
    var mycoin = null, indanger = {n:false,s:false,e:false,w:false}, 
        notallowed = {n:false,s:false,e:false,w:false}, nearestcoin = [0,0,9999], 
        nearestmaybe = [0,0,9999], mydist, i, j;
    if(me.locationY == 0)                   notallowed.n = true;
    if(me.locationY == me.arenaLength-1)    notallowed.s = true;
    if(me.locationX == me.arenaLength-1)    notallowed.e = true;
    if(me.locationX == 0)                   notallowed.w = true;
    for(j=0;j<maybecoins.length;j++){
        if(maybecoins[j][2] > me.coins){
            if(maybecoins[j][0]-1 == me.locationX && maybecoins[j][1] == me.locationY){
                indanger.e = maybecoins[2];
            }
            else if(maybecoins[j][0]+1 == me.locationX && maybecoins[j][1] == me.locationY){
                indanger.w = maybecoins[2];
            }
            else if(maybecoins[j][0] == me.locationX && maybecoins[j][1]-1 == me.locationY){
                indanger.s = maybecoins[2];
            }
            else if(maybecoins[j][0] == me.locationX && maybecoins[j][1]+1 == me.locationY){
                indanger.n = maybecoins[2];
            }
        }
        else if(maybecoins[j][2] > 7 && maybecoins[j][2] < me.coins){
            mydist = dist([me.locationX,me.locationY],maybecoins[j]);
            maybecoins[j][3] = mydist;
            if(mydist < nearestmaybe[2]){
                nearestmaybe = [maybecoins[j][0],maybecoins[j][1],mydist];
            }
        }
    }
    for(i=0;i<coins.length && mycoin==null;i++){
        mycoin = coins[i];
        mydist = dist([me.locationX,me.locationY],coins[i]);
        if(mydist < nearestcoin[2]){
            nearestcoin = [mycoin[0],mycoin[1],mydist];
        }
        for(j=0;j<maybecoins.length;j++){
            if(dist(maybecoins[j],coins[i]) <= mydist){
                mycoin = null;
                break;
            }
        }
    }
    if(nearestcoin[2] < 9999 && nearestmaybe[2] < 9999 && nearestmaybe[2] < nearestcoin[2]-3){
        if(nearestmaybe[1] < me.locationY && !indanger.n && !notallowed.n)      return 'north';
        else if(nearestmaybe[1] > me.locationY && !indanger.s && !notallowed.s) return 'south';
        else if(nearestmaybe[0] > me.locationX && !indanger.e && !notallowed.e) return 'east';
        else if(nearestmaybe[0] < me.locationX && !indanger.w && !notallowed.w) return 'west';
    }
    if(mycoin != null){
        if(mycoin[1] < me.locationY)            return 'north';
        else if(mycoin[1] > me.locationY)       return 'south';
        else if(mycoin[0] > me.locationX)       return 'east';
        else if(mycoin[0] < me.locationX)       return 'west';
        else                                    return 'none';
    }
    if((indanger.n !== false || notallowed.n) && (indanger.s !== false || notallowed.s) && (indanger.e !== false || notallowed.e) && (indanger.w !== false || notallowed.w)){
        if(indanger.n >= indanger.s && indanger.n >= indanger.e && indanger.n >= indanger.w && !notallowed.n)   return 'north';
        if(indanger.s >= indanger.n && indanger.s >= indanger.e && indanger.s >= indanger.w && !notallowed.s)   return 'south';
        if(indanger.e >= indanger.s && indanger.e >= indanger.n && indanger.e >= indanger.w && !notallowed.e)   return 'east';
        if(indanger.w >= indanger.s && indanger.w >= indanger.e && indanger.w >= indanger.n && !notallowed.w)   return 'west';
    }
    else if((indanger.s || indanger.e || indanger.w) && indanger.n === false)       return 'north';
    else if((indanger.n || indanger.e || indanger.w) && indanger.s === false)       return 'south';
    else if((indanger.n || indanger.s || indanger.w) && indanger.e === false)       return 'east';
    else if((indanger.n || indanger.s || indanger.e) && indanger.w === false)       return 'west';
    if(notallowed.n)                            return 'south';
    if(notallowed.s)                            return 'north';
    if(notallowed.e)                            return 'west';
    if(notallowed.w)                            return 'east';
    if(nearestcoin[2] < 9999){
        if(nearestcoin[1] < me.locationY)       return 'north';
        else if(nearestcoin[1] > me.locationY)  return 'south';
        else if(nearestcoin[0] > me.locationX)  return 'east';
        else if(nearestcoin[0] < me.locationX)  return 'west';
        else                                    return 'none';
    }
    return 'none';
}

Would still like to tweak this a bit, but it'll have to do for now.

Logic (picks first possible):

  • Goes for nearby bots with valuable coins
  • Goes for coins that only he can get
  • Avoids bigger bots (attempts a phase-through if surrounded)
  • Avoids the edges
  • Heads for nearest coin

Jo.

Posted 2018-08-08T14:06:30.647

Reputation: 974