O meu primeiro programa

Nesta secção vamos escrever o código do modelo dos bichos que apresentámos no capítulo anterior:

  1. Coloca-se um número inicial de bichos (turtles) ao acaso no mundo.
  2. Cada bicho tem uma certa probabilidade de se reproduzir (taxa de reprodução). Em cada geração, cada bicho pode dar origem no máximo a um novo descendente, o qual é colocado num ponto ao acaso no interior da circunferência de raio 2 centrada no progenitor.
  3. Opcionalmente pode-se permitir que em cada geração os bichos se desloquem no mundo com uma certa velocidade v, ou seja que se movam no seu habitat. Caso os bichos se movam, a posição final de cada bicho é um ponto escolhido ao acaso no interior da circunferência de raio v centrada na posição inicial de cada bicho.
  4. Opcionalmente pode-se introduzir um factor de competição. Se dois bichos ocupam o mesmo território (patch), um deles, escolhido à sorte, morre.

Construiremos também a interface gráfica do modelo. Esta tarefa é tão fácil de fazer em NetLogo, que começamos por ela.

Os elementos de interface

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:

Botões

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:

Cursores

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 (%).

Interruptores

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?.

Monitores

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.

Gráficos

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.

Breve análise do código

O modelo está dividido nas seguintes secções:

Variáveis globais
geracaocontém o número de iterações já decorridas
populacaocontém o número de bichos da presente geração
Rotinas principais
prepararlimpa 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.
executaritera as regras de comportamento das turtles: -movimento, reprodução e choque; actualiza as variáveis globais e desenha-as na janela gráfica.
Rotinas auxiliares
movimentocada turtle move-se para um ponto aleatório a uma distância máxima v.
reproducaocada turtle pode dar origem a outra de acordo com a taxa de reprodução escolhida.
choquese existir mais que uma turtle no mesmo patch, uma escolhida à sorte sobrevive.

O código

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:

Variáveis globais

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.

Rotinas principais
preparar
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.

executar
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
Rotinas auxiliares

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.

movimento
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 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
]
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.

choque

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.