Gathering Food
In the game "Ants", one of the most important elements in winning is gathering food in an efficient manner. Whoever has gather more food at say, turn 20 will have more ants with which to kill their enemies and gather more food. A relatively small difference in performance can result in huge differences over time between opponents. My algorithm for food gathering is currently far from optimal+, but it does a pretty good job. In most of our matches, we are either ahead of or equal to our opponents when it comes to the amount of units at the start of the game.
So, let's talk about the different problems that exist with gathering food. But first, we really need to definewhat we mean by gathering food efficiently. Obviously we want to find and gather as much food as possible. Let's say there are two methods that will get us equal amounts of food, but one will give us more sooner for slightly less food later compared to the other method.
Let's say "Option A" means that we gather 2 food in 5 turns as opposed to 7 turns, but then it takes us 15 turns instead of 12 to gather 3 more food . On turn 12 we have more with "Option A", and on turns 5 we have more with "Option B". Which of these two options should we choose?
I would argue that, since the growth of your ants is exponential, the more ants you have (especially in the first few rounds) the more food you will be able to gather. Thus+, we will favor a strategy that gets us resources fastest, even at the expense of efficiency in later turns.
The very simple case
The simplest possible case is that we have a piece of food. We can see the piece of food, and we know the way to get to it. We have one ant, so we send him to get the food.
The slightly less simple case
We still have one ant and terrain is not a problem. However+, we have two pieces of food. We will gather both of these, but there are two different ways to do this , one bad and one good. We want the ant to go to the closest piece of food first and then go for the second. Now given our definition of efficient food gathering we can simply extend our algorithm to N pieces of food and say that we will get the first piece of food quickest if we go to the nearest piece of food.
It may be that the second and third pieces of food are close together, so that we can have two food faster, but we will be naive about this potential development.
The complex case:
Now here is the real problem: In the game, we will have K ants that want to collect N pieces of food.[footnote] Our current algorithm simply tried to maximize our fast gathering of food. For every piece of food we see, we try to find the ant that is closest. We then recognize the piece of food and tell the closest ant to go gather this food. This is illustrated in the figure below

The nitty gritty:
Well, this sounds all well and good, but how can we actually accomplish this complicated process? We have a list of all our ants, ants.my_ants() , as well as a list of all the ants currently assigned to gather food, self.food_ants. We also keep a list of all food that has already been assigned to some ant to collect, so that two ants do not go after the same piece of food. So the start of
the code is actually quite simple:
food_taken = [f for l in self.taken_food.values() for f in l]
available_food = [f for f in ants.food() if f not in food_taken]
We simply have to create a list of available food to check. Now this available food list will (-be a list of) contain positions representing where there
is food. The function ants.distance(loc1,loc2) returns the distance from loc1 to loc2.
for food_loc in available_food:
for ant_loc in ants.my_ants():
if ant_loc not in self.food_ants:
# If it is not an ant that is already gathering food we only need to consider the distance
distances.append((ants.distance(ant_loc,food_loc), ant_loc, food_loc))
for ant_loc in self.food_ants:
tot_dist = 0
old_path = ant_loc
for food_path in self.food_ants[ant_loc]:
tot_dist += ants.distance(food_path,old_path)
old_path = food_path
# We add the distance from each food to the next
# Distance from last food in que to new food
new_dist = ants.distance(self.food_ants[ant_loc][-1][0], food_loc)
distances.append((tot_dist + new_dist, ant_loc,food_loc))
For a given piece of food+, we now have a list of the distances from each ant to this piece of food.
Then, we simply sort the list and then assign the ant which is closest to this food to collect it. We then use the PFF.find_path(map, loc1,loc2) function, which finds the fastest path from loc1 to loc2 on the given map.
distances.sort()
for dist, ant_loc, food_loc in distances:
foodpath = PFF.find_path(ants.map, ant_loc, food_loc)
if ant_loc not in self.food_ants:
self.food_ants[ant_loc] = []
self.taken_food[ant_loc] = []
self.food_ants[ant_loc].append(foodpath)
self.taken_food[ant_loc].append(food_loc)
This is our food gathering algorithm as it is now. While I do think this algorithm is quite efficient, it suffers from being 'self centered'. In other words, it does not at all take into consideration the actions of other players. So for example it would not consider whether an enemy ant is close to a piece of food, and if we should still try to get that food. The main problem is not whether the solution itself is optimal, but the running time of the algorithm. Our bot is currently having problems with "timing out", and I believe that this food searching algorithm may be the culprit.
Some possible ways of speeding it up might be not to consider all ants, but to somehow localize ants and only search through certain sub groups of ants. But I still think this code is quite solid. There is still some tweaking and optimizing that can be done, but it is not the focus of our current efforts.
In the next article, I will discuss what is currently the weakest aspect of our bot: the combat algorithm and the thoughts that go into that.
[footnote]I realized as I was writing that it is quite possible to write this as some form of maximum flow problem and solve this problem. However at the moment+, our algorithm is far more naïve/greedy, and I think it is relatively likely that a maximum flow problem would take too long for us to solve.


