Gladiabots

Gladiabots

Not enough ratings
AI Writing Techinques Guide
By Blackwater Canyon
This guide sets out to help others tackle both simple and complicated challenges in writing AI code.
2
   
Award
Favorite
Favorited
Unfavorite
The Editor
Connecting Nodes
When connecting two nodes, it's better to draw the connecting line to the center of the target node, rather than the open connecting hole (see green arrow).

Versioning by Folder
It is very useful to keep old versions of your bots in case you notice that your new bots seem to be performing worse that previous bots. Good ideas can sometimes work out poorly in practice. A new bot may also have some new good ideas along with the bad, so it is useful to compare new bots with old bots to generate continuous improvement.

I find that the easiest way to keep versions of my bots has been to put all of the AI related to a single team into one folder and then simply duplicate the folder to start a new version. Once an old version is finished, I never change it. If I find a mistake, I simply duplicate the buggy version, then fix the new duplicate.

If your code never references AIs outside the folder, this is a very clean way to keep your versions. The main disadvantage of this method is that it will create a lot of folders and files, which will increase scrolling and load times.

Versioning by File
I found that I was often making only one or two small changes per version, so I've mostly stopped versioning by folder. It is possible to version by file, but one must be *very* meticulous about changes, or its too easy to put changes into older versions.
Differences and Optimization
These two conditions are not the same. The example on the left means, "There are no enemies in short range." The one on the right means, "There is at least one enemy not in short range."

Subtle Differences
Some nodes are very similar, but have subtle differences. Gladiabots player, HenryChess, brought up this example recently[discord.com]. As Pege indicates[discord.com], they are extremely similar, but not the same. Asking about "Myself" is always, "what have you been doing" and asking about an enemy bot is always, "what is happening now." So if you destroyed a bot that you were shooting at in between the last tick and the current one, the left will evaluate false and the right will evaluate as true. One could use the two nodes in concert to track kills by a bot (there's some error in this metric due to assistance and bullet flight time).

Eliminating Unnecessary Conditions
It can be possible to save nodes by eliminating unnecessary conditions in your logic. In this example, the code on the top contains superfluous conditions. For example, only bots that are carrying resources can drop them or score. Note that the move condition can never be reached, because all bot classes are capable of firing at Medium range.
Common AI Patterns
Using a Master AI
Even if your bots are all very different, it may still be useful to have a single master AI for all of your bots that does simple delegation to each bot type. This makes it very easy to assign AIs in Team Setup, since there is only one to assign, and it can be assigned to all.

First Tick Planning
It can be very useful to execute code exactly once at the beginning of the game. We can do that by checking to see if no time has gone by. Since time is in seconds, this code could run for 4 ticks, so we will want to only run on the first tick. To do that, we can check to see if the bot was idle in the previous tick. That will generally ensure that the code gets run exactly once. Initialization code should be the first code that the bot runs to ensure that it will execute properly. If we want the code to run exactly once by the first bot, and not be repeated by other bots on the team, we can check to see if all our allies are idle, which is generally only true for our first bot on the first turn.

Variable Multiplication
It's not possible to do multiplication directly, but players like computer-whispe2 and others observed it is possible to do a number of additions in sequence to do it using peasant multiplication[en.wikipedia.org]. Remember that nodes can only execute once, so you'll need to copy this Sub-AI every time you want to multiply. This can result in a lot of extra nodes, so use multiplication sparingly.

This example lets us use a number from 0 to 7 for B's value, and essentially any number for C. If you would like to use bigger values for B, just extend the pattern of 1, 2, and 4 with powers of 2 (e.g. 8, 16, 32, etc.) until you can multiply the size you need.

Fixed Multiplication
If you want to multiply by a constant, it's possible to use fewer nodes and counters for that. In this example, I effectively multiply counter by copying it to counter three, then adding it to itself (doubling it) and then adding adding counter 3. Stated another way: 3x = x + x + x or 3x = 2x + x.

Gladiabots player, GameAg, provides a fairly comprehensive text file[cdn.discordapp.com] with examples of how to multiply by every number from 1 through 10,000 on the official Discord.

Fixed Division by Powers of Two (Right Shift)
Like multiplication, there is no division either. Division can be done long-hand. For powers of two, we can write a fairly simple procedure for fixed division. The process to do this is to look at the highest bit, subtract it out, and replace it with the appropriate bit. If we are dividing by two, we'd substitute 4 for 8, 2 for 4, and so on. If we were to divide by 4, we'd substitute 2 for 8, 1 for 4, and so on.

Variable Division
A more general version of division can be written if we know the upper limit of the quotient, using binary long division. This version, referencing work from Gladiabots player Computer-Whispe2, can be extended to larger quotients by expanding the pattern for values of 8, 16, 32, and so on. A drawback to this solution is that it uses many variables (4!) that may ultimately limit your overall AI.

Loops, Over Time
Variable loops are not possible in Gladiabots, but you can execute a loop over time, and bots can collaborate to execute the loop together. In this way, operations like variable division can be conducted with a tiny number of nodes. In this example, B is the dividend, C is the divisor, and A is the quotient. The stop condition is that B is < A, which will also mean that B is the remainder. One shouldn't limit creativity to just mathematical operations like divison. Many algorithms' loops could be executed in this way if the execution length is relatively short.

The Latch
Early players observed that it can sometimes be useful to vary the execution order of nodes to reduce the number of required conditions and node duplication. Bockwurst's video was mostly made obsolete by the addition of counters in the game, but it is still occasionally useful to use this technique.

If Else Options
We often need to put conditions in our AIs. Many languages have an If-Else construct. In Gladiabots, there are a few different options to achieve this. If we know that the condition of one of the branches will lead to an outcome that cannot cause faulty behavior by following a later branch, we can place that branch first, and then not bother to place an "else" condition. In the "Implicit Else" example, if a sniper has no targets out to long range, it can't have targets at medium range. No condition is necessary. If we were concerned about that, we can simply create a condition for the alternative "else" branch, as in the "Explicit Else" example. In some scenarios where there is behavior that will not stop execution, such as tagging or counting, we need a way to ensure that execution on the first branch won't accidentally trigger the second. We can use a latch, described above, to solve this problem. Immediately after passing the first condition, we test the subsequent condition. This will cause the subsequent condition to fail, and prevent execution down that path later.
Tag Counter Methods
(This section is in progress)
There's not a way to assign counters to things like enemy bots, but we can use tag combinations to create the same effect. By using either team or individual tags in combination, we can essentially create counters on any entity.

Binary counters vs. Gray Code
There are two popular ways to encode tag values, Gray code and binary. Gray code is best for counting very small numbers, or where conversion to numbers isn't needed (e.g. you need to count, but only take action at 0). Gray Code[en.wikipedia.org] counters.

Making a Single Counter With Tags
If only one object is meant to be tagged, a simpler technique can be used. This uses the latch described above. Since force fields are always present in some modes and rarely tagged, it can be convenient to tag a force field if an arbitrary host is needed.

Incrementing Many Counters with Binary
One may wish to tag many entities and increment them simultaneously.

Incrementing Gray Code
Gray Code is very easy to increment. It is designed such that exactly one tag is required to be altered per total values represented.

Converting from Gray Code to a Counter
Gray code is a bit difficult to calculate with directly, so it's useful to convert it into a counter before using it. This is done by executing a series of exclusive ORs.
Style
Following a consistent style is a great way to make your code readable and maintainable. My suggestions for keeping your code legible and easy to understand:
  • All code is arranged into rows.
  • When starting a new row, the center-most node is directly beneath it's parent node.
  • One grid square separates each row.
  • All nodes are snapped to grids.
  • Children are always in a lower row than their parent (usually one row down).