king-of-the-hill game hexagonal-grid random javascript
Formic Functions 2: Hierarchies
Watch live | Active answers | Add new answer | Chat room | Source code | Leaderboard
This is an open-ended challenge. New answers and updates are always welcome.
We've seen the territorial highland ants wander the lands in search of food. There have also been talks of aggressive forest ants lurking down below whilst feasting on fungi. These ants, however, are unlike any you've seen before. Collecting food is their goal, as always, but colonies don't need a queen - all ants of this subfamily are capable of creating offspring. There's a catch, though. New ants have less strength than their parent, unless the parent ant spends a large amount of food on them. Ants are capable of killing others weaker than them, and weaker ants are worth less, so your ants need to make careful choices.
Because this competition is made in JavaScript, you can see what other players have made already directly in your browser. Just click on one of the links above - you'll figure out the rest.
To keep the comment section clean, I highly encourage you to use the dedicated chat room for questions and discussion.
No official tournaments available yet.
Screenshots
Here are a couple of images taken at the end of a game that should entice you:
No screenshots available yet.
You can look at more simply by running games in your browser. The controller also allows you to zoom in so you can track the ants' decisions that result in the large-scale patterns you ultimately see.
Definitions
A value range is indicated by a number followed by two dots and another number. "0..7" means "between 0 and 7, inclusive". A different type of range is indicated by a number followed by a plus sign. "1024+" means "greater than or equal to 1024".
Programming
Your task is to provide a JavaScript class that implements a getAction(view)
function and optionally a constructor. All code provided by you will be run under strict mode.
The constructor is called with no arguments. It must take no more than one second to run. It must be side-effect free and must consistently result in an identical object after every run.
The getAction(view)
function (henceforth known as the ant function) is called like this: yourConstructedObject.getAction(someViewArray);
. It must also be side-effect free and must consistently return an object, and provide the same output every time if provided the same input.
At the start of every game, your ant function is granted 1 second of reserve time. It is also granted an additional 10 milliseconds of time every time it is called. Time measurement starts right before the function call and ends upon return. That time is then taken away from the reserve time. Your function must not run out of the reserve time.
The constructed object and any temporary data combined must not consume more than 64 MB of memory at any time.
Here's a template for you to fill in:
class Entry { // TODO: Change the "Entry" to whatever you want, preferably your entry name. Remember that this is a JavaScript class name, so you can't use some characters.
constructor() {
// TODO: Optionally fill this in.
}
getAction(view) {
// TODO: Fill this in.
}
}
Arena
The arena is a toroidal (edge wrapping) grid of hexagonal cells arranged in a rhombus of side length 1500. Initially, all cells have the color 0 and 0.1% have food placed on them, for a total of 2250 pieces of food. Up to 12 entries are randomly chosen every game and have their corresponding ants (one per entry) spawned in random locations, with no more than one ant occupying any cell. The initial turn order of ants is also randomized.
Game
The game lasts 32768 turns, which are processed sequentially. Every turn, in an unchanging order, each ant has its corresponding function called with the local state of the arena passed as an argument. In other words, your ant function is called separately for every ant you control. The action chosen by the ant function is then taken immediately - other ants see the arena changes even during the same turn.
Ants
State
Each ant carries the following properties with them:
- Position: Implementation-defined.
- Ants do not have access to their global position.
- Tier: An integer representing the tier of an ant.
- Equal to 0 for all ants not spawned by other ants.
- Doesn't change over an ant's lifespan.
- The lower its tier, the less valuable an ant is.
- There is no theoretical limit on how high or low tier can be.
- Age: An integer representing how old an ant is.
- Initially 0 for all ants.
- After every action an ant takes its age goes up by one.
- Food: An integer representing the amount of food an ant is carrying.
- Initially 0 for all ants.
- An ant may carry an unlimited amount of food.
- Memory: An integer in the range 0..15 representing the memory of an ant.
- Initially equal to 0 for all ants not spawned by other ants.
- An ant may change it as part of any action.
- Holds no meaning for the controller.
- Owner: Implementation-defined.
- Allows ants to see whether another one is a friend or not.
Sight
Each ant sees 6 cells in its neighborhood, as well as the cell it is currently occupying. Each cell contains the following properties:
color
: An integer in the range 0..7 representing the current color of a cell.
food
: A boolean representing whether or not a piece of food is present on a cell.
ant
: An object if an ant is present on a cell, undefined
otherwise. If present, the object has the following properties:
tier
: Maps to Tier.
age
: An integer in the range 0..3 representing an ant's age on a logarithmic scale. 0 actual age maps to 0, 1..31 actual age maps to 1, 32..1023 actual age maps to 2 and 1024+ maps to 4. [Sandbox note: I'm torn about whether or not the ants should have access to this information.]
- [Sandbox] Alternative for or complement to
age
: older
: A boolean representing whether or not an ant's age is higher that of the currently active ant.
food
: An integer in the range 0..3 representing the amount of stored food by an ant on a logarithmic scale. 0 actual food maps to 0, 1..3 actual food maps to 1, 4..15 actual food maps to 2 and 16+ actual food maps to 3.
memory
: Maps to Memory.
friend
: A boolean representing whether or not an ant is under control of the same entry as the currently active ant.
Every turn sight is passed to your ant function as an array of 7 objects, arranged in this order:
0 1
5 6 2
4 3
The view is randomly rotated by a multiple of 60 degrees every time your ant function is called. This means that it's impossible to extract a consistent sense of direction without clever use of the environment.
Accessing one's own state is done by retrieving the ant object of the 7th element of the array, like this: const me = view[6].ant;
Actions
Each ant must perform an action every turn. The possible actions are as follows:
- Move: Move to the desired cell.
- Format:
{cell: 0..6, action: 0}
- When moving onto a cell with food on it, an ant automatically picks up the piece of food present.
- When moving onto a cell with an ant on it, of the two ants only one remains. If the still ant's
tier
value is lower than that of the moving ant, then the still ant dies and is permanently removed from the game. Otherwise, the unfortunate fate awaits the moving ant instead. In either case, the remaining ant gains all the food of the ant that died and one more.
- Staying still is a valid action and may hold merit in certain situations.
- Paint: Change the color of the desired cell to the desired color.
- Format:
{cell: 0..6, action: 1, color: 0..7}
- Changing the color of a cell to the same one is a valid action.
- Place food: Put a piece of food onto the desired cell.
- Format:
{cell: 0..6, action: 2}
- Placing a piece of food onto a cell with an ant on it results in that ant immediately picking it up.
- Placing food costs food. An ant may only place a piece of food if it has at least one food stored, and one food is taken away from it.
- Placing food onto own cell is a valid action, but placing food onto a cell with a piece of food already on it is not.
- Spawn: Create a new ant under control of the same entry on the desired cell.
- Format:
{cell: 0..6, action: 3, tier: -1..+1, state: 0..15}
- A parent ant must choose to spend 1, 4 or 16 food on the new ant by specifying a
tier
equal to -1, 0 or +1 respectively.
- A parent ant may only spawn an ant of the desired tier if it has the necessary amount of food to do so, and that amount is taken away from it.
- Spawning results in an ant of a tier equal to that of its parent plus the
tier
value specified.
- A parent ant must choose the initial memory of the new ant by specifying a
state
. [Sandbox note: I'm considering not letting parents set the initial memory of offspring.]
- The new ant is inserted into the turn order right before its parent.
- Spawning an ant counts as movement for the new ant, and is resolved as such.
- Spawning an ant onto own cell is a valid action and may hold merit in certain situations.
- Memorize: Replace the currently active ant's memory with the desired number.
- Format:
{memory: 0..15}
- Memorization is not an action itself. It must be appended to one of the four previously mentioned actions.
- An ant may choose to leave this field undefined. No memory change occurs then. [Sandbox note: Torn about this as well.]
Format refers to the JavaScript object that must be returned by your ant function to perform a specific action.
Example outputs:
{cell: 0, action: 0} // Move to cell 0.
{cell: 6, action: 0} // Do nothing.
{cell: 6, action: 1, color: 0} // Set own cell color to 0.
{cell: 3, action: 2} // Put a piece of food onto cell 3. Invalid if there is a piece of food on cell 3.
{cell: 2, action: 3, tier: -1, state: 3} // Spawn an ant 1 tier lower than that of your own with initial memory equal to 3 on cell 2 at the cost of 1 food. Invalid if you have no food.
{cell: 5, action: 3, tier: +1, state: 11, memory: 14} // Spawn an ant 1 tier higher than that of your own with initial memory equal to 11 on cell 5 at the cost of 16 food whilst committing 14 to memory. Invalid if you have less than 16 food.
{cell: 6, action: 3, tier: 0, state: 0} // Spawn an ant with tier equal to that of your own with initial memory equal to 0 on your own cell at the cost of 4 food. A conflict occurs and the newly spawned ant dies, giving you a piece of food. Effectively, ou lost 3 pieces of food. Invalid if you have less than 4 food.
{cell: 6, action: 3, tier: +1, state: 0} // Spawn an ant 1 tier higher than that of your own with initial memory equal to 0 on your own cell at the cost of 16 food. A conflict occurs and you die, giving the newly spawned ant a piece of food (and any additional food you might've had). Effectively, you upgraded yourself at the cost of 15 food. Invalid if you have less than 16 food.
{cell: 3, action: 0, color: 5, test: true, memory: 1} // Move to cell 3 whilst committing 1 to memory. Values of
colorand
testare irrelevant. You may choose to include unrelated properties - they are simply ignored.
[Sandbox note: Maybe the controller shouldn't allow color
in this case? This looks like it might be a good breeding ground for hard-to-detect bugs.]
Example invalid outputs (not exhaustive):
{cell: 7, action: 0} // Error: Value of "cell" outside required range 0..6.
{cell: 3} // Error: "action" left undefined.
{action: 1, color: 4} // Error: "cell" left undefined.
{cell: 4, action: 3, tier: -1} // Error: "state" left undefined.
Scoring
At the end of each game, every entry participating in that game is evaluated according to this formula:

n is the number of ants belonging to the entry currently being evaluated
Ti is the tier of the ith ant belonging to the entry currently being evaluated
Fi is the amount of food the ith ant belonging to the entry currently being evaluated is holding
Or as pseudocode:
var evaluation = 0;
for (int i = 0; i < entry.ants.count; i++) {
var ant = entry.ants[i];
evaluation += Math.Pow(2, ant.tier) + Math.Pow(2, ant.tier - 1) * ant.food;
}
[Sandbox note: Should I count food as score or not?]
The final score of each entry is equal to the amount of entries whose evaluation was lower than that of the currently considered entry. This means that the highest score an entry can get is 11.
Tournaments
A tournament is nothing more than a series of individual games. At any point of a tournament the score of each entry is equal to the average of all of its scores.
Official tournaments
Official tournaments will be run by me on the latest version of Chrome on my personal computer, which at the time of writing is an AMD FX-8350, every time a new entry is posted or an existing one receives a meaningful edit. The leaderboard will be updated once the 1st place becomes consistent between 6 subsets of played games (which gives a probability of 96.875% that the first place won't change).
As stated before, there is no permanent winner. This means that no checkmark will be awarded to any answer, ever. I will continue to run new tournaments for as long as is practical.
Submissions
Each submission must follow this general format:
Entry Title
[Optional text and pictures]
Code block with your JavaScript class
[Optional text, pictures and code blocks]
Not adhering to the above format may result in the submission not being properly picked up by the controller.
Explanations and pictures are highly encouraged, though not necessary. Making your entry pretty and well-documented will entice me (and probably a lot of people) to upvote it.
Disqualification
Your entry will be disqualified if it is found to not adhere to the specification correctly. Most of the time problems will be caught by the controller and reported, but some rules can't be checked programmatically, so they'll be enforced by hand. I reserve the right to disqualify an entry manually if it breaks the rules or if I subjectively believe it has not been made with fair competition in mind. I hope I haven't left any loopholes (and therefore won't have to exercise this power), but if I did then this rule prevents them from potentially ruining the entire challenge.
Disqualification during a game results in all of your ants being immediately and permanently removed from the turn order. Disqualification during a tournament invalidates all games in which your entry has participated and terminates the active game (as it would've been invalid anyway). Disqualification during an official tournament, aside from having the effects of a regular tournament disqualification, prohibits your entry from taking part in any future official tournament until meaningfully edited.
This is not supposed to be an additional challenge - helpful error messages along with your ant function's output and the input that disqualified it will be attached to the disqualification notice. I will also paste these messages into a comment informing you of a disqualification should your entry be disqualified during an official tournament.
Multiple entries and editing
You may provide multiple entries, provided that they do not team up against the others. As long as each entry is working solely towards its own victory, you are permitted to tailor your strategy to take advantage of others' weaknesses. You may also edit your answers whenever you choose. It is up to you whether you post a new entry or edit an existing one; just don't flood the game with nearly identical variations. If you make a variation of another person's entry, remember to give them credit by linking to their entry from your own.
Example entries
Randant
This entry demonstrates the minimal amount of code needed for something to be considered valid. It returns the same output regardless of input.
class Randant {
getAction() {
return {cell: 0, action: 0};
}
}
Because the orientation is random every time, Randant will perform a random walk instead of going straight.
Despite being very simple, this is a perfectly valid submission. It won't ever get disqualified either, because moving is guaranteed to be a valid action.
See Smart Randant on how one could go about improving this entry.
Smart Randant
This entry demonstrates basic mechanics that ought to be commonly used between more advanced entries. It is an improvement of Randant's general design. It depends on input this time around.
class SmartRandant {
getAction(view) {
const me = view[6].ant; // Get data about myself
function wrap(number, cycleLength = 6, negativeSafety = 1) { // Defining functions inside the getAction(view) function is fine
return (number + cycleLength * negativeSafety) % cycleLength; // Wraps numbers (allows for cyclic array access)
}
function stronger(a, b) { // Checks if ant a is stronger than ant b
return a && (!b || a.tier > b.tier);
}
view.forEach((cell, i) => { // Modifying the view is perfectly acceptable (here we do it to store the index alongside each cell)
cell.index = i;
});
const dirs = view.slice(0, 6);
const safe = dirs.filter((cell, i) => !stronger(dirs[cycle(i - 1)].ant, me) && !stronger(dirs[cycle(i + 1)].ant, me) && stronger(me, cell.ant)); // Create an array of cells that are safe to move to
// TODO: Also consider attacking other ants that are at the same strength level as us by spawning a stronger ant on their face
if (safe.length) { // If we've got some safe spaces to move to...
const victims = safe.filter((cell) => cell.ant && stronger(me, cell.ant)); // Prioritize killing other ants
if (victims.length) { // If we've got someone to kill...
const target = victims.reduce((prev, next) => stronger(next.ant, prev.ant) ? next : prev); // Find the strongest victim and target it
return {cell: target.index, action: 0};
}
const food = safe.filter((cell) => cell.food); // Otherwise prioritize grabbing food
if (food.length) { // If we've got some food to grab...
return {cell: food[0].index, action: 0} // Just grab any piece
// TODO: Grab the piece that has the most pieces of food next to it
}
return {cell: safe[0].index, action: 0} // Otherwise move to any safe cell
}
return {cell: 6, action: 0}; // If we don't have a safe cell to move to, just stay still and hope for the best
// TODO: Handle this situation better
}
}
As you can see, Smart Randant is a lot smarter than his counterpart. Smart Randant dropped his suicidal tendencies by finding safe cells before moving. Note that despite not being suicidal, he may still be killed by particularly cunning entries. He's also more aggressive - he'll gladly attack opponents weaker than him and will grab nearby food. Finally, if he's got anywhere to go, he will go there. Otherwise, he'll just stay still, hoping for the best.
You may have also noticed that Smart Randant still has a lot to learn. You can edit your entries to your heart's content, so W.I.P. submissions are allowed, or even encouraged.
Straighter
This entry demonstrates smart usage of the environment for the purpose of fighting randomness. It travels in a straight line, leaving behind a path. It also utilizes and expands Smart Randant's basic framework.
[Placeholder]
Meta
The chat room is active already and is a better place for extended discussion than the comment section. I'd also love to dicuss some preliminary ideas for potential strategies to be used in future entries (though let's try to restrict this dicussion to nothing more than interesting isolated systems rather than full submissions - I'm not particularly interested in posting a nearly-solved challenge).
Most links are broken for now. The controller, for example, doesn't exist yet.
I'm aware that I'll have to make (or find) a custom number library that can handle nearly arbitrarily small and large powers of 2 for scoring (I've managed to design a system which spawns progressively less valuable ants every 2 turns at no cost, leading to a score equal to about 1 + 2e-16384
).