Contest Finished! Read comments on blobs to view their score.
This KoTH is loosely inspired by Primer's Natural Selection Simulation. Your bot is a blob. In order to survive, you must eat pellets to regain energy, which is used to move. With extra energy, blobs can split into two.
Energy and Movement
Your blob starts off each round with 100 energy, and it has no limit on the amount of energy it can collect. Each round is run in turns, with each blob having the option to move North, East, South, or West in any given turn, or stand still. Moving uses 1 energy, and standing still uses 0.25 energy. The map's side length is ceil(0.25 * blobCount) * 2 - 1
units, with a minimum of 9 units. All blobs start on the edge of the map, with one placed in each corner and every subsequent blob being placed 2 units away from any others. Every 30 turns, a wave of pellets are placed in random spots around the map, at least 1 unit from any edge. Each time a wave of pellets appears, the quantity of pellets (originally twice the number of blobs or the width of the map, whichever is larger) in the next wave is decreased by 1, forcing the number of blobs to decrease over time. Each pellet restores between 5 and 15 energy. When a blob's energy is less than or equal to 0, it dies.
Eating
If two or more blobs attempt to occupy the same location, the one with the most energy will eat the others, receiving their energy. If both have equal energy, both vanish.
Detection and Information
Blobs can see any pellets or other blobs within a distance of 4 units. When their functions are called, blobs are provided with:
- The side length of the map
- The position of the blob on the map
- The positions of all pellets within their search radius, as well as their values
- The positions of all blobs within their search radius, as well as their energy and UIDs
- The energy, UID, and locations of the blob whose function is being executed
- A storage object unique to the blob
- A storage object shared by all blobs related to the blob through splitting
Splitting
If a blob has more than 50 energy, it can choose to split. Splitting costs 50 energy, and any remaining energy is divided evenly between the two blobs. All blobs are either originals or split copies, with every copy tracing back to an original. All of these together are "relatives." All relatives have one communal storage object. Relatives can still eat each other, and can split, use their own storage object, or collect energy without affecting others.
Energy Transfer
If two blobs are next to each other (after moving), one of the bots can transfer energy to the other. This is done by returning SendNorth(amt)
, SendEast(amt)
, SendSouth(amt)
, or SendWest(amt)
, with amt
being a number representing the amount sent. This can be any amount that the sender can afford, including all of their energy. It is recommended that the blob who is receiving energy is told to stay still through communal storage, so that it does not move away when the energy is being transferred (though the energy would not be deducted from the sender's total in this case).
Functions, Storage, and UIDs
In order to allow more complex learning behaviors, all blobs will be given an integer UID (Unique Identifer). These UIDs will be randomly generated each map, preventing strategies based on individual targets. When a blob's function is called, it is passed four arguments:
- The side length of the map as an integer
- An object with two arrays:
pellets
, andblobs
. Both arrays contain objects, both having apos
property containing the pellet or blob's position formatted as[x,y]
. Pellets will have anenergy
property, while blobs will have auid
property and anenergy
property - An object containing various properties of the blob it is passed to:
energy
,uid
, andpos
. Thepos
array is formatted as[x,y]
- An object containing the two storage objects of the blob. A
self
property contains an individual storage object which can be modified however the blob sees fit (by manipulating properties of the object that is passed), and acommunal
property which can be modified by any relative.
Blobs are not moved immediately to prevent earlier/later turns having an advantage. All movements are processed in groups (All collisions/eating, then all pellets, then splitting, etc.) If a blob lands on a pellet or smaller blob and, in the process uses its last energy, the blob will still consume the pellet/energy independent of whether that would would bring its total energy above 0.
In order for relative blobs to recognize one another, the communal storage must be used for each blob to record its UID in an array, or through some other system.
Return Values
In order to move or split, the return value of the function is used. First, the meaning of the cardinal directions in terms of coordinates:
- North = -Y
- East = +X
- South = +Y
- West = -X
Note that [0,0]
is the top left corner, and Y increases as you go down. The return value of the function should follow these rules:
- To do Nothing: Return nothing, 0, null, undefined, false, or any other value that equates to false
- To Move: Return one of four global variables: North, East, South, or West, which equate to "north", "east", "south", or "west" (which could also be used as a return value)
- To Split: Return the global variable SplitNorth, SplitEast, SplitSouth, or SplitWest, the direction indicating where to place the new blob
If a split command is returned and the amount of energy required is greater than or equal to the energy of the blob, nothing will happen. Blobs will not be able to leave the map.
Predefined Library Functions
There are a few basic functions available by default, to save some time:
taxiDist(pt1, pt2)
Returns the taxicab distance between two points (X distance plus Y distance).
taxiDist([0, 0], [2, 2]) //4
taxiDist([3, 4], [1, 5]) //3
taxiDist([1.25, 1.3], [1.3, 1.4]) //0.15
taxiDist([0, 0], [5, 2.5], 2.5) //3
taxiDist([0, 0], [2, 4], 2.5) //2.4
hypotDist(pt1, pt2)
Returns distance between two points according to the pythagorean theorem
hypotDist([0, 0], [5, 12]) //13
hypotDist([4, 6], [8, 9]) //5
hypotDist([0, 1], [2, 1]) //2
hypotDist([1, 1], [2, 2]) //sqrt(2)
modDir(dir, amt)
Takes the inputted direction, rotates 90 degrees clockwise amt
times, then returns the new value.
modDist(North, 1) //East
modDist(East, 2) //West
modDist(West, 3) //South
modDist(South, 4) //South
Example Blob
This blob will not move until it finds a pellet nearby. Then, it will move in the direction it thinks is most likely to reward it. If its energy is ever above 150, it will split.
function(map, near, me, storage) {
if (me.energy > 150)
return SplitNorth;
if (!near.pellets.length)
return null;
var dirs = [0, 0, 0, 0];
for (let p, i = 0; i < near.pellets.length; i++) {
p = near.pellets[i];
dirs[0] += me.pos[1] - p.pos[1];
dirs[1] += p.pos[0] - me.pos[0];
dirs[2] += p.pos[1] - me.pos[1];
dirs[3] += me.pos[0] - p.pos[0];
}
return [North, East, South, West][dirs.indexOf(Math.max(...dirs))];
}
Rules
- Standard Loopholes are prohibited. Also, no Unstandard Loopholes.
- No blob may attempt to modify or read any data not passed to it via its parameters
- No blob may attempt to modify a return-value variable to sabotage other blobs
- A round lasts until the only remaining blobs are relatives
- No blob may modify data by injecting functions into its parameters which modify values using the
this
keyword - All submissions must either be in Javascript or a language which is not too different from Javascript (Python, for example). All answers will be converted to Javascript for the competition.
- The winner is the blob which has collected the highest amount of energy in total across all rounds (from either pellets or consuming smaller blobs that are not relatives)
Controller: https://gist.github.com/Radvylf/1facc0afe24c5dfd3ada8b8a2c493242
Chatroom: https://chat.stackexchange.com/rooms/93370/hungry-blobs-koth