'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-people procedure, 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 is forward 0.1, so I just move forward.
  • Now I enter the avoid-walls procedure. 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-walls and this means that I also need to exit the ask people [...] command block in go. 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-walls in the previous iteration)... however, before I can move in this correct direction, I am again in move-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 move forward 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:

  1. Your people are the agents executing the move-people procedure, so a better name for this procedure would be just move so that you can have ask people [move].
  2. 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 use pcolor in the context of a turtle).
  3. 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 go procedure: 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:

  1. Face the nearest exit.
  2. If the patch ahead of them is clear, they will ignore the while loop and jump directly to forward 1.
  3. 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 while loop 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:

  1. Keep note of the quadrant in which the nearest exit is;
  2. 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