ABM Teaching Guide | An Enchantment of Digital Archaeology > Tasks for Golems – Building an ABM (Agent-Based Model)

Our Code on GitHub

Berghahn Digital Archaeology supports the transparent accessibility of any code or program used and/or incorporated in the digital projects found on this site. To view, download, or suggest updates for items related to any of our projects, please visit our GitHub page and repositories.


Tasks for Golems – Building an ABM (Agent-Based Model)

In this appendix, I am going to walk with you as we build a simple archaeological agent-based model. My ambition here is that you will find the experience as similarly enchanting as my students, but without perhaps the angst of having a grade attached to the exercise. Then we’re going to add some playful elements to the model, making it not quite a game but also not quite a simple simulation anymore. We will also build a reimplementation of the core model dynamics for the Itineraries model discussed in chapter 2, ‘Reanimating Networks’. We are not writing code from scratch. Instead, we are going to use the code snippets that come bundled with the NetLogo modelling environment. It’s a bit like using Lego blocks from different Lego sets to create that Star Wars / Harry Potter mash-up you’ve always wanted.

The point of doing this is that there is enchantment in the process of building. Moreover, when you build an agent-based model, you are giving your digital golems instructions that they will carry out (if they are well expressed—i.e., the instructions are syntactically correct), which may not necessarily mean they do what it is you wanted. Are they expressing some unanticipated outcome of the story you have told in code, some logical necessity that throws new light on the archaeological material, the archaeological story that you’re seeking to resurrect? Or is it just another damned bug? It’s at this point that you need a mode of wonder, of enchantment, to figure out what is a meaningful outcome of what you have encoded and what is an undesirable artefact of your thought. That is, what kind of ‘fail’ have you encountered?

You may skip ahead to the finished code if you want, but do try to work through the process below. We’re going to start with a completed model that can be thought of in archaeological terms. Then we’re going to back up and figure out how to load archaeological network data into this environment. Once we have an archaeological network, let us pretend that it is a network of sites connected to each other, over which we are interested in exploring the consequences of a simple trade in pots. We will then build into the simulation a pair of playful buttons: one to erase sites from the network and one to build more connections. We’ll finish off by modifying this network to use the information on the ‘strength’ of the connections as a proxy for damage-in-shipment.

I rarely write a simulation from scratch. What we are doing here is the way I have learned to build simulations: from the bric-a-brac of others’ work, using found code that might be retrieved from NetLogo’s own library of models and code snippets or that might exist out there on the web (one spot to start looking is at OpenABM, hosted at https://www.comses.net/).

Golems with Colds

One of my favourite demo models in the NetLogo environment is ‘virus on a network’. In this section, let’s explore its code and see how we can put a player into the simulation. NetLogo is available at http://ccl.northwestern.edu/netlogo. (In what follows below, I am using NetLogo version 6.0.4). Download and install the environment for your computer. Open it up, then under ‘files’, select ‘model library’. Under ‘sample models’, select ‘networks’ and then ‘virus on a network’.

This model simulates some kind of connected system (cities? countries? ports? individuals?) into which a virus has been introduced. The nodes – individual agents – can be susceptible to the virus, infected by the virus, or resistant to the virus. Nodes do not know right away that they’ve become infected, and there’s always a chance that they might become resistant. (If you click on the ‘info’ tab, you will find a description of what all the pieces of the model do, which incidentally makes this an example of ‘literate’ programming, where the analysis and paradata of the model can be included in the model file itself).

NetLogo: Virus on a Network – initial state

Run the model with its default settings. Notice how the virus burns itself out and the count of ‘resistant’ nodes over time represents an s-curve. Play with the settings: can you arrange things so that the virus never dies out, but remains endemic in the population? Is it a function of the size of the population? Is it a function of the ‘virus-check-frequency’? Imagine your own interest in archaeology: are there analogous situations where you can see that a ‘virus-on-a-network’ might work as a suitable metaphor for some aspect of your work? I can imagine where the introduction of a new way of marking Roman bricks – including the consular date – might be considered a kind of ‘virus’. Rutilius Lupus was the first to use consular dates on his stamps. The speed with which this innovation spread in the brick industry is sometimes seen as the result of a government dictate. So let’s test that.

Move the number-of-nodes slider all the way to the right, to 300 individuals. Move the initial-outbreak-size slider to 1. Hit the setup button, and then the go button. What happens? What are the shapes of the curves compared with when you ran the model from its default starting position? What does this result mean? In this case, I don’t think that the ‘resistant’ category is meaningful in the way it would be for an actual disease; instead, it can be used as a kind of indicator of the underlying dynamics going on. The s-curve that we see when starting from a number of infected individuals is not present when we start from a single individual; the time to model completion is much longer; and as we run this in multiple runs we can see that which node makes a difference – that starting conditions matter. A poorly connected node infects the network much slower than a well-connected node – but a poorly connected node connected to a well-connected other node is a different situation again.

What the model gives us right now is a kind of hypothetical baseline for the behaviour of the dynamics that we’re interested in. The task then is to run these dynamics on an archaeological network. The archaeological network does not literally need to be a network visible in the archaeological materials. It can be a hypothesized network, but in this case your interests are back-to-front in that you would be trying to use some kind of archaeological dynamic to test the impact of your hypothesized network.

Loading Archaeological Network Data into NetLogo

Let us load an actual archaeological network into the NetLogo environment (or at least a network that we can pretend is archaeological). NetLogo comes with code samples within its model library. (It also comes with tutorials that will enable you to teach yourself the ins and outs of building models, which use the textbook by Wilensky and Rand (2015) and the supporting website at http://intro-to-abm.com).

Network Import Example

Look for ‘network import example’. We’re going to build a simple economic model of trading in Roman pots on top of this code example (we are following and then extending a toy model originally devised by Tom Brughmans (2016) as part of his teaching and shared on his blog, archaeologicalnetworks.wordpress.com). When this code example loads up, you are presented with the basic NetLogo interface with a single button. If you right-click on this button, you can select ‘edit’ and see that the underlying code is a single command, import-network. Switch to code panel (clicking on ‘code’, the rightmost tab in the interface) and you’ll see that import-network looks like this:

to import-network
set-default-shape turtles "circle"
layout-circle (sort turtles) (max-pxcor - 1)

This command first clears the interface and resets all variables. It selects a default shape for representing all the agents (which are turtles, demonstrating NetLogo’s descent from the Logo programming language). The next command, import-attributes, loads our node-list (archaeological sites with associated information, perhaps), and then layout-circle distributes the nodes equally around the interface. The connections between our nodes is loaded by import-links, and then the model’s clock is reset.

Go back to the interface panel and click on the import-network button. A network of directed links with the strength of those links is displayed! The weighting of those links could represent the volume of trade from one node to another. It could represent perhaps the degree of similarity of assemblages of the material culture at the two sites. What would you like it to represent? Whatever you choose, you need to keep careful paradata, the information on why and how you make your choices (the info panel in NetLogo is an easy place to do this; hit the ‘edit’ button there and you can record your thoughts as you go). You can find the attributes.txt file in the NetLogo -> models -> code examples directory on your computer. By default, it looks like table A.1.

Table A.1. Contents of the file ‘attributes.txt’.

The first column is the node’s ‘who’ number or its identification; the second is the displayed size of the node (and we can understand ‘size’ to represent all kinds of different information in context); and the final column is the displayed colour for the node. You can open this file in a text editor. Feel free to add some more nodes or change up some of the attributes.

The next file to consider is the links.txt file, which is in the same NetLogo -> models -> code examples directory on your computer. The default contains three columns again, arranged source, target and weight (see table A.2).

Table A.2. Contents of the file ‘links.txt’.

Thus, node 1 is connected to node 4 with a strength of 0.8. This is a directed link. Node 4 is not connected to node 1. That is to say, perhaps node 1 exported 0.8 tonnes of goods to node 4, but node 4 exported nothing to node 1. Node 1’s relationship to node 4 is different than node 4’s relationship to node 1. It might be in our archaeological network that we cannot actually say anything about the directionality of the link, that we only know that node 1 and node 4 were connected. In that case, and using this data, we would have to add another row to this file, as in table A.3.

Table A.3. Adding more data to ‘links.txt’ to denote directionality of the link.

If you added more nodes to the attributes.txt file, you must add those same nodes to the links.txt file and specify some connections and the strength of those connections. Save your work. Back in NetLogo, if you then click on the import-network button in your NetLogo interface window, your new data appears on the screen!

Remember those other commands that are part of the import-network command? They work like this:

to import-attributes
;; This opens the file, so we can use it.
file-open "attributes.txt"
;; Read in all the data in the file data on the line is in this order:
;; node-id attribute1 attribute2
while [not file-at-end?]
;; this reads a single line into a three-item list
let items read-from-string (word "[" file-read-line "]")
create-turtles 1 [
set node-id item 0 items
set size item 1 items
set color item 2 items

In NetLogo, any line that begins with a semicolon (;) is a comment. This code example is well-commented throughout, explaining what each line of code accomplishes. The import-links command works in a similar fashion to import-attributes, iterating over the list of data in the imported files, creating links between the pairs of nodes.

Simulating a Very Simple Pottery Trade on this Default Network

Let’s now get something happening on this network. We’re going to add a very simple trading mechanic (a reimplementation of Tom Brughmans’s 2013 trading model tutorial) on to a network. We are going to assume that our network represents sites that are connected in some kind of trading relationship. We are going to ignore for the moment the weighting between the sites as recorded in the links.txt file. In simulations, it’s always best to start simple and build in just enough complexity as you go, making sure you understand how the model is behaving at each stage. In the model that we are building, we are going to assume that there is a trade threshold, and, in each turn, all of the factors that influence the decision to trade are going to be represented by whether a random number between 0 and 1 is higher than that threshold. That threshold, and whether or not the collection of individual pottery sellers subsumed under the idea of ‘site’ decides to trade, obviously hides a multitude of social dynamics. This is a very stupid model. A far more complex iteration of this basic idea can be found in Graham and Weingart 2015, which builds from this basic idea to develop a laboratory for testing different ideas about trade in the Roman world. Stupid models are nevertheless useful.

The next idea to consider is how trade will be represented. Here, we are going to create a fixed number of ‘pots’ in this world, distribute them randomly across the network, and then trade will be represented by the site sending one pot to one of its neighbours over the link connecting them. One pot leaves, one pot arrives. Before we go any further, what do you think will happen? How will this model behave? The complete code is in appendix B, ‘Pot Trade Model Code’, but below I walk through how we build it:

1. On the model interface tab, where it says ‘button’, click there and select ‘slider’. Then, click the ‘add’ button and click anywhere on the empty space below the import-network button. This opens up a dialogue allowing you to create a new variable that can be set using a slider interface. In the text box where it says ‘global variable’, write trade-threshold. In the ‘maximum’ box put ‘1’ and in the ‘increment’ put ‘0.1’ as the value. Then click ok. You now have a trade threshold value that can be moved from 0 to 1 in increments of 0.1.

2. Make another slider, but call it num-pots and make its maximum value 100, and allow it to increment by 1.

3. Make a new button (click on where you selected the slider, change it to button, and then click on add). In the code window that opens, type go. Tick off the box that says ‘forever’.

Now, you don’t have a go procedure yet, so the error alert will appear across the top of the NetLogo interface. Don’t worry; we’ll fix that in a moment. But now would be a good time to save your work. Go to file -> save and save it somewhere sensible on your machine. Warning! You are not saving it in the models directory where you found the code sample when we began. This means that when you run this model that you’re building and hit the import-network button, NetLogo will not find attributes.txt or links.txt. You can solve this by copying those files and placing them in the same directory where you saved your model, or you can put the full path (location) of the files in the code itself—for example, on my Mac it’s this:

file-open "/Applications/NetLogo 6.0.4/models/Code Examples/attributes.txt"

Save your work often!

4. Go to the code tab in NetLogo. We’re going to give each turtle a pots variable by declaring that ‘pots’ belongs to turtles. We’ll also give each turtle an I.D. as well:

turtles-own [node-id pots]

5. The ‘pots’ variable will store a random value up to the maximum of the num-pots slider you created. We’re going to add this next bit of code to the existing import-network code:

to import-network
set-default-shape turtles "circle"
layout-circle (sort turtles) (max-pxcor - 1)
;; new code that we are adding
ask turtles
[set pots random (num-pots)]

By asking the turtles to set something, we’ve created a new attribute for our turtles, a count of the number of pots that they hold. To begin with, it is a random number anywhere from 0 up to the current setting for that global variable num-pots. Our next task is to tell the turtles how to trade. We’re only going to ask turtles that have pots to do this activity (because otherwise we would get an error, asking turtles who have no pots to trade pots). In the code window, right after the end that finished the import-network routine, we’ll start a new routine that we’ll call trade-pots.

to trade-pots
ask one-of turtles with [pots > 0]
[ if random-float 1 > trade-threshold
[set pots pots - 1
ask one-of in-link-neighbors
[ set pots pots + 1 ]

The first line addresses what comes after only to those turtles that have any pots. The next line has the turtle see if it’s time for them to trade (i.e., that the random number they’ve drawn is higher than the trade-threshold we set using our slider over on the interface page). If that condition is true, then the turtle immediately deducts one pot from its inventory and asks one of the other turtles to which it is connected to increase its inventory by one. You can see that that trade-threshold rule covers a lot of very complicated social mechanisms. Would it help to think of this as a kind of tributary empire? If so, what other mechanisms would you want to add to this simple model?

6. Finally, we’ll tell the model how to run, and we’ll also add a slight modification so that the site with the most pots changes its size in proportion to the number of pots that it has. In the code window, right after the end for the import-network routine and before the to trade-pots line, insert your cursor and hit return a few times, to give you space. It is a convention of NetLogo models that the go routine should be laid out before the routines that it calls. Here’s what that routine will look like:

to go
let total-pots sum [pots] of turtles
repeat total-pots
[ trade-pots]
;resize the turtles to demonstrate how many pots they have
ask turtles [set size 0.1 + 5 * sqrt (pots / total-pots)]

We start by creating a temporary variable called ‘total-pots’ which sums up the number of pots we have – remember that when we first create this world with the import-network button, each site gets a random number of pots up to the maximum specified by our slider num-pots. The next line, repeat total-pots, means that we are going to run the next bit, the call to our trade-pots routine, the exact number of times that there are pots in this world. (What might the effect of this decision be? Is it warranted, given the scenario we’re modelling? What other metrics might we use for deciding how many times to trade?) Once all of the trading has finished, NetLogo moves to the next line and has each turtle change its size in proportion to the number of pots it has compared to the total number of pots in the world.

You’ve done it! You’ve built a simulation that loads network data and that simulates a social process that we are interested in. This simple model contains a number of assumptions and simplifications about the world. To answer the question of whether or not these assumptions and simplifications are warranted is to engage with the power of simulation for trying to understand, to deform, our knowledge about the past. We are not simulating the past: we are simulating, we are operationalizing, a story we tell about the past. What story does this model tell? Go over to the interface tab and, before you run the model, save your work. Then, jot down some ideas: what will you see when this model runs?

To run your model, first click on import-network, then click on go. What happens? Is it what you predicted? How would you extend this model? Some things you could try might be to change the number of pots traded in an exchange or perhaps add a ‘money’ variable so that when pots go one way, money goes another.

Game of Thrones Meets Pottery World

I have been making an argument for simulation and archaeogaming as an avenue for enchantment in archaeology, for bringing the artificial life of agent models and simulations into dialogue with archaeologists to find a richer, more reflective and reflexive archaeological practice. One way of doing that is to make the leap from these simulations, these games that play themselves, into games that we can play with these digital golems ourselves. Developing a bit of programming facility with NetLogo does translate to other kinds of programming languages; my experiments with Andreas Angourakis translating the Artificial Anasazi model (bundled with NetLogo) into a first-person experience in the Unity game engine involved a lot of translating from NetLogo to the C# language. It also highlighted numerous assumptions and other questions of the simulation, which is one reason why we should do these acts of translation.

In the same way that trying to operationalize our stories about the past into code in the first place forces us to think clearly and carefully about our assumptions, translating from one coding language to another highlights the way that code itself occludes some ideas but makes others easier to implement. NetLogo, for instance, did not originally ship with built-in commands (or ‘primitives’) that could handle network concepts like links and nodes. These had to be built from other more basic blocks. Now that network concepts are built into NetLogo, we can use these easily, but perhaps these new primitives implement particular understandings of network science that work against the task we are trying to model.

All of that is to say I will not walk you through using the Unity game engine in this book! Instead, we’re going to make a playful addition to our pottery model: a ‘smite’ button to see how resilient this world is. Our smite button will function much like R. R. Martin with regard to his characters in Game of Thrones: just when things start looking up for a character, he kills them off. A crucial difference is we are not going to ask one-of turtles [die] because that would cause the simulation to crash. (Try it for yourself: create a new button, put ask one-of turtles [die] as the code within it. Then, click import-network and go . . . then hit your new smite button. It might work a few times, but then you’ll smite a turtle just as it was executing its instructions, and you’ll crash your simulation. Dismiss the error message; you can then import-network again and start over.)

Instead, we’re going to ask the links to die, and we’re going to slightly reorganize the code so that it won’t crash when the link disappears. Our modified to trade routine looks like this:

to trade-pots
let target one-of turtles with [pots > 0 and count out-link-neighbors > 0]
;ask one-of turtles with [pots > 0 ]
if target != nobody [ask target
[ if random-float 1 > trade-threshold
[set pots pots - 1
ask one-of out-link-neighbors
[ set pots pots + 1 ] ] ]

What we’re doing is making sure that when the turtle is running this routine, it absolutely has a link to someone else. We’re defining a target as an agent with pots and links. If we’ve selected nobody, then we skip this routine and avoid an error message (i.e., if target is not equal to nobody). Having done this, now add a button and put the code ask one-of links [die] into it; you can name this button ‘smite’ or something similar.

Now run your simulation and watch how it reacts to the decay of the network. Sometimes, pots will drain away suddenly to another node; other times, the whole thing just slowly withers. What might this signify for our simulated pottery trade? Instead of smiting these ties between sites, what else could we try? What role are you playing in this simulated world?

Let’s add the ability to increase links in our model. Make a new button and give it this code:

ask one-of turtles [ create-link-to one-of other turtles
[set strength random-float 3
set label strength]

This code creates a new link, sets its strength as some number between 0 and 3, and then displays a label showing that strength. How does this affect the running of your model? Do you suddenly see a site’s pottery drain away? Why is that happening? Finally, let’s use that ‘strength’ number, the value of the link, as a modulating force on our trade. What if we imagined it as representing the difficulty of trade along that particular route? We might want to create some code that essentially says, ‘when trading pots over this route, let a certain fraction be broken and never arrive’. We could implement that like this:

to trade-pots
let target one-of turtles with [pots > 0 and count out-link-neighbors > 0]
if target != nobody and random-float 1 > trade-threshold ;; target represents one site, one turtle at a time that we want to run our trade routine
ask links [set breakage-factor strength]
ask target
let pots-loss 1 + breakage-factor
set pots max (list 0 (pots - pots-loss));; 1 pot plus the
;; breakage - so we're treating the strength of the link as
;; a kind of friction, a certain kind of wastage on that route.
;; We use max and list so that we don’t end up with negative
;; values.
ask one-of out-link-neighbors
[set pots pots + 1] ;; no breakage, only the pot arrives ] ] end

This modified trade routine can be read in English like this:

  • First, we make sure we’re dealing with a site (a turtle) that has pots and has links.
  • If we’ve got such a turtle, and a random number draw is higher than the trade threshold, then we’re going to continue:
  • We ask the links to transfer their strength value to a global bin called ‘breakage-factor’
  • Now ask the turtle that is doing the trade to do the following:
  • Remove at least one-and-a-bit pots from its inventory
  • Ask the other site (turtle) to which it’s connected to upgrade its inventory with the one pot that does arrive.
  • And end.

If you just pasted the trade-pots code over top of your existing to-trade routine, you will get an error because we have to set up that global bin variable, ‘breakage-factor’. You do that by adding breakage-factor to the ‘globals’ list at the top of your model code, like so:

globals [links-list breakage-factor]

Does this make a difference for how the model behaves? Does it change depending on the number of potential pots that we start with? Is there a change in behaviour when a certain number of links are added or removed? Does the code make unwarranted assumptions? Can you change the code to make it better represent ‘trade’? If you’re asking these questions, you are starting to see the power of releasing digital golems to relive the past. I find it very compelling to watch these golems go about their tasks, to ask, Why do they do that – is it a function of my programming or is it an emergent consequence of the story that I have encoded? What have I forgotten in order to make this work? How is it failing, and where is the productive aspect of that failure?

Golems Who Gossip

In chapter 2, ‘Reanimating Networks’, I discussed an early model that I made that simulated the diffusion of information along the Antonine Itineraries. That model was built on an early version of NetLogo that did not have the capacity to represent nodes and links. Instead, I painted paths onto the background that the agents wandered over, and I had them follow the paths, bouncing and jostling their ways along. That technical aspect of the model might be suspect and perhaps had undetermined effects on the rate of information diffusion. In this section, we’re going to reimplement the key features of that model but on a networked representation of space. Once we get it working, I will point you to a source of data that represents the Roman world as a pattern of nodes and links, and I will leave you to import that data and start seeing for yourself: does Graham 2006a hold up?

This aspect of agent-based modelling is perhaps more terrifying than it is enchanting, for who, in all honesty, wants to have their work critically examined in this way and flirt with the possibility that the work is flawed? I am confident that the overall thrust of that piece will hold up, even if the details change. I would also welcome the opportunity to be wrong because that way, well, won’t we have learned something new?

In the Itineraries model, the mechanic modelled was a simple ‘Have you heard the message, yes or no?’. The critical bit was the shape of the interactions. We’ll build this by starting over with the network import code snippet, saved to your workspace with the attributes.txt and links.txt files in the same folder. To build it, I took pieces of code and inspiration from the following examples:
• network import
• communication t-t network example
• link-walking turtles

Take a look at those code snippets and explore what they do. Then we’ll begin. The complete code is in appendix C, ‘Information Diffusion on a Network’.

We’ll start by defining what our turtles are, so that different breeds have different abilities. We need some turtles to represent sites and some to represent the travellers or walkers who are moving over the links between sites. Eventually, we will use the ‘strength’ value of those links as a function to modulate the speed with which the walkers walk. With your basic node-import code open, change up the first three lines that say turtles-own links-own globals to look like this:

breed [nodes node] ;; these are our sites
nodes-own [node-id breed [walkers walker] ;; the turtles who wander around
walkers-own ;; variables that only the walkers own
links-own [strength] ;; a better term might be 'distance'
;; or 'ease of travel' (in case of latter, smaller values =
;; easier, right?
globals [links-list travel-factor]
;; travel factor has to
;;be accessible by turtles of either breed

The ‘breed’ command tells NetLogo what the plural and singular are for the new kind of turtle that you’ve created. Having done that, we can then specify variables that belong only to a particular breed. The next part of our simulation is to put the pieces on the board. We create a new procedure called setup:

to setup
create-walkers num-walkers [
set color red
set shape "person"
set size 2
set location one-of nodes ;; tells our new walker its new home
move-to location ;; moves the walker there directly
set message? false ;; ignorance is bliss
set new-location one-of [link-neighbors] of location
;; gives the walkers a travel goal
set journey-time 1 ;; initial degree of forward
;; movement: 1 patch at a time
ask one-of walkers [ set message? true ]

In the interface window, create a new button and give it the command setup. The num-walkers is a variable set by a slider that you make in the interface window. Set that up now, but make sure that the minimum is 2 – that way, one walker will have heard the message and one will have not heard it. Create-walkers is a command that tells NetLogo to make new turtles but call them walkers; then each walker will have the following variables or commands performed on it as it is made. In this case, each walker is told that its current location is one of the nodes on the network that we imported, and then the walker is transported to that node. Each walker is then given a destination it will be travelling to by selecting one of the nodes its node is connected to. Each walker is told that its initial speed of travelling will be one step at a time. Finally, one walker is given the message.

Golems are going to walk around this network. When the golem who has the message encounters a golem who does not have the message, the ignorant golem is going to become enlightened. That golem in turn will pass the message on. Soon, all of the golems will have heard the message. It’s a direct-touch, direct-message transfer. Is that a reasonable assumption? Given everything you have learned in this section, do you see how you could give a golem a likelihood that they will ignore the message? Garble the message? Sometimes forget to pass it on? What would those situations imply or do for the model? Would it be a better model if we did include these situations? How do you measure better?
Our go routine will look like this:

to go
ask walkers [
move ;; walkers walk
communicate ;; they talk to anyone else who happens to be present
recolor ;; change their color if they've
;; encountered someone with the message
check-if-arrived ;; see if they've arrived at their
;; new destination
if ((count walkers with [message?]) / (count walkers)) * 100 = 100 [stop] ;; ending condition

In each tick of the clock, walkers will move and gossip; they will change their colour so we know they’ve heard the message, and they will check to see if they’ve arrived at their destination. We will also check to see if 100 per cent of the golems on the board have heard the message, in which case the simulation will stop. Then the clock will tick. Create a new button in the interface, give it the command go, and tick off the ‘forever’ button.

Let’s build the move routine. We want the walkers to walk over the links, and we will want the information in that link to be used to make the journey harder or easier, longer or quicker. We’re going to do that by getting each link to pass its information to a global variable which our walkers will access. Here’s an interesting problem though – since we are modulating how far a walker steps in a given tick, it is entirely possible that our walker will overshoot its destination, getting trapped in a cycle of back-and-forth jumping. This is what happens when we tell the walker fd journey-time. So we’ll let the walker slow down sometimes:

to move
face new-location ;; make sure walker is facing its
;; destination at all times
ifelse random-float 1 > 0.5
[fd journey-time]

The ifelse command is our correction to overshooting the target. If they flip a coin heads, they slow down to one step at a time. Otherwise, they travel at the speed suggested by the information in the link. But how do we get the information from the link to the walker? That happens when we tell the walker to choose-destination. That happens once the walker runs the check-if-arrived procedure:

to check-if-arrived
let arrived one-of nodes-here
if arrived = new-location
[set location new-location

Each turn, the walker looks at where they’re at, and if it happens to be a node (nodes-here), they compare it against the new-location variable they used when they were setting off. If it is, they change their location accordingly, which frees them up to select a new destination:

to choose-destination
;; see what places are connected to this one
;; adjust the travel-factor (distance, ease of travel) to the value of the new path/link
;; adjust speed so that it is modulated by this new value
set new-location one-of [link-neighbors] of location
ask (end1 = [location] of me and end2 = [new-location] of me)
(end2 = [location] of me and end1 = [new-location] of me)] [set travel-factor strength]
set journey-time (1 / travel-factor)

The speed of travel is set when the walker chooses the new destination. So what about those gossiping golems?

to communicate
if any? other walkers-here with [message?] ;; hello?
[ set message? true ] ;; if yes, then now I know the message too
end to recolor ;; walker procedure
if message?
[ set color blue ]

In communicate, the walker looks at where it is and checks to see if another walker is present. If there is and that walker has the message (its message? variable is set to true), then the walker changes its message variable to be true as well. Golems who’ve heard the message turn blue.

The model is now built and, conceptually, is very similar to the original model I built in 2006. Do you see any assumptions that seem intolerable to you? Do you see assumptions for which you could imagine a reasonable archaeological or historical rationale? How would you test alternative assumptions? What do you think will happen as the model runs? Go to the interface tab and hit the setup button and then the go button.

The default network reaches a 100 per cent golems-who’ve-heard-the-message state rather quickly. Can you add a ‘kill links’ button, or a ‘build links’ button, knowing what you now know about NetLogo? How does the model respond to adding links? The real test of whether or not my Itineraries model holds up is in the results – the time for full message penetration – given different network geographies. Meeks, Scheidel, Weiland and Arcenas (2014) make the underlying network data for their ORBIS representation of the Roman world (http://orbis.stanford.edu) in node and link tables that can be downloaded at http://purl.stanford.edu/mn425tz9757. Download that data and see if you can get it to import into the model we have just built. Don’t worry if the display looks crowded; golems only move along links (although there are ways of making the network layout cleanly, as documented in the NetLogo documentation). Can you extract networks that cover Roman Britain? Roman Spain? Roman France? Roman Italy? Do your findings agree with mine?


The code throughout these appendices builds upon, uses or adapts exemplar code from the following:

Brughmans, Tom. 2016. ‘Network Science with Netlogo Tutorial’. Retrieved 4 November 2019 from https://archaeologicalnetworks.files.wordpress.com/2013/11/netlogo_networks_tutorial_v2.pdf.

Wilensky, Uri. 2018a. ‘Communication T-T Example, Netlogo 6.0.4 Models Library, Code Examples’. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.

———. 2018b. ‘Link-Walking Turtles Example, Netlogo 6.0.4 Models Library, Code Examples’. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.

———. 2018c. ‘Network Import Example, Netlogo 6.0.4 Models Library, Code Examples’. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.

Creative Commons License

This work is licensed under a Creative Commons Attribution 4.0 International License.

Latest from the Blog