Movement Simulation in a Circular Arena

Viewed 284

I wish to accomplish the following with a NetLogo simulation:

Create a bounded circular world with a radius of 10 cm and three zones

  • Outer Zone: define an annulus 1.5 cm from the world boundary (red)
  • Inner Zone: circle with radius = 8 cm (gray)
  • Forbidden Zone: off-limits for all turtles (black)

Locomotory Behavior

  • A turtle has predefined turning angles and movement lengths given in the code below
  • However, I would like to include the probability for a turtle to pause for some normally-distributed duration of n ticks

Edge Following Behavior - Thigmotaxis

A turtle may enter the Outer Zone at will, but the following rules govern its behavior while there:

  • A turtle may escape the Outer Zone if it crosses into the Inner Zone.
  • However, the probability for a turtle to remain within the Inner Zone equals [some normally-distributed probability] per tick.

Generate a position report (2D coordinates) for each turtle on a per tick basis

Export this report(s) as a CSV or TXT file

My code is based on the "Random Walk Example" from the NetLogo library and includes "per tick" rules such as turning angles, movement lengths. I tried to implement the probability of a turtle to pause per tick, but I don't know how to incorporate a duration of n ticks. Inner and outer regions are differently colored patches. Then, we can base the turtle behavior on if pcolor = red [commands go here]. Also, When a turtle hits the world boundary, it tends to become stuck as it squirms to find an escape angle. Rather, is it possible to allow the turtle to "turn into" the curved wall of the world whilst avoiding the Forbidden Zone (black patch color)? Finally, for data export, it is easy enough to save the Commmand Center output, but it is not formatted conveniently for analysis i.e. x, y coordinates in separate columns, etc.

to setup
  clear-all
  resize-world -100 100 -100 100 ; 1 unit = 1 mm
  set-patch-size 2
  ask patches [set pcolor black] ; outside arena | how to designate this patch color as "forbidden"?
  ask patch 0 0 [ask patches in-radius 100  [set pcolor red]] ; outer zone
  ask patch 0 0 [ask patches in-radius 85  [set pcolor gray]] ; inner zone
  create-turtles 10 [set size 15]
  ask turtles [
    set color one-of base-colors ; assign color to each turtle without repeats?
    pen-down
  ]
  reset-ticks
end

to go
  if ticks = 1200 [stop] ; each tick = 1 second
  move-turtles
  tick
end

to move-turtles
  ask turtles [
  if random-float 1 > 0.05 ; 5% probability of pause per tick
  [;equal chance of right or left turn
    if random-float 1 > 0.50 [right random-normal 30 10]
    if random-float 1 < 0.50 [left random-normal 30 10]
  ]
  forward random-normal 10 4
  show list xcor ycor
  ]
end
1 Answers

Technically this is a bunch of (admittedly related) questions, rather than a single question (as is preferred on StackOverflow). But I'll see if I can make a coherent answer.

Here is a runnable version of the final code with all the changes for anyone interested.

First, the simplest way to control turtle movement is to use the patch-ahead primitive to check where they would be moving to, and then changing their destination if necessary. This is easiest when using forward 1 instead of a random amount, so I start with that. I then use face one-of neighbors with [pcolor = red] to get the turtle facing a "good" patch. Again, that only works with the forward 1.

I also added __change-topology false false to prevent a turtle from wrapping around the world, as some of the patches do touch at the edges.

to setup
  clear-all
  resize-world -100 100 -100 100 ; 1 unit = 1 mm
  ; make sure the turtles can't wrap at the patches at the edges that touch
  __change-topology false false
  set-patch-size 2
  ask patches [set pcolor black] ; outside arena | how to designate this patch color as "forbidden"?
  ask patch 0 0 [ask patches in-radius 100  [set pcolor red]] ; outer zone
  ask patch 0 0 [ask patches in-radius 85  [set pcolor gray]] ; inner zone
  create-turtles 10 [set size 15]
  ask turtles [
    set color one-of base-colors ; assign color to each turtle without repeats?
    pen-down
  ]
  reset-ticks
end

to go
  if ticks = 1200 [stop] ; each tick = 1 second
  move-turtles
  tick
end

to move-turtles
  ask turtles [
    if random-float 1 > 0.05 ; 5% probability of pause per tick
    [
      ;equal chance of right or left turn
      if random-float 1 > 0.50 [right random-normal 30 10]
      if random-float 1 < 0.50 [left random-normal 30 10]
    ]
    ; Item #1 - outer zone blocked
    ; if turtles only move `1` at time, they cannot "skip" past a patch
    ; so to prevent moving into the black, all we have to do is point them somewhere else
    if [pcolor] of patch-ahead 1 = black [
      face one-of neighbors with [pcolor = red]
    ]
    forward 1
  ]
end

The second item was a sticky inner zone from the outer. We use the same technique as above with a couple small tweaks. I have no more changes to setup or go, so just the move-turtles is presented from here on out:

to move-turtles
  ask turtles [
    if random-float 1 > 0.05 ; 5% probability of pause per tick
    [
      ;equal chance of right or left turn
      if random-float 1 > 0.50 [right random-normal 30 10]
      if random-float 1 < 0.50 [left random-normal 30 10]
    ]

    ; to control turtle movement, we're going to use `patch-ahead` to check the patch color
    ; before moving.  if turtles only move `1` at time, they cannot "skip" past a patch, which
    ; is necessary to use this technique.

    ; Item #1 - outer zone blocked
    ; so to prevent moving into the black, all we have to do is point them somewhere else
    if [pcolor] of patch-ahead 1 = black [
      face one-of neighbors with [pcolor = red]
    ]

    ; Item #2 - inner zone stickiness
    ; if we're in the outer zone and our target patch is inner zone, don't keep moving that way
    ; if we fail our check
    if
      pcolor = red ; we're in the outer zone
      and [pcolor] of patch-ahead 1 = gray ; our target patch is grey
      and random-float 1 < 0.8 ; our random probability to bounce off
    [
      ; then we bounce off
      face one-of neighbors with [pcolor = red]
    ]

    forward 1
  ]
end

The third item is the "pause" you want the turtles to do. I was unclear how this was supposed to work. First, most simply, is just a chance that the turtle will not act for a tick. Here I store the chance in a paused variable that I use for both turning and then forward. It's very hard to see the result as the ticks run so quickly, but they do pause for a moment.

to move-turtles
  ask turtles [
    ; Item 3a - single tick pause - 5% chance to pause and not move forward
    let paused (random-float 1) <= 0.05
    if not paused
    [
      ;equal chance of right or left turn
      if random-float 1 > 0.50 [right random-normal 30 10]
      if random-float 1 < 0.50 [left random-normal 30 10]
    ]

    ; to control turtle movement, we're going to use `patch-ahead` to check the patch color
    ; before moving.  if turtles only move `1` at time, they cannot "skip" past a patch, which
    ; is necessary to use this technique.

    ; Item #1
    ; so to prevent moving into the black, all we have to do is point them somewhere else
    if [pcolor] of patch-ahead 1 = black [
      face one-of neighbors with [pcolor = red]
    ]

    ; Item #2
    ; if we're in the outer zone and our target patch is inner zone, don't keep moving that way
    ; if we fail our check
    if
      pcolor = red ; we're in the outer zone
      and [pcolor] of patch-ahead 1 = gray ; our target patch is grey
      and random-float 1 < 0.8 ; our random probability to bounce off
    [
      ; then we bounch off
      face one-of neighbors with [pcolor = red]
    ]

    if not paused [
      forward 1
    ]
  ]
end

Since it is hard to see that effect and because you mentioned wanting to pause for n ticks, I also did a version where each turtle tracks how long the pause should be. To do this we need to add a turtles-own [ pause ] variable at the top of the file to count the ticks it sits still for. If a turtle hits the random chance, instead of moving it sets the pause variable and on future ticks will just count that back down to 0 and then start moving again.

to move-turtles
  ask turtles [
    ; `pause = 0` to keep them from spinning in place when paused
    if pause = 0 and random-float 1 > 0.05 ; 5% probability of pause per tick
    [
      ;equal chance of right or left turn
      if random-float 1 > 0.50 [right random-normal 30 10]
      if random-float 1 < 0.50 [left random-normal 30 10]
    ]

    ; to control turtle movement, we're going to use `patch-ahead` to check the patch color
    ; before moving.  if turtles only move `1` at time, they cannot "skip" past a patch, which
    ; is necessary to use this technique.

    ; Item #1 - outer zone barrier
    ; so to prevent moving into the black, all we have to do is point them somewhere else
    if [pcolor] of patch-ahead 1 = black [
      face one-of neighbors with [pcolor = red]
    ]

    ; Item #2 - inner zone stickiness
    ; if we're in the outer zone and our target patch is inner zone, don't keep moving that way
    ; if we fail our check
    if
      pcolor = red ; we're in the outer zone
      and [pcolor] of patch-ahead 1 = gray ; our target patch is grey
      and random-float 1 < 0.8 ; our random probability to bounce off
    [
      ; then we bounch off
      face one-of neighbors with [pcolor = red]
    ]

    ; Item #3b - counting pause
    ; if we are paused, just decrement the pause and take no action
    ifelse pause > 0 [
      set pause (pause - 1)
    ] [
      ifelse random-float 1 > 0.05 [
        ; 95% chance to move forward
        forward 1
      ] [
        ; 5% chance to pause 5 ticks
        set pause 5

      ]
    ]
  ]
end

Your final item was exporting turtle data to CSV or text. You have two options. You can use the CSV extension or you can use BehaviorSpace to run your model as an "experiment". Either will work. You already had the right idea with the show list xcor ycor statement in your move-turtles procedure, you just need to get the data for all turtles to output to CSV or with the BehaviorSpace reporters: [(list who xcor ycor)] of turtles. I include who number so the data is easier to track/sort by turtle.

Edit:

As noted in the comments, the prior version can throw a runtime error. This NOBODY error is because I disabled wrapping, meaning a turtle could face a world edge and do patch-ahead 1 which returns NOBODY and I forgot to check for it. I'll add a check for that, but since you want the variable step length version, let's add that add the same time.

One problem we have using random-normal 10 4 is that it can generate arbitrarily large numbers. (As an aside, it seems to me having a pause for each turtle also makes them move a random amount of distance per time already, I'm not convinced the random distance adds much over that.) If we want the turtle to move more than 1 space at a time, but we want to keep the relatively simple logic of just checking the patches ahead, we can just move the forward 1 into a loop, decrementing the distance as we go. We also, then, have to move the bounce/sticky check into that loop as we could march from the edge of the black back across the inner border.

I also made a more robust face-red-patch procedure to try to make sure we never jump into the black.

You can run the model online to test it out.

to move-turtles
  ask turtles [
    ; `pause = 0` to keep them from spinning in place when paused
    if pause = 0 and random-float 1 > 0.05 ; 5% probability of pause per tick
    [
      ;equal chance of right or left turn
      if random-float 1 > 0.50 [right random-normal 30 10]
      if random-float 1 < 0.50 [left random-normal 30 10]
    ]

    ; to control turtle movement, we're going to use `patch-ahead` to check the patch color
    ; before moving.  if turtles only move `1` at time, they cannot "skip" past a patch, which
    ; is necessary to use this technique.
    
    ; Item #1 - outer zone barrier
    ; so to prevent moving into the black, all we have to do is point them somewhere else
    if patch-ahead 1 = nobody or [pcolor] of patch-ahead 1 = black [
      face-red-patch
    ]

    ; Item #3 - pause
    ; if we are paused, just decrement the pause and take no action
    ifelse pause > 0 [
      set pause (pause - 1)
    ] [
      ifelse random-float 1 > 0.05 [
        ; 95% chance to move forward        

        ; Item #5 - variable length moves
        let forward-amount random-normal 10 4
        
        while [patch-ahead 1 != nobody and [pcolor] of patch-ahead 1 != black and forward-amount > 0] [

          ; Item #2 - inner zone stickiness
          ; if we're in the outer zone and our target patch is inner zone, don't keep moving that way
          ; if we fail our check
          if 
            pcolor = red ; we're in the outer zone
            and [pcolor] of patch-ahead 1 = gray ; our target patch is grey
            and random-float 1 < 0.8 ; our random probability to bounce off
          [
            ; then we bounce off
            face-red-patch
          ]
          forward 1
          set forward-amount (forward-amount - 1)
        ]
      ] [
        ; 5% chance to pause 5 ticks
        set pause 5
      ]
    ]
  ]
end

to face-red-patch
  ; this kind of looping code can be dangerous, but if we're careful to use it only when we know it
  ; will terminate it should be okay.  Use Tools > Halt if you think it hit a bad state and is in an
  ; infinite loop
  while [patch-ahead 1 = nobody or [pcolor] of patch-ahead 1 != red] [
    set heading random 360
  ]
end
Related