This challenge was inspired by @HelkaHomba's excellent challenge Red vs. Blue - Pixel Team Battlebots. That challenge was probably the best one I've seen on this site. Ever.

My challenge is still a lot different, but @HelkaHomba deserves credit for the inspiration.

Overview

This is a team where your team wins by having all players alive on your team. In other words, last team standing wins. Draws will be re-done.

You are on a board. You know your position on the first round (tick 0). You also know who is in the area around you:

A single red square in a 9x9 grid, surrounded by white cells.

In this case, you are all alone (or so you think) with no-one around you. You can see surrounding items in the first argument to your ontick handler. More about the API later.

Your team

Your team is determined by your user ID. To find that out, click on your profile picture:

My profile picture

Then find your user ID in the address bar:

It's between /users/ and /yourusername

If it is odd, you are on the blue team.

If it is even, you are on the red team.

You're welcome for the hand-drawn circles.

Your (bot's) name

Your bot's name starts with the first letter of your team ("r" or "b"). It must match the regex /^(r|b)[A-Za-z_-]$/. Other than that, you can pick your bot's name. Please don't use an already-existing one.

Starting

The red players will start near the top of the map, and the blue will start near the bottom. You are given special info on the first tick (turn) in the environment parameter for the ontick function. I recommend storing that. See the API for details.

On your turn

Turn order is randomized initially, but then stays the same.

Turn actions

You may only do one action per turn.

  • Move

    When you would like to move, you call this.move(num) in the API. num is the cell you would like to move to:

    0 is top left, 1 is top middle, 2 is top right, 3 is middle right, 4 is middle left, 5 is bottom left, 6 is bottom middle, and 7 is bottom right.

    The relative locations of the numbers you can move to are stored in the global constant threeByThree:

[
    [0, 1, 2],
    [3, undefined, 4],
    [5, 6, 7]
]

If you move into a wall, or another player, nothing happens.

  • Rotate

    To rotate, you call this.rotate(num). Num is the direction you want to rotate:

    0 is top, 1 is right, 2 is down, and 3 is left

    Rotation is absolute.

  • Kill

    If another player (from another team) is in the cell you are facing, you can call this.kill() and kill them. If there is no-one there, or they are on your team, this does nothing. Example:

    Same numbers as above, the 0 cell is green, the 1 is blue, the 2 is orange, and the 3 is yellow

    If you are turned to 0, you can kill green. If you are are turned to 1, you can kill blue. If you are are turned to 2, you can kill orange. If you are are turned to 3, you can kill yellow.

  • Bomb

    Bombing kills all players including you and teammates in the 9 squares around you. Example:

    There is a 9x9 grid with "x"s in each cell.

    Why would you ever want to do this? Kamikaze. If there are more players not on your team in the 9 cells around you then there are on your team, you might consider bombing. (I suggest you notify your comrades first!)

  • Place a landmine

    This creates a death square for others not on your team. When you place a land mine, you also move so you don't step on it. You call this.landMine(num) where num is the square you want to go to. Example:

    A single red square in a 9x9 grid, surrounded by white cells.

    Then you call this.landMine(4):

    [A 9x9 grid, with a red "M" in the middle and a red cell on the middle right.

    See that "M"? It's a landmine. Others can see it...for now. Anyone, even those not on your team, can see a landmine on the tick it is placed. But after that tick is over, no-one, not even you can see it. But it will explode as soon as an enemy walks over it. Example:

    Two 9x9 grids, a blue cell in the middle left in the first one, a red "M" in the middle of the first one, a red "x" in the middle of the second one, and  an arrow between them.

    Blue moved on your landmine, and BOOM! You just got another kill.

    For every 2 kills you get (from direct killing or land mines), you get 1 extra landmine to place. You also get one at the start.

  • Dig

    When you dig, you look for landmines in a 5x5 area centered around you. This does not show the team of the bot that placed the landmine. (Remember that you can't be killed by a landmine placed by someone on your team.) For example, if this was the grid around you:

    Then the return value of this.dig() would be:

[undefined,undefined,undefined,true,undefined,
undefined,undefined,undefined,undefined,undefined,
undefined,undefined,undefined,undefined,
undefined,undefined,true,undefined,undefined,
true,undefined,undefined,undefined,undefined]

The array indexes are from starting in the top left, going right, than down, not including yourself:

There are 23 in total, and their relative locations are stored in the global constant fiveByFive:

[
    [0, 1, 2, 3, 4],
    [5, 6, 7, 8, 9],
    [10, 11, undefined, 12, 13],
    [14, 15, 16, 17, 18],
    [19, 20, 21, 22, 23]
]

Note that dig reveals mines placed on previous ticks, unlike aroundMe.

Communication

When you want to talk to someone, you call this.sendMessage(recipients, team, data). The data can be anything you want, and you can send it to anyone you want, even players on other teams. This can be used to trick poorly-programmed bots, but all players can see who sent a message and who's team they are on.

Example:

Send something to a bot named "redisbest":

this.sendMessage("redisbest", undefined, "Hi!");

Send something to a bot named "redisbest" and "blueiscool":

this.sendMessage(["redisbest", "blueiscool"], undefined, {hello: "there"});

Send something to the entire red team

this.sendMessage(undefined, "red", {hello: "red"});

Send something to everyone

this.sendMessage(undefined, "*", {hello: "everyone"});

Send something to the entire red team and a bot named "blueiscool":

this.sendMessage("blueiscool", "red", {hello: "bots"});

API

Your code must consist of a single call to the createBot function. Nothing else. Sample code:

createBot({
    ontick: function(environment) {
        return new Promise((resolve, reject)=>{
            this.move(0);//example
            resolve();//please call this when you are done
        });
    },
    onmessage: function(data, from, fromBot) {
        console.log("onMessage: " + this.name + " from " + this.team + " got message ", data, " from " + from + ", on team " + fromTeam);
        this.sendMessage(["bot", "otherbot"], "team", "some data");
    },
    team: "red",//your team
    name: "rmyteamname",//team name must begin with the first letter of your team's name
    onkill: function(){
        //say goodbye
    }
});

(You are free to copy-paste this. Just modify it for your team, etc.)

Methods

  • ontick(environment)

    Called when it's your turn. Must return a Promise that resolves in 1 second or less or it will be ignored. This is for performance reasons and has the nice side-effect of not having the tab hang.

    this (when in ontick)

    • landMines How many land mines you have left. The more kills you have, the more land mines you get. For every 2 bots you kill, you get 1 more landmine. You also get 1 to start.
    • direction The direction you are facing.
    • storage Storage that persists between calls to onTick and onMessage. An empty object at start. Modify for any purpose, but make sure it's an array or object always to ensure it persists correctly.
    • move(num) Move to the specified position. Does nothing if invalid. See above for details.
    • rotate(num) Rotate to the specified position. Does nothing if invalid. See above for details.
    • kill() Kills the player you are facing, if exists and is not on your team. See above for details.
    • bomb() Kills anyone in the 9 squares around you, including yourself.
    • landMine(num) Places a land mine where you are, and then moves to the specified position. Does nothing if invalid num or you have none left. See above for details.
    • dig() new! Returns an array of info about the landmines in a 5x5 area centered around you. See above for details.
    • sendMessage(recipients, team, data) recipients can either be a single bot (string), an array of bot, or undefined/null. It is who you would like to send the message. team is a string of the team you would like to send the message. Use "*" to send a message to everyone. data is anything that can be passed to a JS function. It is send to the recipients. If it is an object or array, it is passed by reference, so you and the recipient(s) can save that to their storage and any modifications to the object affects both bot's copies. Note that recipients that are in either the list of bots, the exact bot specified in the string, or a bot on the team you specified, it will get the message.

environment

On the first tick

  • x: Your player's x-position
  • y: Your player's y-position
  • gridWidth: The width of the grid (in cells)
  • gridHeight: The height of the grid (in cells)

    On all ticks

  • aroundMe: An array of players and landmines. The players are objects that look like {name: "bot name", team: "bot team"}, and the landmines are {team: "team of bot who placed mine"}. The indexes of the array:

    0 is top left, 1 is top middle, 2 is top right, 3 is middle right, 4 is middle left, 5 is bottom left, 6 is bottom middle, and 7 is bottom right.

    Note that landmines placed on a tick other than the current one will not be shown.

    aroundMe example:

    Let's say this is the grid (you are red):

    A 9x9 grid, with light blue in the upper left, a grey "M" in the upper right, red in the middle, yellow in the middle left, and a red "M" in the bottom left.

    Your aroundMe will look like this:

[
    {name: "bexamplebluebot", team: "blue"},
    undefined,//sparse array, nothing in index 1
    undefined,//there is technically a landmine here, but it wasn't placed this tick, so it is not shown
    undefined,//nothing in 3
    {name: "yexampleyellowbot", team: "yellow"},
    {team: "red"},//this is a landmine, you can tell is not a bot because it has no name. mines have the team name of the player they were placed by. This mine was placed this tick, otherwise you couldn't see it
    //nothing else after index 5, so the array's length is 5.
]

The indexes of the array are explained here:

0 is top left, 1 is top middle, 2 is top right, 3 is middle right, 4 is middle left, 5 is bottom left, 6 is bottom middle, and 7 is bottom right.

Your bot effectively sees this:

A light blue box in the upper left with a black number 0 in it, a yellow box on the left edge with a black number 4 in it, and a red "M" in the bottom-left with a black 5 on it.

  • onmessage(data, fromBot, fromTeam)

    this (when in onmessage)

    • sendMessage(recipients, team, data) Standard message sending function.
    • storage Standard storage.

    data The data sent from the sender. fromPlayer The player the message was sent from. fromTeam The team the message was sent from.

  • onkill()

    this (when in onkill)

    • sendMessage(recipients, team, data) Standard message sending function.

Convenient (constant) global arrays:

threeByThree:

[
    [0, 1, 2],
    [3, undefined, 4],
    [5, 6, 7]
]

Useful for passing data to the move function as well as interpreting aroundMe. See above.

fiveByFive :

[
    [0, 1, 2, 3, 4],
    [5, 6, 7, 8, 9],
    [10, 11, undefined, 12, 13],
    [14, 15, 16, 17, 18],
    [19, 20, 21, 22, 23]
]

Useful for the this.dig() function in the ontick handler.

Try it out!

The controller will be run from my machine on localhost for performance reasons, but you can use the CodePen to test your bot out.

Note that you must paste your code in the console and press Enter before you click run. You can paste in as many bots as you'd like. The "Test bots" are examples for you to test against. If you can beat or tie all of them, you have at least a decent bot.

Submissions

Rules

Rules (enforced by the controller)

  • Your main ontick code must not take more than 1 second. We don't want rounds to take forever. If your code takes > 1 second, it will be stopped.
  • If you attempt to do more than 1 action per turn, or do an invalid action (eg. this.move(-1) or moving into a wall), it will be ignored.
  • More may come soon...

Rules (enforced by me, can result in DQ)

  • Don't write global variables (reading is fine).
  • Your code must work in Nodejs (in case the controller is ported to Nodejs), so JSON.parse(...) is fine, but alert() is not.
  • You are not allowed to call createBot or interfere with the controller in any way.
  • Don't use someone's else's code without permission and significant changes. No copybots.
  • Please, no loopholes!
  • More may come soon...

My bots

Here are some bots:

This bot randomly chooses an action. Well, it's a weighted random, but still pretty random. If you can kill this bot (it will eventually kill itself, that doesn't count), than you have at least a decent bot. Post it and see what happens!

createBot({
  ontick: function(environment) {
    return new Promise((resolve, reject)=>{
      if(Math.random() < 0.02){
        this.bomb();
      }else if(Math.random() < 0.1){
        this.kill();
      }else if(Math.random() < 0.3){
        this.rotate(~~(Math.random() * 4));
      }else{
        this.move(~~(Math.random() * 8));
      }
      resolve();
    });
  },
  team: "none",
  name: "xrandomness2",
  onkill: function(){
    console.log("Goodbye world... :(");
  }
});

My bots have a name beginning with "x" and a team of "none". You are welcome to use some of this code, but please do at least some modification. If you can't be bothered to at least tweak a number, you won't win.

Formatting your submission

Please use this format:

# rmyamazingbot

    createBot({
        ontick: function(environment) {
            return new Promise((resolve, reject)=>{
                this.move(0);//example
                resolve();//please call this when you are done
            });
        },
        onmessage: function(data, fromTeam, fromBot) {
            console.log("onMessage: " + this.name + " from " + this.team + " got message ", data, " from " + from + ", on team " + fromTeam);
            this.sendMessage(["bot", "otherbot"], "team", "some data");
        },
        team: "red",//your team
        name: "rmyteamname",//team name must begin with the first letter of your team's name
        onkill: function(){
            //say goodbye
        }
    });

Long, but cool explanation...

Feature requests, bugs, questions, etc?

Comment below! Please check to see if there is already a comment with that. If there is one already, upvote it.

Want to talk to your team?

Use the chat rooms for red and blue.

Language

Currently, only JS and something that compiles to JS is supported, but if you know of a way to get other languages to work with Nodejs I would be happy to port the controller to Nodejs.

Final notes

Strategy ideas

Help your team! Creating a bot that is designed to help another bot and work together. This strategy worked well for Red vs. Blue - Pixel Team Battlebots

Rep seekers

I will accept the highest voted answer on the winning team. Keep in mind that earlier answers tend to get more votes, but their weaknesses are more likely to be found and exploited.

Also, if you answer soon, you might get the +100 bounty.

  • 1
    Comments are not for extended discussion; this conversation has been moved to chat. – Dennis Apr 27 '17 at 19:51
  • May I make more than one bots? (sorry,I know the conversation has been moved, I'm just chatbanned so yeah) – lol May 4 '17 at 9:10
  • @SIGSEGV yes, but someone else must post it. You could post one bot and give the code of another to someone on your team, but you can't post twice. – programmer5000 May 4 '17 at 10:43
  • About positioning, where is the [0, 0]-indexed cell, is it the top-left cell? Also, does messaging consume your action (per turn)? Thanks. – Thrax May 10 '17 at 9:56
  • @Thrax yes, and no. You can even message in response to an message. – programmer5000 May 10 '17 at 11:35

xscared (non-competing)

createBot({
    ontick: function(environment) {
        var reverse = [0, 1, 2, 3, 4, 5, 6, 7].reverse();
        return new Promise((resolve, reject)=>{
            (this.aroundMe || []).forEach((item,idx)=>{
                this.move(reverse[idx]);
                return resolve();
            });
            this.move(~~(Math.random() * 8));
            return resolve();
        });
    },
    onmessage: function() {
    },
    team: "none",
    name: "xscared",
    onkill: function(){
    }
});

Very scared of people. Goes away from the first person (or landmine) it sees. Otherwise, it moves randomly. Note that this is not competing, just an example. Try to beat it!

backup, a blue bot

As warned in chat, I never wrote anything in javascript in my life, so if you find any mistake, please tell me! (Thanks to @programmer5000 for already helping me with it)
The concept of this bot is that it communicates with other bots from the same team and send them its position along with a map of the mines it found. It tries to join the closest blue bot (if one send its position data [given as an [x,y] array]), and stays near it (with its back to it as much as possible), killing approaching red bots or looking ahead for mines.

createBot({
    team: 'blue',
    name: 'backup',
    ontick: function(environment) {
        return new Promise((resolve, reject) => {
            //if (typeof this.x != "undefined") this.storage['position'] = [this.x, this.y];
            if (typeof environment.x != "undefined") this.storage['position'] = [environment.x, environment.y]; //Modified according to @WasteD
            if (typeof this.storage['map'] == "undefined") { //Create empty map
                var map = [[]];
                //for(i=0;i<this.gridHeight;i++) map[i]=[];
                for(i=0;i<environment.gridHeight;i++) map[i]=[]; //Modified according to @WasteD
                this.storage['map'] = map;
            }
            var blue = []
            var red = []
            var x = this.storage['position'][0];
            var y = this.storage['position'][1];
            var dx = [-1, 0, 1, -1, 0, 1, -1, 0, 1]
            var dy = [1, 1, 1, 0, 0, 0, -1, -1, -1]
            (this.aroundMe || []).forEach((item, idx) => { // Update map and list positions of surrounding blues and reds
                if (item && item.team == 'red' && typeof item.name != "undefined") red += idx;
                if (item && item.team == 'red' && typeof item.name == "undefined") this.storage['map'][x+dx[idx]][y+dy[idx]] = 'M';
                if (item && item.team == 'blue' && typeof item.name != "undefined") blue += idx;
            });
            this.sendMessage(undefined, "blue", {"position": this.storage['position'], 'map': this.storage['map']}); //Send to buddies my position and the map
            if (red.indexOf([1, 4, 6, 3][this.direction]) > -1) this.kill() ; //If red guy is in front of
            else if (red.indexOf([1,4,6,3]) > -1) this.rotate(red.indexOf([1,4,6,3])); //If red guy is next but not in front of
            else if (blue.indexOf(3) > -1){ //If blue buddy on the left
                if (blue.indexOf(4) > -1){ //If another one is on the right
                    if (blue.indexOf(1) > -1 && this.direction != 2) this.rotate(2); //...and a third one at the top
                    else var digging = this.dig();
                    }
                else if (this.direction != 1) this.rotate(1);
                else var digging = this.dig();
            }
            else if (blue.indexOf(1) > -1){
                if (blue.indexOf(6) > -1 && this.direction != 3) this.rotate(3);
                else if (this.direction != 2) this.rotate(2);
                else var digging = this.dig();
            }
            else if (blue.indexOf(4) > -1){
                if (this.direction != 3) this.rotate(3);
                else var digging = this.dig();
            }
            else if (blue.indexOf(6) > -1 && this.direction != 0) this.rotate(0);
            else if (blue.indexOf([0,2]) > -1){ //If no blue next to me but one in diagonal, move next
                this.move(1);
                this.storage['position'][1] = y+1; //Update position
            }
            else if (blue.indexOf([5,7]) > -1){
                this.move(6);
                this.storage['position'][1] = y-1;
            }
            else if (typeof this.storage['other_blue'] != "undefined"){ //Check if buddies said where they were, try to go near the closest one
                var dmin = 99999;
                var pos = []
                (this.storage['other_blue'] || {}).forEach((item, idx) => {
                    var d = Math.sqrt(Math.pow(item['position'][0]-x,2) + Math.pow(item['position'][1]-y,2));
                    if (d < dmin){
                        dmin = d;
                        pos = item['position'];
                        }
                });
                if (pos[0]-x > 0){
                    this.move(4);
                    this.storage['position'][0] = x+1
                }
                else if (pos[0] < 0){
                    this.move(3);
                    this.storage['position'][0] = x-1
                }
                else if (pos[1] > 0){
                    this.move(1);
                    this.storage['position'][1] = y+1
                }
                else{
                    this.move(6);
                    this.storage['position'][1] = y-1
                }
            }
            else var digging = this.dig();
            if (typeof digging != "undefined"){ //Check out surroundings if dig() was played and update the map accordingly
                var dx2 = [-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2];
                var dy2 = [2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2];
                (digging || []).forEach((item, idx) => {
                    //if (item && item.team == 'red' && typeof item.name == "undefined") this.storage['map'][x+dx2[idx]][y+dy2[idx]] = 'M';
                    if (item) this.storage['map'][x+dx2[idx]][y+dy2[idx]] = 'M'; //previously misread what dig() returned
                });
            }
            resolve();
        });
    },
    onmessage: function(data, fromTeam, fromBot) {
        if (typeof data['position'] != "undefined" && fromTeam == 'blue') { //If position sent by a blue bot
            if (typeof this.storage['other_blue'] == "undefined") this.storage['other_blue'] = [];
            for (i in this.storage['other_blue']){
                var found = false;
                if ('name' in i){
                    if (i['name'] == fromBot){
                        i['position'] = data['position'];
                        found = true; //Update if position already known from previous ticks
                        }
                }
            }
            if (!found) this.storage['other_blue'] += {'position':data['position'], 'name':fromBot}; //Add position if previously unknown
            this.sendMessage(fromBot, undefined, "roger.");
        }
    },
    onkill: function() {this.sendMessage(undefined, "blue", {"position": this.storage['position'], 'map': this.storage['map']});}
});
  • Hey You care if I also enter this but (with different name) I am also on blue – Christopher Apr 28 '17 at 20:41
  • @Christopher No i don't mind, but that would be a bit more interesting for you and for the team if you make one at least a bit different (at least to complement the 2 bots that already exists). – plannapus Apr 29 '17 at 11:46
  • Will do that. I will change it up – Christopher Apr 29 '17 at 13:05
  • If I try to run your bot in codepen it doesn't work because you are using this.x and so on but it is environment.x or am I wrong? – WasteD May 1 '17 at 14:17
  • @WasteD as I said I don't know Javascript at all, so it's possible. But if it's the case then I guess it should also be environment.gridHeight and environment.aroundMe? In which case then the other bots shouldn't work either since they use this.aroundMe. – plannapus May 2 '17 at 7:14

Blue, blue, my world is blue

createBot({
    team: 'blue',
    name: 'blue-blue-my-world-is-blue',
    ontick: function(environment) {
        return new Promise((resolve, reject) => {
            var red = 0;
            // See who's around me
            (this.aroundMe || []).forEach((item, idx) => {
                if (item && item.team == 'red') red++;
            });
            // If surrounded, take one for the team
            if (red >= 6) this.bomb();
            else {
                // Translate direction into position
                var kill = [1, 4, 6, 3][this.direction];
                // Random values
                var move = Math.floor(Math.random() * 8);
                var nsew = Math.floor(Math.random() * 4);
                // Lay a landmine if possible
                if (this.landMines) this.landMine(move);
                // Kill if someone is in the way
                else if (this.aroundMe && this.aroundMe[kill] && this.aroundMe[kill].team == 'red' && this.aroundMe[kill].name) this.kill();
                else {
                    // Move somewhere if already in the requested direction
                    if (nsew == this.direction) this.move(move);
                    // Otherwise just rotate to the requested direction
                    else this.rotate(nsew);
                }
            }
            resolve();
        });
    },
    onmessage: function(data, from, fromBot) {},
    onkill: function() {}
});

Mostly random, but will bomb if surrounded, and favors checking around and killing over moving.

  • Smart! Nice one. – programmer5000 Apr 25 '17 at 19:25
  • 3
    Yo listen up, here is a story, about a little guy who lives in a blue world. – lol May 2 '17 at 11:11

Relaxed-Bomber

This bot searches for a spot with at least 1 free cell on each side then plants a mine. It camps on it until an enemy approaches. When someone draws near, he'll step back back and forth on his mine to bait the other bot on it. He'll also rotate and kill if needed. When he doesn't have any mine left, he'll seek refuge on the top-left corner with its back to the wall and retaliate if he's threatened.

No special teamplay here, apart from broadcasting his position to his team with self keyword.

createBot({
    team: 'red',
    name: 'relaxed-bomber',
    ontick: function(environment) {
        return new Promise((resolve, reject) => {
            if (typeof this.storage['dropped'] == "undefined") {
                this.storage['dropped'] = false;
                this.storage['covered'] = false;
                this.storage['baited'] = false;
            }
            if (typeof environment.x != "undefined" && typeof environment.y != "undefined") {
                this.storage['pos'] = [environment.x, environment.y];
            }
            if (typeof environment.gridWidth != "undefined" && typeof environment.gridHeight != "undefined") {
                this.storage['grid'] = [environment.gridWidth, environment.gridHeight];
            }
            var x = this.storage['pos'][0];
            var y = this.storage['pos'][1];
            var x0 = this.storage['grid'][0];
            var y0 = this.storage['grid'][1];
            var source = [1, 4, 6, 3];
            var dest = [6, 3, 1, 4];
            var rot = [0, 1, 2, 3];
            var movex = [-1, 0, 1, -1, 1, -1, 0, 1];
            var movey = [-1, -1, -1, 0, 0, 1, 1, 1];
            var action = false;
            if (this.landMines > 0) { 
                var move = [false, false, false, false];
                var moveIndex = -1;
                if (x <= 0) { move[1] = true; }
                if (x >= x0 - 1) { move[3] = true; }
                if (y <= 0) { move[2] = true; }
                if (y >= y0 - 1) { move[0] = true; }    
                if (move[0] && !move[1] && !move[2] && move[3]) { moveIndex = 0; }
                if (move[0] && !move[1] && !move[2] && !move[3]) { moveIndex = 1; }
                if (move[0] && move[1] && !move[2] && !move[3]) { moveIndex = 2; }
                if (!move[0] && !move[1] && !move[2] && move[3]) { moveIndex = 3; }
                if (!move[0] && move[1] && !move[2] && !move[3]) { moveIndex = 4; }
                if (!move[0] && !move[1] && move[2] && move[3]) { moveIndex = 5; }
                if (!move[0] && !move[1] && move[2] && !move[3]) { moveIndex = 6; }
                if (!move[0] && move[1] && move[2] && !move[3]) { moveIndex = 7; }  
                if (moveIndex >= 0) {
                    this.storage['pos'] = [ x + movex[moveIndex], y + movey[moveIndex]];
                    this.move(moveIndex);
                } else {
                    this.storage['dropped'] = true;
                    this.storage['covered'] = false;
                    this.landMine(1);
                }
            } else {
                if (this.storage['dropped']) {
                    this.storage['dropped'] = false;
                    this.storage['covered'] = true;
                    this.storage['pos'] = [ x + movex[6], y + movey[6]];
                    this.move(6);
                } else if (this.storage['covered']) {
                    for (var i = 0; i < source.length; i++) {
                        if (typeof environment.aroundMe[source[i]] != "undefined" && typeof environment.aroundMe[source[i]].team != "undefined" && environment.aroundMe[source[i]].team == "blue" && typeof environment.aroundMe[source[i]].name != "undefined") {
                            this.storage['covered'] = false;
                            this.storage['baited'] = true;
                            this.storage['mine'] = this.storage['pos'].slice();
                            this.storage['reverse'] = source[dest[i]];
                            this.storage['pos'] = [ x + movex[dest[i]], y + movey[dest[i]]];
                            this.move(dest[i]);
                            action = true;
                        }
                    }
                    if (!action) {
                        this.dig();
                    }
                } else if (this.storage['baited']) {
                    for (var i = 0; i < source.length; i++) {
                        if (typeof environment.aroundMe[source[i]] != "undefined" && typeof environment.aroundMe[source[i]].team != "undefined" && environment.aroundMe[source[i]].team == "blue" && typeof environment.aroundMe[source[i]].name != "undefined") {
                            if (this.direction == rot[source[i]]) {
                                this.kill();
                                this.storage['baited'] = false;
                                action = true;
                            } else {
                                this.rotate(rot[source[i]]);
                                action = true;
                            }
                        }
                    }
                    if (!action) {
                        if (this.storage['mine'][0] == this.storage['pos'][0] && this.storage['mine'][1] == this.storage['pos'][1]) {
                            this.storage['pos'] = [ x + movex[this.storage['reverse']], y + movey[this.storage['reverse']]];
                            this.move(this.storage['reverse']);
                            this.storage['reverse'] = source[this.storage['reverse']];
                        } else {
                            this.storage['pos'] = [ x + movex[this.storage['reverse']], y + movey[this.storage['reverse']]];
                            this.move(this.storage['reverse']);
                            this.storage['reverse'] = dest[this.storage['reverse']];
                        }
                    }
                } else {
                    for (var i = 0; i < source.length; i++) {
                        if (typeof environment.aroundMe[source[i]] != "undefined" && typeof environment.aroundMe[source[i]].team != "undefined" && environment.aroundMe[source[i]].team == "blue" && typeof environment.aroundMe[source[i]].name != "undefined") {
                            if (this.direction == rot[source[i]]) {
                                this.kill();
                                this.storage['baited'] = false;
                                action = true;
                            } else {
                                this.rotate(rot[source[i]]);
                                action = true;
                            }
                        }
                    }
                    if (!action) {
                        if (x > 0 && y > 0) {
                            this.storage['pos'] = [ x + movex[0], y + movey[0]];
                            this.move(0);
                        } else if (x > 0 && y == 0) {
                            this.storage['pos'] = [ x + movex[3], y + movey[3]];
                            this.move(3);
                        } else if (x == 0 && y > 0) {
                            this.storage['pos'] = [ x + movex[1], y + movey[1]];
                            this.move(1);
                        } else {
                            this.rotate(1);
                        }
                    }
                }
            }
            this.sendMessage(undefined, "red", {'self': this.storage['pos'] });
            resolve();
        });
    },
    onmessage: function(data, fromTeam, fromBot) {},
    onkill: function() {}
});
  • What team are you on? – programmer5000 May 10 '17 at 14:49
  • @programmer5000 Since the bots' names have to begin with the letter of the team, I guess I'm Team Red :) – Thrax May 10 '17 at 14:50
  • Nice bot! I suggest you brodcast what's around you to your team, too. – programmer5000 May 10 '17 at 14:51

Backup 1 another blue bot (forgot to do this earlier)

createBot({
    team: 'blue',
    name: 'backup1',
    ontick: function(environment) {
        return new Promise((resolve, reject) => {
            //if (typeof this.x != "undefined") this.storage['position'] = [this.x, this.y];
            if (typeof environment.x != "undefined") this.storage['position'] = [environment.x, environment.y]; //Modified according to @WasteD
            if (typeof this.storage['map'] == "undefined") { //Create empty map
                var map = [[]];
                //for(i=0;i<this.gridHeight;i++) map[i]=[];
                for(i=0;i<environment.gridHeight;i++) map[i]=[]; //Modified according to @WasteD
                this.storage['map'] = map;
            }
            var blue = []
            var red = []
            var x = this.storage['position'][0];
            var y = this.storage['position'][1];
            var dx = [-1, 0, 1, -1, 0, 1, -1, 0, 1]
            var dy = [1, 1, 1, 0, 0, 0, -1, -1, -1]
            (this.aroundMe || []).forEach((item, idx) => { // Update map and list positions of surrounding blues and reds
                if (item && item.team == 'red' && typeof item.name != "undefined") red += idx;
                if (item && item.team == 'red' && typeof item.name == "undefined") this.storage['map'][x+dx[idx]][y+dy[idx]] = 'M';
                if (item && item.team == 'blue' && typeof item.name != "undefined") blue += idx;
            });
            this.sendMessage(undefined, "blue", {"position": this.storage['position'], 'map': this.storage['map']}); //Send to buddies my position and the map
            if (red.indexOf([1, 4, 6, 3][this.direction]) > -1) this.kill() ; //If red guy is in front of
            else if (red.indexOf([1,4,6,3]) > -1) this.rotate(red.indexOf([1,4,6,3])); //If red guy is next but not in front of
            else if (blue.indexOf(3) > -1){ //If blue buddy on the left
                if (blue.indexOf(4) > -1){ //If another one is on the right
                    if (blue.indexOf(1) > -1 && this.direction != 2) this.rotate(2); //...and a third one at the top
                    else var digging = this.dig();
                    }
                else if (this.direction != 1) this.rotate(1);
                else var digging = this.dig();
            }
            else if (blue.indexOf(1) > -1){
                if (blue.indexOf(6) > -1 && this.direction != 3) this.rotate(3);
                else if (this.direction != 2) this.rotate(2);
                else var digging = this.dig();
            }
            else if (blue.indexOf(4) > -1){
                if (this.direction != 3) this.rotate(3);
                else var digging = this.dig();
            }
            else if (blue.indexOf(6) > -1 && this.direction != 0) this.rotate(0);
            else if (blue.indexOf([0,2]) > -1){ //If no blue next to me but one in diagonal, move next
                this.move(1);
                this.storage['position'][1] = y+1; //Update position
            }
            else if (blue.indexOf([5,7]) > -1){
                this.move(6);
                this.storage['position'][1] = y-1;
            }
            else if (typeof this.storage['other_blue'] != "undefined"){ //Check if buddies said where they were, try to go near the closest one
                var dmin = 99999;
                var pos = []
                (this.storage['other_blue'] || {}).forEach((item, idx) => {
                    var d = Math.sqrt(Math.pow(item['position'][0]-x,2) + Math.pow(item['position'][1]-y,2));
                    if (d < dmin){
                        dmin = d;
                        pos = item['position'];
                        }
                });
                if (pos[0]-x > 0){
                    this.move(4);
                    this.storage['position'][0] = x+1
                }
                else if (pos[0] < 0){
                    this.move(3);
                    this.storage['position'][0] = x-1
                }
                else if (pos[1] > 0){
                    this.move(1);
                    this.storage['position'][1] = y+1
                }
                else{
                    this.move(6);
                    this.storage['position'][1] = y-1
                }
            }
            else var digging = this.dig();
            if (typeof digging != "undefined"){ //Check out surroundings if dig() was played and update the map accordingly
                var dx2 = [-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2];
                var dy2 = [2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2];
                (digging || []).forEach((item, idx) => {
                    //if (item && item.team == 'red' && typeof item.name == "undefined") this.storage['map'][x+dx2[idx]][y+dy2[idx]] = 'M';
                    if (item) this.storage['map'][x+dx2[idx]][y+dy2[idx]] = 'M'; //previously misread what dig() returned
                });
            }
            resolve();
        });
    },
    onmessage: function(data, fromTeam, fromBot) {
        if (typeof data['position'] != "undefined" && fromTeam == 'blue') { //If position sent by a blue bot
            if (typeof this.storage['other_blue'] == "undefined") this.storage['other_blue'] = [];
            for (i in this.storage['other_blue']){
                var found = false;
                if ('name' in i){
                    if (i['name'] == fromBot){
                        i['position'] = data['position'];
                        found = true; //Update if position already known from previous ticks
                        }
                }
            }
            if (!found) this.storage['other_blue'] += {'position':data['position'], 'name':fromBot}; //Add position if previously unknown
            this.sendMessage(fromBot, undefined, "roger.");
        }
    },
    onkill: function() {this.sendMessage(undefined, "blue", {"position": this.storage['position'], 'map': this.storage['map']});}
});

Blue Fighter

createBot({
  team: "blue",
  name: "blue-fighter",
  ontick: function(environment) {
    return new Promise((resolve, reject)=>{
      let map = environment.aroundMe;
      let sides = [1, 4, 6, 3];
      let facing = sides[this.direction];
      let isTeam = (team,a) => a && a.team === team;
      let isRed = (a)=>isTeam("red",a);
      let isBlue = (a)=>isTeam("blue",a);
      let randomSquare = ()=>Math.floor(Math.random()*8);
      let redNum = map.filter(isRed).length;
      let blueNum =  map.filter(isBlue).length;
      if(redNum > blueNum && redNum > 2){
        this.bomb();
      }else if(isRed(map[facing])){
        this.kill();
      }else if(sides.includes(map.findIndex(isRed))){
        this.rotate(sides.indexOf(map.findIndex(isRed)));
      }else if(Math.random() < 0.5 && this.landMines > 0){
        this.landMine(randomSquare());
      }else{            
        this.move(randomSquare());
      }
      resolve();
    });
  },
  onmessage: function(data, from, fromBot) {},
  onkill: function(){}
});

Blue fighter moves and landmines randomly and rotates toward red players. If the surrounding blocks have more red than blue, it bombs. If it is facing a red player, it kills it.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.