In Tutorial #2, you learned how to use agent monitors and command centers to inspect and modify agents and make them do things. Now you're ready to learn about the real heart of a NetLogo Model: the Procedures tab. This tutorial leads you through the process of building a complete model, built up stage by stage, with every step explained along the way.
To start a new model, select "New" from the the File menu. Then begin making your model by creating a once-button called 'setup'.
Here's how to make the button:
Now you have a button called 'setup'. It will execute the procedure 'setup' when pressed, which once we define it, will do just that -- set up the NetLogo world the way we want it.
At this point, the Errors tab will turn red. That's because there is no procedure called 'setup'! If you want to see the actual error message, switch to the Errors tab:
Now switch to the Procedures Tab and create the 'setup' procedure like this:
One line at a time:
to setup begins defining a procedure named "setup".
ca is short for clear-all (you can also spell it out if you want). This command will blank out the screen, initialize any variables you might have to 0, and kill all turtles. Basically, it wipes the slate clean for a new run of the project.
crt 100 will then create 100 turtles. Each of these turtles will begin on the center patch (at location 0,0). That's why you only see what looks like one turtle on the screen; they're all on top of each other -- lots of turtles can share the same patch. Only the last turtle to arrive on the patch is visible. Each of these newly-created turtles has its own color, evenly distributed through the range of NetLogo colors, and its own heading, evenly distributed around the circle.
ask turtles [ ... ] tells each turtle to execute, independently, the instructions inside the brackets. Note that crt is not in brackets. If the agent is not specified, the observer executes it.
fd (random screen-edge-x) is really a pair of commands. Each turtle will first evaluate the expression random screen-edge-x which will return a random value between 0 and 'screen-edge-x' (the dimension from the center to the edge of the screen along the x-axis, maybe 20 or so). It then takes this number, and goes fd (short for forward) that number of step, in the direction of its heading.
end ends the definition of the "setup" procedure.
Press your 'setup' button when you've written the code. You will see the turtles quickly spread out in a rough cluster:
Notice the density distribution of the turtles on the Graphics Window. Press 'setup' a couple more times, and watch how the turtles' arrangement changes. Can you think of other ways to randomly distribute the turtles over the screen? (There are lots of ways.) Note that if a turtle moves off the screen, it "wraps", that is, comes in the other side. When you're satisfied that the button does what you want, it's time to write your next procedure.
Make a forever-button called 'go'. Again, begin by creating a button, but this time check the "forever" checkbox in the edit dialog.
Then write its procedure:
to go move-turtles end
But what is move-turtles? Is it a primitive (built in to NetLogo), like fd is? No, it's a procedure that you're about to write, right after the go procedure:
to move-turtles
ask turtles [
rt random 360
fd 1
]
end
Line by line:
ask turtles [ ] says that each turtle should execute the commands in the brackets.
rt random 360 is another two-step command. First, each turtle picks a random integer between 0 and 359 (random doesn't include the number you give it). Then it turns right ("rt" is short for "right turn") that far. Heading is measured in degrees, clockwise around the circle, starting with 0 degrees at twelve o'clock.
fd 1: After each turtle does that, it moves forward one step in this new direction.
Why couldn't we have just written that in go? We could, but during the course of building your project, it's likely that you'll add many other parts. We'd like to keep go as simple as possible, so that it is easy to understand. It could include many other things you want to have happen as the model runs, such as calculating something or plotting the results. Each of these sub-procedures could have its own name.
The 'go' button is a forever-button, meaning that it will continually execute its code until you shut it off (by clicking on it again). After you have pressed 'setup' once, to create the turtles, press the 'go' button. Watch what happens. Turn it off, and you'll see that all turtles stop in their tracks.
We suggest you start experimenting with other turtle commands. Type commands into the Command Window (like set color red), or add them to setup, go, or move-turtles. Note that when you enter commands in the Command Center, you must choose T>, P>, or O> in the popup menu on the left, depending on which agents are going to execute the commands. You can also use the tab key, which is more convenient than using the popup menu. T>commands is identical to O> ask turtles [commands ], and P>commands is identical to O> ask patches [ commands].
You might try typing T> pendown into the Command Center, or changing set heading (random 360) to lt (random 45). Or, instead of saying crt 100 in setup, say crt number, where number is a slider variable, and create a slider from the toolbar just as you created the buttons. Play around. It's easy and the results are immediate and visible -- one of NetLogo's many strengths. Regardless, the tutorial project continues...
Now we've got 100 turtles aimlessly moving around, completely unaware of anything else around them. Let's make things a little more interesting by giving these turtles a nice background against which to move. Go back to the 'setup' procedure. We can rewrite it as follows:
patches-own [elevation]
to setup
ca
setup-patches
setup-turtles
end
to setup-patches
ask patches
[ set elevation (random 10000) ]
diffuse elevation 1
ask patches
[ set pcolor scale-color green elevation 1000 9000 ]
end
to setup-turtles
crt 100
ask turtles
[ fd (random screen-edge-x) ]
end
The line at the top, patches-own [elevation] declares that we have a variable for the patches, called elevation. Our setup-patches procedure then uses this variable. First, each patch picks a random number between 0 and 9999, inclusive, and sets its elevation value to that number.
We then use an observer primitive, diffuse, that essentially smooths out the distribution of this variable over the neighboring patches.
We can see the effect of diffuse in the next command, which uses scale-color, a reporter uses the different values of elevation to assign colors to the patches. In this case, we're assigning different shades of green to all the patches. (Don't worry about the numbers given to diffuse and scale-color just yet...) The larger elevation is, the lighter the shade of green. Low values of elevation will result in darker shades.
Setup-turtles is familiar, being exactly what we were doing in our old setup.
Type this in and press the 'setup' button. Voila --a lush NetLogo patch landscape.
Here's how you can see how diffuse works. Return to the Procedures Window, and 'comment-out' the diffuse command, by adding a semicolon in front of it like this:
;diffuse elevation 1
Press 'setup' again -- doesn't look as good, does it? This is because, as mentioned above, diffuse has each patch share its value of elevation with all its neighbors, by having every patch reset its value of elevation to a new value that depends on the value of elevation all around it. Don't worry if you don't exactly understand what's going on here. You can read about it by going to the Primitives Dictionary; and it may help to toy with the values being passed to it, and see what happens.
We're now prepared to create some kind of dialog between the turtles and the patches. In fact, we even have an idea for a project here. Notice that we called the patch variable 'elevation', and that our landscape sort of looks topographical? We're going to have our turtles do what is called 'hill-climbing', where every turtle seeks to find the highest elevation it can.
We'll now explain the notion of compound commands -- a feature that makes NetLogo quite different from StarLogoT (and somewhat closer to natural language). Go to the Command Center, and type O> show max values-from patches [elevation] and show min values-from patches [elevation]. These two reporters will, respectively, search over all the patches to return to you the highest elevation and the lowest. These commands work like this (you can read about them in the NetLogo Dictionary too):
Look up 'values-from' in the dictionary. It shows "values-from AGENTSET [expression]" and says it returns a list. In this case, it looks at the expression (elevation) for each agent in the agentset (patches) and returns all of these as a list of elevations.
Look up 'min' in the dictionary. It shows "min list" and says it's a reporter. So it takes the list of elevations and reports the smallest value.
'Show' displays this value in the command center. And there you have it. We will use this compound command in our model.
One last thing before we return to the coding. Those two values we just read, the highest and the lowest elevations, might be nice to know. Rather than have to retype that every time, let's use a shortcut. First, at the top of your code (right after the 'patches-own' declaration), declare two global variables as such:
globals [highest ;; the highest patch elevation
lowest] ;; the lowest patch elevation
and in setup-patches, write:
to setup-patches
ask patches
[ set elevation (random 10000) ]
diffuse elevation 1
ask patches
[ set pcolor scale-color green elevation 1000 9000 ]
set highest max values-from patches [elevation]
set lowest min values-from patches [elevation]
ask patches [
if (elevation > (highest - 100))
[set pcolor white]
if (elevation < (lowest + 100))
[set pcolor black] ]
end
Now we have saved the highest and lowest points in our terrain and displayed them graphically.
Look at the last two statements, the 'if-statements'. Each patch looks at both of these statements, and compares its own value of elevation to our global variables highest and lowest. If the comparison evaluates to 'true', the patch executes the list of commands inside the brackets. In this case, it simply changes its color. If the comparison evaluates to 'false', the patch skips over the commands.
What this does is that all patches whose value of elevation is NEAR to the highest (within about 1% for our values) change their color to white, and all patches whose values are NEAR to the lowest become black. This is so that they'll be easier to see. You can make a couple of quick changes here if you wish -- they won't affect the rest of the model. Instead of saying 'set pcolor white' and 'set pcolor black', you can say 'set pcolor blue' and 'set pcolor red', or whatever other colors you wish. Also, you can change the range of 'highest peaks' and 'lowest peaks' by changing the number 100 to some other number.
After this, create two monitors in the Interface Window with the Toolbar. (You make them just like buttons and sliders, using the monitor icon on the Toolbar.) Name one of them 'highest' and one of them 'lowest'. Now every time you click 'setup' and redistribute the values of elevation, you'll know exactly what the highest values are, and where they can be found.
Okay. Finally we're ready to start hill-climbing. To rehash: we've got some turtles randomly spread out from the origin; and we've got a landscape of patches, whose primary attribute is their elevation. Lastly, we have two kinds of tools to help us understand the patch landscape: each patch has a color, depending on its value of elevation, and we have a pair of monitors telling us what the highest peak and lowest valley are. What we need now is for the turtles to wander around, each trying to get to the patch that has the highest elevation.
Let's try a simple (i.e. naive) algorithm first. We'll assume a three things: 1), that the turtles cannot see ahead farther than just one patch; 2), that each turtle can move only one square each turn; and 3), that turtles are blissfully ignorant of each other. Before, we had a procedure move-turtles like this:
to move-turtles
ask turtles [
set heading (random 360)
fd 1
]
end
But now we don't want them to move randomly about. We want each turtle to look at the elevation of each patch around it, and move to the patch with the highest elevation (i.e., execute a greedy search, if you will). If none of the patches around it have a higher elevation than the patch it is on, it'll stay put. Here's the code:
;; performs a greedy-search of radius 1
;; for highest elevation
to move-to-local-max ;; call this procedure from 'go'
ask turtles [
set heading uphill elevation
if ( elevation-of patch-at dx dy > elevation )
[ fd 1 ]
]
end
There are five new commands here: 'uphill', 'elevation-of', 'patch-at', and 'dx' and 'dy'. 'uphill elevation' finds the heading to the patch with the highest value of elevation in the patches in a one-patch radius of the turtle. Then through the use of the command 'set heading', the turtle sets its heading to that direction. 'elevation-of patch-at dx dy' has each turtle looks at the variable elevation in the patch on which the turtle would be if it went forward 1. If the test is true, the turtle moves itself forward 1. (The test is necessary because if the turtle is already on the peak, we don't want it to move off it!)
Go ahead and type that in, but before you test it out by pressing the 'go' button, ask yourself this question: what do you think will happen? Try and predict how a turtle will move, where it will go, and how long it'll take to get there. When you're all set, press the button and see for yourself.
Not too exciting. Surprised? Try to understand why the turtles converge to their peaks so quickly. Maybe you don't believe the algorithm we've chosen 'works correctly'. There's a simple change you can make to test it: rewrite setup-patches so that it says:
to setup-patches
ask patches
[
set elevation pycor
set pcolor scale-color green elevation
(0 - screen-edge-y) screen-edge-y
]
end
Now run the project. See that the turtles all head for the highest elevation -- the top of the screen.
Another common tool to see what's going on is to write T> pd in the Command Center. Then each turtle traces its path with its color, and you can observe its path.
Our turtles rapidly arrive at local maxima in our landscape. Local maxima and minima abound in a randomly generated landscape like this one. Our goal is to still get the turtles to find an 'optimal maximum', one of the white patches. We need to start refining the model.
Part of the problem is that our terrain is terribly lumpy. Every patch picked a random elevation, and then we diffused these values one time. This really doesn't give us a continuous spread of elevation across the graphics window, as you might have noticed. We can correct this problem to an arbitrary degree by diffusing more often. Replace the line:
diffuse elevation 1
with:
repeat 5 [ diffuse elevation 1 ]
The repeat primitive is another way for NetLogo to loop besides the forever-button. Repeat takes a number (here, 5) and a command list (here, the diffuse command), and executes the command list that number of times (here, five times). Try it out, and look at the landscape (i.e. press 'setup' and see what you think). Then, press 'go' and watch the turtles' performance. (Remember that the lighter the patch, the greater the elevation.)
Obviously, fewer peaks make for an improvement in the turtles' performance. On the other hand, maybe you feel like this is cheating -- the turtles really aren't doing any better, it's just that their problem was made easier. True enough. If you call repeat with an even higher number (40 or so), you'll end up with only a handful of peaks, as the values become more evenly distributed with every successive call. Watch your monitor values.
In order to specify how 'smooth' you want your world to be, let's make it easier to try different values. Maybe one time you'll want the turtles to try and 'solve a hard world', and maybe another time you'll just want to look at an easy landscape. So we'll make a slider variable. Create a slider in the Interface Window and call it "smoothness" in the editing box. The minimum can be 0, and the maximum can be 50 or so. Then change your code to:
repeat smoothness [ diffuse elevation 1 ]
Experiment with the turtles' performance in different worlds.
We still haven't even begun to solve the greedy-search problem, though. Before trying something else, it'd be nice if we could have some other, more precise method for evaluating the turtles' performance; watching little arrows writhe about in the Graphics Window only helps so much. Fortunately, NetLogo allows us to plot data as we go along.
To make plotting work, we'll need to create a plot in the Interface tab, and set some settings in it. Then we'll add one more procedure to the Procedures tab, which will update the plot for us.
Let's do the Procedures tab part first. Change go to call the new procedure we're about to add:
to go move-turtles do-plots end
Then add the new procedure:
to do-plots
set-current-plot "Turtles at Peaks"
plot count turtles with
[ elevation >= (highest - 100) ]
end
What we're plotting is the number of turtles who've reached our 'peak-zone' (within 1% of the highest elevation) at some given time.
Note that we use the plot primitive to add the next point to a plot, but before doing that, we need to tell NetLogo which plot we want, since later our model might have more than one plot.
Thus we're plotting the number of turtles within 100 units of our maximum elevation at some given point in time. The plot command moves the current plot pen to the point that has x- coordinate equal to 1 greater than the old x- coordinate and y-coordinate equal to the value given in the plot command (in this case, the number of turtles with [ elevation >= (highest - 100) ]). Then the plot command draws a line from the current position of the plot pen to the last point it was on.
In order for set-current-plot "Turtles at Peaks" to work, you'll have to add a plot to your model in the Interface tab, then edit it so its name is "Turtles at Peaks", the exact same name used in the code. Even one extra space will throw it off -- it really must be exactly the same in both places.
Note that when you create the plot you can set the minimum and maximum values on the x and y axes, and the color of the default plot pen (pick any color you like). You'll want to leave the "Autoplot?" checkbox checked, so that if anything you plot exceeds the minimum and maximum values for the axes, the axes will automatically grow so you can see the whole plot.
Now reset the project and run it again. You can now watch the plot be created as the model is running:
You might try running the model several times under different settings (i.e. different values of smoothness) and watch how fast the plot converges to some value, and what fraction of the turtles make it to the top.
There are a few miscellaneous bugs and quirks you might have already noticed. Here are some quick changes you can make: First, we have a green landscape -- a naturally green turtle is going to be hard to see. In the ask turtles block in 'setup-turtles', you can say:
if (color = green) [ set color red ]
Second, instead of always using 100 turtles, you can have a variable number of turtles. Make a slider variable (say, 'number'):
Then instead of 'crt 100', you can type:
crt number
How does using more or fewer turtles affect the success value displayed by the plot?
Third, when all the turtles have found their local maxima, wouldn't it be nice for the model to stop? This requires a few lines of code.
globals [ highest ;; maximum patch elevation lowest ;; minimum patch elevation turtles-moved? ;; so we know when to stop the model ]
to go set turtles-moved? false move-to-local-max do-plots if (not turtles-moved?) [ stop ] end
to move-to-local-max
ask turtles [
set heading uphill elevation
if ( elevation-of patch-at dx dy > elevation )
[
fd 1
set turtles-moved? true
]
]
end
Finally, what rules can you think of that would help turtles escape from lower peaks and all get to the highest ones? Try writing them.
So now you have a nice framework for exploring this problem of hill-climbing, using all sorts of NetLogo modeling features: buttons, sliders, monitors, plots, and the graphics window. You've even written a quick and dirty procedure to give the turtles something to do. And that's where this tutorial leaves off.
You can continue with this model if you'd like, experimenting with different variables and algorithms to see what works the best (what makes the most turtles reach the peaks).
Alternatively, you can look at other models, or go ahead and build you own. You don't even have to model anything. It can be pleasant just to watch patches and turtles forming patterns, or whatever. Hopefully you will have learned a few things, both in terms of syntax and general methodology for model- building. The entire code as created above is shown below.
The complete model is also available in NetLogo's Models Library, in the Code Examples section. It's called "Tutorial Example".
patches-own [ elevation ] ;; elevation of the patch
globals [
highest ;; maximum patch elevation
lowest ;; minimum patch elevation
turtles-moved? ;; so we know when to stop the model
]
;; We also have two slider variables, 'number' and
;; 'smoothness'. 'number' determines the number of
;; turtles, and 'smoothness' determines how erratic
;; terrain becomes during diffusion of 'elevation'.
;; resets everything
to setup
ca
setup-patches
setup-turtles
end
;; creates a random landscape of patch elevations
to setup-patches
ask patches [set elevation (random 10000) ]
repeat smoothness [diffuse elevation 1 ]
ask patches
[ set pcolor scale-color green elevation 1000 9000 ]
set highest max values-from patches [elevation]
set lowest min values-from patches [elevation]
ask patches [
if (elevation > (highest - 100))
[set pcolor white]
if (elevation < (lowest + 100))
[set pcolor black]
]
end
;; initializes the turtles
to setup-turtles
crt number
ask turtles [
if (color = green) [ set color red ]
fd (random screen-edge-x)
]
end
;; RUN-TIME PROCEDURES
;; main program control
to go
set turtles-moved? false
move-to-local-max
do-plots
if (not turtles-moved?)
[ stop ]
end
;; performs a greedy-search of radius 1
;; for highest elevation
to move-to-local-max
ask turtles [
set heading uphill elevation
if ( elevation-of patch-at dx dy > elevation )
[
fd 1
set turtles-moved? true
]
]
end
to do-plots
set-current-plot "Turtles at Peaks"
plot count turtles with
[ elevation >= (highest - 100) ]
end