12  Programming agent AI

12.1 Modelling behaviour

We advance into implementing the behaviour rules, i.e. Artificial Inteligence, of each agent type, particularly traders. The conceptual model specifies the following:

  • settlements: produce goods according to their size and a productivity term. They create or destroy traders according to their current size.
  • traders: take some goods from their base and deliver them to another settlement, which is chosen based on the size of other settlements and the distance between these and the base settlement. Traders will then return with goods from that settlement back to the base and restart the cycle.

Implementing this will take us several steps in development (up to step 9). Given the importance and potential complexity of these behaviours, let us start by implementing the most fundamental aspect: the traders’ criterium for choosing a destination settlement. We have mentioned “distance”, yet how should traders measure such distance in a heterogeneous terrain?

12.2 Implementing path finding

In the original repository of the PondTrade model, you will notice an initial attempt to approach this aspect through a simple network implementation, using NetLogo’s links (PondTrade_step07_agent AI v1_links.nlogo). This approach is similar to other ABM and network-based models applied to topics of trade and settlement interactions.

However, as you may know, there can be a significant difference between a straight line measurement (Euclidean distance) and a more complex path cost calculation that uses terrain data. As the PondTrade model eventually did, we will to “complicate” our design and make a proper “least-cost route” calculation using this as a perfect excuse for learning.

But how should we implement a least-cost path algorithm in NetLogo? We follow our modular philosophy, search and find an implementation of the A* (A-star) algorithm in NetLogo User Community Models. We get the main fragment of the code and adapt it to our purposes, making sure we keep a clear reference to the source as a commentary header:shown. Do the same to check the information about this trader base. This time, however, write the inspect settlement <WHO NUMBER> directly into the console. You can now verify that the trader and settlement were assigned the same colour.

Because our model assigns different path costs for each patch (i.e., land or water), we must create a new procedure that assigns these costs and add a call to it just after the other steps in create-map:

We are forced then to introduce another parameter, relativePathCostInLand, which specifies how much the movement on land costs in relation to the movement on the water. Add a slider in the interface (from 0 to 100, by 0.01, default value at 50).

Given the special circumstance of traders arriving and leaving settlements, let us introduce a differential value for pathCost in patches with settlements. For this we should modify create-coastal-settlements and introduce the parameter relativePathCostInPort (from 0 to 100, by 0.01, default value at 10):

Notice that relativePathCostInPort will generally not affect A* results since routes will always have two patches with this pathCost. However, it does matter because routes will more likely avoid going through a third settlement if a path through water is available.

12.3 Testing the algorithm

You can now experiment with the A* algorithm by running setupas it is, and then find-a-path between two patches of your choosing. To observe how the algorithm works, you can “uncomment” the lines, including code about colouring patches within the algorithm code. Then, reduce the simulation speed in the interface and rerun the procedure.

Notice how the large difference in path cost between land and water (x50) makes A* avoid drawing a route through land, practically until all adjacent water patches are explored.

12.4 Adapting model schedule

Now that we have confirmed the code we just introduced let us implement everything we will need to call A* and keep track of all routes between settlements. Given that A* takes time to compute, we should make everything possible to avoid repeating the calculation of routes.

Running the code as it is will generate the maximum number of routes given the settlements present (e.g., if numberOfSettlements = 10, it will be 45 or 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1). We store each route as a list of patches, and all routes as a list of routes (routes). These will be globally accessible by all traders in our model.

Notice that it takes a considerable time to calculate all possible routes, and the number of settlements increases this time exponentially. At this point, we should be aware of how to stop NetLogo while still running a long command. Go to Tools > Halt. This might not work if NetLogo interface is unresponsive. Then the only options are to wait or quit or force-quit NetLogo.

12.5 Decision making

Having the calculation of routes solved, we now must use them to inform the decision of traders. Let us create a procedure to be called by traders named choose-destination, for which we will need to implement a few “helper” procedures:

Notice that get-route is only helpful for debugging and querying the information in routes.

To inform the traders’ decisions, we specify a very simple calculation of the potential gain of a route (sum of sizeLevel of origin and destination) and calculate a ratio between the benefit and cost of routes. Traders will then compare these ratios for all possible routes from its base settlement and choose the one with the highest value.

12.6 Movement

Once a destination is decided, traders must move towards it and then, once there, start the return trip. We implement this behaviour assuming that movement must go through each patch center in the route and that traders move at a maximum speed of 1 / [pathCost] of patch-here, meaning they will be delayed in proportion to the local value of pathCost.

We wrap up this development step by finally implementing our preliminary model cycle, ordering the behaviour of traders within the conventional go procedure:

As we specified in our conceptual model, traders will choose a destination whenever they are at their base and move towards their current destination.

12.7 Enhancing visualisation

To help visualise routes, implement the following version of update-display, call it at the end of setup, and add two switches, showRoutes and showActiveRoutes to the interface:

12.8 Testing agent behaviour

As a temporary “hack”, replace the code create-traders-per-settlement with the following:

Reduce numberOfSettlements, vary seed, and run setup again. Run go repeatedly to observe the behaviour of the loner trader.

Pond Trade step 7
Pond Trade step 7