'How to make people go towards a specific patch and avoid certain patches on the way to the destination?
I am building an evacuation model for my university lecture hall. The problem I am facing is that the people always walk over the gray patch that is not supposed to be walked over even though I have included the procedure to avoid walls in the code. This is the current code for my evacuation model. Also is it possible for the turtles to stop moving when arrived at the exit which in this case is the green patch?
breed [people person]
to setup
clear-all
reset-ticks
setup-patches
setup-people
end
to setup-people
set-default-shape people "person"
ask n-of n-people (patches with [pcolor = white]) [sprout-people 1]
ask people [set color cyan]
end
to setup-patches
draw-wall
draw-exit
;change the color of the floor for better visibility
ask patches[
if pcolor = black [set pcolor white ]
]
end
to draw-wall
; Make 4 boundary walls
ask patches with [ pycor >= -25 and pycor >= 25][ set pcolor gray ]
ask patches with [ pycor <= -25 and pycor <= 25][ set pcolor gray ]
ask patches with [ pxcor >= -25 and pxcor >= 25][ set pcolor gray ]
ask patches with [ pxcor <= -25 and pxcor <= 25][ set pcolor gray ]
; make rows of walls inside that look like seats in a lecture hall
; left rows of chairs
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 20][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 18][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 16][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 14][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 12][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 10][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 8][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 6][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 4][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 2][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = 0][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = -2][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = -4][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = -6][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = -8][set pcolor gray]
ask patches with [pxcor <= 21 and pxcor >= 13 and pycor = -10][set pcolor gray]
; middle rows of chairs
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 20][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 18][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 16][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 14][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 12][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 10][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 8][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 6][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 4][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 2][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = 0][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = -2][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = -4][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = -6][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = -8][set pcolor gray]
ask patches with [pxcor >= -9 and pxcor <= 9 and pycor = -10][set pcolor gray]
; right rows of chairs
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 20][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 18][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 16][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 14][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 12][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 10][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 8][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 6][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 4][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 2][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = 0][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = -2][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = -4][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = -6][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = -8][set pcolor gray]
ask patches with [pxcor >= -21 and pxcor <= -13 and pycor = -10][set pcolor gray]
end
to draw-exit
; Setting 4 exits assuming all UTAR lecture halls have 4 exits
; two at the top left and right and two at the bottom left and right
ask patches with [pxcor <= 23 and pxcor >= 21 and pycor = 25][set pcolor green]
ask patches with [pxcor >= -23 and pxcor <= -21 and pycor = 25][set pcolor green]
ask patches with [pxcor <= 23 and pxcor >= 21 and pycor = -25][set pcolor green]
ask patches with [pxcor >= -23 and pxcor <= -21 and pycor = -25][set pcolor green]
end
to go
let hall patches with [pycor <= 0 and pycor >= -25 and pxcor <= 0 and pxcor >= -25 ]
ask people[
move-people
avoid-walls
]
tick
end
to move-people
face min-one-of patches with [pcolor = green ] [distance myself ]
fd 0.1
end
to avoid-walls
ifelse [pcolor] of patch-ahead 1 = gray
[lt random-float 360 ] ;; we see a gray patch ahead of us. We turn a random amount
[fd 0.1] ;; otherwise, it is safe to move forward
end
Solution 1:[1]
This will be a long answer but I want to guide you step by step.
Let's start from why you are facing an unwanted behaviour.
In go, first you ask people to move (move-people) and only later you ask them to avoid walls (avoid-walls).
Even looking at it more closely, you can see that your avoid-walls procedure is not having any effect on your people's walk. Let's read your code from the point of view of your agents:
- I am a person and I enter the
move-peopleprocedure, which makes me face the nearest exit. There is a wall in front of me, but nothing in the code is telling me that I should care about it: in fact the next line of code isforward 0.1, so I just move forward. - Now I enter the
avoid-wallsprocedure. As I said, there is a wall in front of me, so I randomly change my heading. Ah, there is no wall in front of me anymore! However I will not move forward in this new direction, because there is no line of code telling me to do it now that I changed my heading. - I exit
avoid-wallsand this means that I also need to exit theask people [...]command block ingo. I have finished my actions for this iteration. - Here we are in the next iteration. I am now heading towards a patch without walls (because I randomly selected this heading from
avoid-wallsin the previous iteration)... however, before I can move in this correct direction, I am again inmove-people, and I am asked again to change my heading to face the nearest green patch. This means that I am back to the initial situation, when I had a wall in front of me! The next line of code is asking me to moveforward 0.1, so I continue moving on the wall... - and on, and on, and on...
So let's see, step by step, how you can solve this.
First of all, let's tidy up a couple of things:
- Your
peopleare the agents executing themove-peopleprocedure, so a better name for this procedure would be justmoveso that you can haveask people [move]. - In order to have agents stop moving when they reached the exit, you can simply make sure that only those agents who are not on a green patch will be asked to move:
ask people with [pcolor != green] [move](note that turtles can directly read and modify patches-own variables of the patch they are standing on, this is why we are able to usepcolorin the context of a turtle). - You will also probably want to have your model stop when everyone reached an exit, so you can add a stop condition at the beginning of the
goprocedure:if (not any? people with [pcolor != green]) [stop].
The result of the three points above will be the following go procedure:
to go
if (not any? people with [pcolor != green]) [stop]
ask people with [pcolor != green] [
move
]
tick
end
Now, back to the content of move.
As you can see, I only have move inside go (i.e. I removed any procedure that is specific to the goal of avoiding walls). This is because, following the journey of a person as we described it above, it is essential that the actions for avoiding walls are not carried out after moving, but upon choosing where to move.
An equally important thing is to make sure that when a person finds a direction where there are no walls, it has to move in that direction before being asked to face the exit again.
Therefore, a first idea could be to do:
to move
face min-one-of patches with [pcolor = green] [distance myself]
while [[pcolor] of patch-ahead 1 = gray] [
right random 360
]
forward 1
end
This way, we make sure that at each iteration of go people will:
- Face the nearest exit.
- If the patch ahead of them is clear, they will ignore the
whileloop and jump directly toforward 1. - If the patch ahead of them has a wall, they will enter the loop and only exit it when they found a clear direction (i.e. when the condition of the
whileloop evaluates as false).
In any case, on each iteration of go every turtle will move exactly by 1 in a direction without walls. On the next iteration of go each turtle will simply start again by facing the nearest exit and testing the wall condition again.
This works, and it is similar to the Look Ahead Example model in the NetLogo library (with the difference that, here, the while loop makes it possible that every turtle moves at every iteration of go, instead of having to spend a full iteration only to change heading if they need to). However, if you try it, you will see that the result is that people who find themselves between the lines of walls will move randomly until they find a way out. This is not very realistic, but we can make it more realistic.
For example, we can make sure that people who find a wall will not move completely randomly to find a way away from the wall, but instead will lean somewhat towards the direction of the nearest exit.
An idea could be to divide the 360° spectrum into four quadrants (0°-89°; 90°-179°; 180°-269°; 270°-359°), so that each person facing a wall can:
- Keep note of the quadrant in which the nearest exit is;
- If there is a wall in front of them, look for a new direction in the same quadrant.
This way, the wandering of people will be less randomic (i.e. we will see less back and forth, and more going around obstacles).
For this purpose, let's create a new reporter called new-direction. To use this we can create a people-own variable called target (to avoid repeating min-one-of patches with [pcolor = green] [distance myself]):
people-own [
target
]
to move
set target min-one-of patches with [pcolor = green] [distance myself]
face target
while [[pcolor] of patch-ahead 1 = gray] [
set heading new-direction
]
forward 1
end
to-report new-direction
let direction-quadrant ceiling (towards target / 90)
if (direction-quadrant = 0) [
set direction-quadrant 1
]
report random 91 + (90 * (direction-quadrant - 1))
end
You can work out the maths yourself and see that new-direction always reports a new heading that is in the same quadrant (including its borders) as the direction towards the nearest exit. Given that this happens inside the while loop, each agent will then exit the loop only once they found a direction that is more or less towards the nearest exit and that can avoid the wall. Once they found it, they move forward.
However you will also see that sometimes people get stuck in a loop: this happens when they find themselves in a location where another step forward causes the nearest exit to change quadrant (as we defined quadrants from the point of view of turtles), and the subsequent step forward causes the nearest exit to go back to the previous quadrant.
We can solve this by making sure that people remember what was the exact patch they were standing on in the previous step (I call it last-patch in the code below). If such patch is the same as the one they are planning to go to now (checked with (patch-ahead 1 = last-patch)), it means that they are stuck in a loop. In that case, instead of executing forward 1 they execute forward -1 which basically means "go backwards by 1". Considering that at this point forward refers to the heading the turtle has acquired after exiting the while block, this means that in this way the turtle is forced outside the loop:
to move
let last-patch patch-at-heading-and-distance (heading) (-1)
set target min-one-of patches with [pcolor = green] [distance myself]
face target
while [[pcolor] of patch-ahead 1 = gray] [
set heading new-direction
]
ifelse (patch-ahead 1 = last-patch)
[forward -1]
[forward 1]
end
Note that this solution is improvable: it works in your scenario but it may not be sufficient with more complicated designs of the hall and walls.
Solution 2:[2]
You run "move" in which the turtles face a direction, then move. Then you run "avoid" in which the turtles look for gray and turn.
So your turtles have already moved before they look for gray.
My advice: aim, then correct, then move. Note that this very simple navigation rule will not move like a person would move.
to aim
;; face nearest green patch
face min-one-of patches with [pcolor = green ] [distance myself ]
end
to correct
;;if about to step on a gray patch, turn away from it
if ([ pcolor ] of patch-ahead .1 = gray)
[ ;; where is the center of the next patch?
let towards-patch-center towards patch-ahead .1
;; calculate a direction away from the center of the patch
let diff subtract-headings towards-patch-center heading
;; turn left (CCW) or right (CW)
if (diff >= 0 ) ;; it's ahead or clockwise
[ set heading heading - 1 ] ;; turn counter-clockwise
if (diff < 0 ) ;; its counter-clockwise
[ set heading heading + 1 ] ;; turn clockwise
]
end
to walk
jump .1
end
to navigate
aim
correct
walk
end
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | TurtleZero |
