Nesta secção vamos escrever o código do modelo dos bichos que apresentámos no capítulo anterior:
Construiremos também a interface gráfica do modelo. Esta tarefa é tão fácil de fazer em NetLogo, que começamos por ela.
No capítulo anterior tivemos a oportunidade de interagir com o modelo através dos elementos de interface. De seguida vamos compreender o que eles significam, como são tratados internamente pelo NetLogo e como usá-los para facilitar o nosso trabalho de programação. Os elementos de interface são introduzidos no modelo através da barra de ferramentas:
Os botões são criados para facilitar a execução automática de comandos através de um único clique. Para criarmos um botão clicamos em e em seguida clicamos na área de trabalho (janela branca do NetLogo). Esta acção abrirá a caixa de diálogo Button como pode ser vista na figura:
Indicamos que o botão chama a rotina preparar e clicamos em OK. A função da rotina preparar é colocar o modelo nas condições iniciais.
Podemos mover ou alterar o tamanho do botão (ou de qualquer outro elemento de interface). Para tal clicamos com o botão direito do rato sobre o botão preparar, seleccionamos Select, clicamos (cf. figura da direita) e de seguida usamos o botão esquerdo do rato para alterar a posição ou o tamanho.
De seguida criamos o botão "executar", cuja função é executar repetidamente a rotina executar. Por essa razão seleccionamos a opção Forever, conforme ilustrado na figura seguinte.
De seguida criamos o botão que limpa tudo. Este botão executa o comando ca (o mesmo que clear-all e que em português significa limpa tudo). Para tornar mais clara a função do botão alteramos o Display name para "limpar tudo", conforme pode ser visto na figura seguinte:
Um cursor define uma variável global acessível a todos os agentes do modelo. Geralmente esta variável é usada para variar os parâmetros do modelo sem a necessidade de reescrever os comandos. Para criarmos um cursor clicamos em e em seguida clicamos na área de trabalho. Esta acção abrirá a caixa de diálogo Slider como pode ser vista na figura:
Nesta caixa indicamos que o nome da variável global é numero-inicial-bichos, que o valor mínimo é 0, o valor máximo é 100, que o incremento é 1 e que o valor por defeito é 35. De seguida criamos o cursor da velocidade:
onde o valor mínimo é 0, o valor máximo é 1, o incremento é 0.1 e o valor por defeito é 0.5. Por fim criamos o cursor da taxa-de-reproducao:
onde o valor mínimo é 0, o valor máximo é 100, o incremento é 1, o valor por defeito é 29 e as unidades são percentagens (%).
Um interruptor define e representa visualmente uma variável lógica global acessível a todos os agentes do modelo. As variáveis lógicas podem tomar os valores true (verdadeiro, quando o interruptor está On) ou false (falso, quando o interruptor está Off). Para criarmos um interruptor clicamos em e em seguida clicamos na área de trabalho. Esta acção abrirá a caixa de diálogo Switch como pode ser vista na figura:
e à variável global chamamos competicao?.
Um monitor mostra o valor dinâmico de uma variável. Para criarmos um monitor clicamos em e em seguida clicamos na área de trabalho. Esta acção abrirá a caixa de diálogo Monitor como pode ser vista na figura:
e usamos este monitor para mostrar a variável geracao. Como a geracao é um número inteiro, indicamos que tem 0 casas decimais. Criamos também o monitor populacao (que é o número total de bichos), o qual é um número inteiro, pelo que também tem 0 casas decimais.
Um gráfico permite desenhar um gráfico dinâmico a partir dos dados gerados pelo modelo. Para criarmos um gráfico clicamos em e em seguida clicamos na área de trabalho. Esta acção abrirá a caixa de diálogo Monitor como pode ser vista na figura:
onde especificámos que o eixo dos x é a variável geracao, que esta inicialmente varia entre 0.0 e 10.0 e onde especificámos que o eixo dos y é a variável populacao e que esta inicialmente varia entre 0.0 e 1000.0. O NetLogo é bastante versátil, pois se durante a execução do modelo os pontos do gráfico saírem fora deste, este reajusta-se automaticamente para que todos os pontos fiquem lá dentro.
Uma vez construída a interface gráfica, passemos ao código do modelo.
O modelo está dividido nas seguintes secções:
geracao | contém o número de iterações já decorridas |
populacao | contém o número de bichos da presente geração |
preparar | limpa a janela gráfica, inicia as variáveis globais e desenha-as na janela gráfica; cria todas as turtles e posiciona-as de forma aleatória. |
executar | itera as regras de comportamento das turtles: -movimento, reprodução e choque; actualiza as variáveis globais e desenha-as na janela gráfica. |
movimento | cada turtle move-se para um ponto aleatório a uma distância máxima v. |
reproducao | cada turtle pode dar origem a outra de acordo com a taxa de reprodução escolhida. |
choque | se existir mais que uma turtle no mesmo patch, uma escolhida à sorte sobrevive. |
O código completo do modelo é o seguinte e é colocado na tab Procedures (cf. figura da direita):
globals [ geracao populacao ] to preparar ca set geracao 0 set populacao numero-inicial-bichos plotxy geracao populacao create-custom-turtles populacao [ setxy random-xcor random-ycor set color yellow ] end to executar ask turtles [ movimento velocidade reproducao ] if competicao? [ ask turtles [ choque ] ] set geracao geracao + 1 set populacao count turtles plotxy geracao populacao end to movimento [ distancia ] rt random-float 360.0 forward random-float distancia end to reproducao if random 100 < taxa-de-reproducao [ hatch 1 [ movimento 2 ] ] end to choque without-interruption [ while [ any? other-turtles-here ][ ask one-of turtles-here [ die ] ] ] end
Vejamos em detalhe cada componente lógica do código:
A declaração inicial indica a existência de duas variáveis globais (geracao e populacao).
globals [ geracao populacao ]
A variável geracao é um indicador temporal, enquanto que a variável populacao dá-nos o número de bichos da presente geração.
to preparar ca set geracao 0 set populacao numero-inicial-bichos plotxy geracao populacao create-custom-turtles populacao [ setxy random-xcor random-ycor set color yellow ] end
A rotina preparar, tem o objectivo de preparar a simulação, ou seja, limpa resultados anteriores e devolve às variáveis os valores iniciais.
A limpeza de resultados anteriores e da janela gráfica onde se situam as turtles e os patches é feita pela instrução ca (o mesmo que clear-all). As condições iniciais são impostas pelas instruções que começam com a palavra set. A instrução plotxy desenha no gráfico o pondo de coordenadas (geracao, populacao). O valor do parâmetro numero-inicial-bichos é definido na interface e é o valor inicial da população. As turtles são criadas com a instrução create-custom-turtles, a qual permite correr os comandos para as iniciar.
to executar ask turtles [ movimento velocidade reproducao ] if competicao? [ ask turtles [ choque ] ] set geracao geracao + 1 set populacao count turtles plotxy geracao populacao end
A rotina executar parte das condições iniciais e modifica-as através da aplicação sucessiva de um conjunto de regras. Relembra-se que o botão para esta rotina é do tipo "para sempre" (forever), pelo que uma vez iniciada, a rotina é aplicada sucessivamente até ordens em contrário dadas pelo o utilizador. O primeiro bloco
ask turtles [ movimento velocidade reproducao ]
faz com que as turtles se movam e reproduzam de acordo com os valores especificados pela velocidade e pela taxa-de-reproducao, os quais são fornecidos pelos cursores na interface gráfica.
Por sua vez, o bloco seguinte aplica a rotina choque às turtles no caso de existir competição.
if competicao? [ ask turtles [ choque ] ]
A variável lógica competicao? é introduzida através do interruptor competicao? na interface gráfica.
A instrução seguinte incrementa de uma unidade a variável geracao.
set geracao geracao + 1
A seguinte conta o número de turtles e actualiza a variável populacao.
set populacao count turtles
Por fim a instrução seguinte desenha no gráfico o ponto de coordenadas (geracao, populacao).
plotxy geracao populacao
Passemos às rotinas auxiliares. Como se verifica pela janela da interface e pelo código, estas rotinas não são principais, pois não existe nenhum botão que as corra directamente. São pois rotinas auxiliares, incorporadas na rotina executar. Estas rotinas estão separadas para decompor o código em blocos lógicos, a fim de facilitar a leitura deste, assim como para detectar mais rapidamente eventuais erros de programação.
to movimento [ distancia ] rt random-float 360.0 forward random-float distancia end
A rotina movimento movimenta as turtles. A instrução rt (right) roda a orientação da turtle para a direita um ângulo aleatório entre 0° e 360°. A instrução forward desloca-a para a frente uma distância real aleatória entre 0 e o valor armazenado na variável distancia.
Relembre-se que quando esta rotina é chamada na rotina executar a variável distancia recebe o conteúdo da variável velocidade.
ask turtles [ movimento velocidade reproducao ]
A rotina reproducao origina novas turtles. O número de turtles criado em cada iteração é dado em termos estatísticos pela taxa-de-reproducao e está compreendido entre 0 e o número de turtles existentes.
to reproducao if random 100 < taxa-de-reproducao [ hatch 1 [ movimento 2 ] ] end
A instrução hatch origina x novas turtles, onde x é o número que se segue ao comando (neste caso 1). Esta nova turtle é em tudo idêntica à progenitora (cor, posição, etc.). A rotina movimento fá-la deslocar-se para outro patch próximo. Não esquecer que esta rotina é executada em cada iteração para todas as turtles.
A rotina choque materializa os efeitos da competição. A ideia intuitiva é que cada patch só tem recursos suficientes para uma turtle, pelo que se estiver mais do que uma no mesmo patch, apenas uma sobrevive.
to choque without-interruption [ while [ any? other-turtles-here ][ ask one-of turtles-here [ die ] ] ] end
Esta rotina pega em todas as turtles que estejam no mesmo patch e mata-as, ao acaso, uma a uma, até que só reste uma:
while [ any? other-turtles-here ][ ask one-of turtles-here [ die ] ]
Podemos interpretá-la como a luta aos pares que observamos na Natureza.
No NetLogo a instrução ask faz com que os comandos seguintes sejam executados por todos os agentes ao mesmo tempo, o que habitualmente é desejável, pois o NetLogo corre sobre o Java e o Java executa este tipo de operações mais depressa. No entanto quando os agentes interagem entre si e alteram atributos / regras de comportamento, esta característica pode ter efeitos indesejáveis... Por exemplo, neste caso, se tivermos duas turtles no mesmo patch e se só estivermos a usar a instrução ask, em 50% dos casos ambas as turtles morrem. Porém na Natureza este resultado é de longe a excepção. Por esta razão usamos a instrução without-interruption que põe as turtles em fila de espera e faz com que cada uma execute as suas tarefas quando chegar a sua vez.