Reputation: 25
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
Reputation: 2926
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.
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:
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.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.to-report condition
and the global variable block-now
exist mainly for readability.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
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.
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