27
4
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 king-of-the-hill 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:
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:
Then find your user ID in the address bar:
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: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: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: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:
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:Then you call
this.landMine(4)
: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:
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 toonTick
andonMessage
. 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 invalidnum
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, orundefined
/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 theirstorage
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-positiony
: Your player's y-positiongridWidth
: 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: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):
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:
Your bot effectively sees this:
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, butalert()
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 – 2017-04-27T19:51:05.937May I make more than one bots? (sorry,I know the conversation has been moved, I'm just chatbanned so yeah) – Matthew Roh – 2017-05-04T09:10:36.460
@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 – 2017-05-04T10:43:44.057
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 – 2017-05-10T09:56:00.410
@Thrax yes, and no. You can even message in response to an message. – programmer5000 – 2017-05-10T11:35:07.377
I think there's a problem with the command
move(0);
on your test site, since even your sample bot won't move with this command only. The other values seem fine, though. – Thrax – 2017-05-10T13:00:57.783@Thrax that moves up and to the left, which would be in the wall if you started in the upper-left. Is it a problem when you are in the middle of the grid? – programmer5000 – 2017-05-10T13:22:45.240
I started at the bottom right (blue-team), and the bot wouldn't move. I then tried with 1 and 3 and both worked. – Thrax – 2017-05-10T13:28:41.803
What happens if i choose to explode to kill an opponent and, in the same round, he decide to move away from me? – Alix Eisenhardt – 2017-07-13T15:29:53.137
@AlixEisenhardt 1: Which ever happens first. "Turn order is randomized initially but then stays the same". If you happen to go first, the opponent will die before taking a turn. Otherwise, you die without taking a turn. – programmer5000 – 2017-07-15T18:44:09.907