E W
E W

Reputation: 61

Is there a simple way to code for a sequence of multiples of ticks in netlogo?

How do I have a model decrease a variable by some increment every x ticks, without simply coding a long list? It seems like there should be a way to use multiples of ticks, e.g decrease the variable every 20n ticks, where n = 1, 2, 3,..., but I couldn't think of how that would work, so I just created a list.

For example, in the code below, I am decreasing the variable octopamine-level by 1 unit every 20 ticks past first-leader-tick.

if (ticks - first-leader-tick = 20) or (ticks - first-leader-tick = 40) or (ticks - first-leader-tick = 60) or (ticks - first-leader-tick = 80) or (ticks - first-leader-tick = 100) or (ticks - first-leader-tick = 120) or (ticks - first-leader-tick = 140) or (ticks - first-leader-tick = 160) or (ticks - first-leader-tick = 180)
    [set octopamine-level octopamine-level - 1]

However, I am planning on using a much larger variable, which would require a ridiculously long list, so a cleaner method would be very helpful.

Thanks!

Upvotes: 0

Views: 170

Answers (2)

TurtleZero
TurtleZero

Reputation: 1064

Very simply, you can use the MOD operator to do this.

MOD does a division, then returns the remainder.

This has the great side effect of always returning a number between 0 and one less than the divisor.

Even more simply: ticks MOD 20 always gives a number between 0 and 19. 100 mod 20 is 0. 119 mod 20 is 19. 120 mod 20 is zero again. See? Neat, right?

So you could do

If (ticks - first-leader-tick) mod 20 = 0 [ ;; do the thing you do every 20 ticks. ]

Note that this will make it do the thing the first time, too. So adjust your logic accordingly.

Upvotes: 0

Matteo
Matteo

Reputation: 2926

Simple approach #1

You can use a reminder for the next time that the value needs to be updated:

globals [
 next-update
 octopamine-level
]

to setup
 clear-all
 reset-ticks
  
 set octopamine-level 10
 type "initial level of octopamine: " print octopamine-level
  
 set next-update "none"     ; This is needed because, otherwise, the first iteration (i.e. where
                            ; 'ticks = 0') would always satisfy the 'if (ticks = next-update)'
                            ; condition. Doing 'set next-update - 1' would work too, depending on
                            ; which approach you find more relevant and/or elegant.
end

to go
; With this first block of commands I'm just randomly choosing the
; initial tick for starting the reduction.
 if (octopamine-level = 10) AND (random 100 < 1) [
  set octopamine-level octopamine-level - 1
  set next-update ticks + 20
    
  type "ticks = " type ticks type ", octopamine = " type octopamine-level print ", reduction started"
]

 if (ticks = next-update) [
  set octopamine-level octopamine-level - 1
  set next-update ticks + 20
    
  type "ticks = " type ticks type ", octopamine = " print octopamine-level
]

 if (octopamine-level = 0) [stop]

 tick
end

Simple approach #2

If you also want to have the information more readily accessible about how long will it take until the next update / how long has passed since the last update, you can create a counter:

globals [
 counter
 counter-on?          ; See comment to in 'to setup'.
 octopamine-level
]

to setup
 clear-all
 reset-ticks

 set octopamine-level 10
 type "initial level of octopamine: " print octopamine-level
 
 set counter "none"
 set counter-on? FALSE     ; This, together with the condition in which it is used, is needed
                           ; because we cannot simply 'set counter "none"', as that will give
                           ; a runtime error when executing 'set counter counter - 1' (and using
                           ; 'if (is-number? counter) [set counter counter - 1]' doesn't look
                           ; very nice to me).
                           ; Note that you could get rid of all the 'counter-on?' thing and just
                           ; do 'set counter -1' upon setup. This would work, but would also
                           ; execute 'set counter counter - 1' at every tick, bringing 'counter'
                           ; to always more negative numbers before the reduction actually starts -
                           ; which I personally don't really like.
end

to go
; With this first block of commands I'm just randomly choosing the
; initial tick for starting the reduction.
 if (octopamine-level = 10) AND (random 100 < 1) [
  set counter-on? TRUE
  set counter 20
  set octopamine-level octopamine-level - 1
  
  type "ticks = " type ticks type ", octopamine = " type octopamine-level print ", reduction started"  
 ]

 if (counter = 0) [
  set octopamine-level octopamine-level - 1
  set counter 20
  
  type "ticks = " type ticks type ", octopamine = " print octopamine-level
 ]

 if (octopamine-level = 0) [stop]

 if (counter-on?) [
   set counter counter - 1 
 ]
  
 tick
end

Another positive side of this approach is that it makes it simple to pause and resume the counter anytime you want if you ever need to, simply by using set counter-on? FALSE and set counter-on? TRUE.

Less simple approach

To take an approach that looks more similar to your line of thought (but more convoluted than the previous options), this kind of need can also be addressed by using remainders, and in NetLogo you can use remainder indeed (check here).

You can use the following routine to perform a command every 20 ticks.

if (remainder ticks 20 = remainder first-leader-tick 20) [
 set octopamine-level octopamine-level - 1
]

Overall, performing the type of adjustments that I discussed for the other approaches earlier, it would look something like:

globals [
 first-leader-tick
 reduction-on?
 octopamine-level
]

to setup
 clear-all
 reset-ticks
  
 set octopamine-level 10
 type "initial level of octopamine: " print octopamine-level
  
 set reduction-on? FALSE
end

to go
; With this first block of commands I'm just randomly choosing the
; initial tick for starting the reduction.
 if (octopamine-level = 10) AND (random 100 < 1) [
  set reduction-on? TRUE
  set first-leader-tick ticks
  set octopamine-level octopamine-level - 1
    
  type "ticks = " type ticks type ", octopamine = " type octopamine-level print ", reduction started"
 ]
  
 if (reduction-on?) AND (ticks > first-leader-tick) AND (remainder ticks 20 = remainder first-leader-tick 20) [
  set octopamine-level octopamine-level - 1
  
  type "ticks = " type ticks type ", octopamine = " print octopamine-level
 ]
  
 if (octopamine-level = 0) [stop]
  
 tick
end

The (reduction-on?) condition is needed because otherwise tick 0 will always pass the test (in fact it is true that remainder 20 20 = remainder 0 20).

The (ticks > first-leader-tick) condition is needed because (remainder ticks 20 = remainder first-leader-tick 20) is true also when the reduction starts (i.e. when ticks = first-leader-tick), which would bring to execute twice set octopamine-level octopamine-level - 1 on the tick when the reduction starts (once in the first if-block and once in the second if-block).

Alternatively, if you want you can get rid of the (ticks > first-leader-tick) condition by either:

  • removing the set octopamine-level octopamine-level - 1 line from the first if-block;
  • bringing the second if-block (the one where remainders are evaluated) before the first if-block (the one where the reduction is initiated).

Upvotes: 1

Related Questions