James W
James W

Reputation: 25

Netlogo calculate all turtles of a type that are touching

In Netlogo, I have a grid of turtles all touching, and I want to count the number of green turtles (MHC = 3 in code below) that form each cluster of green turtles (amid the other colours). Perhaps I am going about it all wrong, but it seems very very hard.

I have tried while loops designed to start with a single green cell (unconnected to any previous green cluster) and assign a number to its turtles-own variable block. Then each green neighbor in-radius 1 receives the same number, and so on and so on, until each touching green cell receives the same number. Then the next cluster will receive a new number and start over. However, unless it's just a question of bad bracketing, it really doesn't seem to work. Here is the functional code (which just creates the grid of colour changing turtles):

turtles-own[MHC block]

globals[prWound]

to set-up
  clear-all
  reset-ticks
  ask patches [sprout 1 [set color magenta]]
  ask turtles [set MHC 2]
  set prWound 0.0001
end

to rules
  ask turtles with [MHC = 0][set color red]
  ask turtles with [MHC = 1][set color green]
  ask turtles with [MHC = 2][set color magenta]
  ask turtles with [MHC = 3][set color blue]
  ask turtles with [MHC = 4][set color orange]

ask turtles [if random 100 < 1 [set MHC (random 5)] ;vary MHC betwen 0-4,
    set block 0
    if random-float 1 < prWound [ask turtles in-radius 4 [die] die] 
    if any? patches in-radius 1 with [not any? turtles-here] and random 100 < 50 [if random 100 < 2.5 
[set MHC (random 5)] hatch 1 [move-to one-of patches in-radius 1 with [not any? turtles-here]
  ]
  tick
end

to go
  rules
end

Here is the part where I try to add block values that I cannot get to work (added just before the tick):

  ask turtles with [MHC = 1][
    if block = 0 [set block (max([block] of turtles) + 1) ]
    while [any? [turtles with [MHC = 1 and block = 0] in-radius 1] of turtles with [block = [block] of myself]]
[if any? [turtles with [MHC = 1 and block = 0] in-radius 1] of turtles with [block = [block] of myself]
[set block ([block] of myself)]]
  ]

I think the in-radius might be at least one of the problems - I am not sure it can be used this way.

Upvotes: 0

Views: 263

Answers (1)

Matteo
Matteo

Reputation: 2926

Update: simpler approach

I leave my initial reply below unchanged, however see that a much simpler approach can be taken:

to count-blocks
  set block-now 0
  
  ask turtles [set block 0]
  
  while [any? turtles with [condition]] [
   set block-now block-now + 1
   
   ask one-of turtles with [condition] [
     join-and-search 
    ]
  ]
end


to join-and-search
  set block block-now
  
  if any? (turtles-on neighbors) with [condition] [
   ask (turtles-on neighbors) with [condition] [
      join-and-search
    ]
  ]
end


to-report condition
  ifelse (color = green and block = 0)
   [report TRUE]
   [report FALSE]
end

Note that, although while is used only once in this case, to join-and-search in actual fact creates a loop by calling itself, with the recursive call being performed only if any? (turtles on neighbor) with [condition] exist; which makes the candidate? passage (i.e. becoming a candidate, recruiting candidates, stop being a candidate) not required here.

I think that just a warning is due in this case: I don't know if it is best practice to let a procedure call itself. On the one hand, it sounds like something worth flagging; on the other hand, it seems to me that this join-and-search could not be any more problematic than any other loop built with a weird condition.

Initial reply

While trying to solve this, I myself discovered something that I did not consider about in-radius, and surely there lies part of the problem.

Before disclosing that, however, let me say that I am not sure if this in-radius-thing is everything that was wrong with your attempt: by the time I found out about it, I had already taken my approach to the problem.

In general, however, one piece of advice: keep your code as tidy and as readable as possible (including indentation) - it becomes a lot easier to spot where a problem lies.

That said, the main elements of my approach:

  • Two while loops: the first one checks whether there are any eligible turtles in the whole simulation that will initiate a new block; the second one (nested within the first one) checks whether there are any turtles left to be allocated to the current block being evaluated.
  • A candidate? turtles-own variable, which constitutes the condition for the second while loop. When being allocated to a block, each turtle also performs a search of its neighbours. If there are any turtles that should be added to the current block, then these get candidate? = TRUE and the inner loop starts again.
  • Also, I've split the relatively few commands in many procedures with relevant names. This makes both the code more readable but also more scalable: when you're going to expand on the model's variables, agentsets, conditions etc it will be easier to add line of codes to allocated sections and check whether a particular section is working on its own.
  • to-report condition and the global variable block-now exist mainly for readability.
  • As of now, this code re-counts blocks at every go (and blocks may change number between one iteration of go and the other). It will surely be possible to adapt the approach to the case in which you want to keep blocks' numbers across go iterations.
globals [
  prWound
  block-now
]

turtles-own [
 MHC
 block
 candidate?
]


to setup
  clear-all
  reset-ticks
  ask patches [sprout 1 [set color magenta]]
  ask turtles [set MHC 2]
  set prWound 0.0001
end


to go
  rules
  count-blocks
  tick
end


to rules
  ask turtles with [MHC = 0][set color red]
  ask turtles with [MHC = 1][set color green]
  ask turtles with [MHC = 2][set color magenta]
  ask turtles with [MHC = 3][set color blue]
  ask turtles with [MHC = 4][set color orange]

ask turtles [
    if random 100 < 1 [set MHC (random 5)] 
    set block 0
    if random-float 1 < prWound [ask turtles in-radius 4 [die] die]
    if any? patches in-radius 1 with [not any? turtles-here] and random 100 < 50 [
     if random 100 < 2.5 [
       set MHC random 5
      ]
      hatch 1 [move-to one-of patches in-radius 1 with [not any? turtles-here]]
    ]
  ]
end


to count-blocks
  set block-now 0
  
  ask turtles [
   set block 0
  ]
  
  while [any? turtles with [condition]] [start-count-round]
  
end


to start-count-round
  
  set block-now (block-now + 1)
  
  ask turtles [
   set candidate? FALSE
  ]
  
  ask one-of turtles with [condition] [set candidate? TRUE]
  
  while [any? turtles with [candidate?]] [
    ask turtles with [candidate?] [
     join
     search
     conclude 
    ]
  ]
end


to join
  set block block-now
end


to search
  let target (turtles-on neighbors) with [condition and not candidate?]
  ask target [set candidate? TRUE]
end


to conclude
  set candidate? FALSE
end


to-report condition
  ifelse (color = green and block = 0)
   [report TRUE]
   [report FALSE]
end

Before

Click to see image

After

Click to see image

What about in-radius?

While it may seem intuitive that a turtle looking for turtles in-radius 1 will find the turtles standing on any of the immediate neighboring patches, that is not the case: the input number for in-radius is in fact a distance - i.e. not the number of patches that need to be crossed.

In terms of distance, turtles standing on horizontally- or vertically-neighboring patches will be at a distance of 1. Instead, turtles standing on diagonally-neighboring patches will be at a distance of 1.4:

observer> clear-all
observer> ask patch 0 0 [sprout 1]
observer> ask patch 0 1 [sprout 1]
observer> ask patch 1 1 [sprout 1]
observer> ask turtle 0 [show distance turtle 1]
(turtle 0): 1
observer> ask turtle 0 [show distance turtle 2]
(turtle 0): 1.4142135623730951

This is the reason why not even my approach was working until I replaced let target turtles in-radius 1 with [condition and not candidate?] with let target (turtles-on neighbors) with [condition and not candidate?].

Note that you use in-radius twice in the first chunk of code that you shared. While in one case that is just patches in-radius 1, which you can replace with neighbors, in another instance it is turtles in-radius 4. You might want to consider the effect of that distance thing in the latter case.

A final note on the code

Just to make sure: are you sure that the order of things in to rules is what you want? For how things are now, turtles change their MHC value but only change colour in the subsequent round of go (but, by that time, they will have changed MHC again).

Upvotes: 2

Related Questions