La Tartaruga come animale

Valid HTML 4.01!

Comportamento casuale

Pensiamo qui alla tartaruga LOGO come ad un animale (o robot) cui assegnamo un comportamento tramite programmi. Cominciamo studiando un movimento casuale ovvero diamo una lunghezza random al passo e/o alla direzione con cui esso si muove nel mondo a sua disposizione. Siamo sicuri che i comportamenti non sono guidati da "intelligenza" ma ci sorprenderemo a notare come, con particolari scelte dei parametri, sembrino invece determinati da un obiettivo.

La funzione LOGO che fornisce numeri casuali è la che restituisce come risultato un numero non negativo minore del suo unico input. Scriviamo allora la seguente funzione che restituisce un numero a caso tra nell'ipotesi che .


to randomizza :x1 :x2
op ((random (1+ abs ( :x2 - :x1 ))) +  :x1 )
end 
 

Costruiamo poi un mondo rotondo con

to mondo :raggio
pd
circle :raggio
end  
 

e permettiamo il movimento solo in esso. Imponiamo che, quando l’insetto urterebbe contro la parete, giri di 180 gradi. La casualità dell'angolo di rotazione è limitata da un adattamento all'ambiente: ha una direzione privilegiata e non è eccessiva. La scia dopo un po' riempe tutto il cerchio in cui è costretto l’animale.


to ronza :raggio
rt randomizza -10 5
pu
fd 5 
ifelse not (sqrt ((xcor*xcor) + (ycor*ycor)))< :raggio ~
[bk 5 lt 180 pd] [bk 5 pd fd 5]
ronza :raggio
end
 

In effetti questa casualità nella procedura scritta è limitata ad un’incertezza fissata della direzione ma possiamo facimente modificare la procedura per renderla parametrica nella direzione e nella distanza. Inoltre non è difficile rendere il mondo quadrato e ottenere lo stesso risultato. Lasciamo questo per esercizio e vediamo altre tecniche di guida al movimento. Una semplicissima consiste semplicemente nel non fare la mossa se non è possibile. La procedura seguente testa solo se la mossa è possibile o meno in un mondo di raggio 50.


to possibile :quanto
local "posso 
make "posso "true
ht pu fd :quanto
if (sqrt(( xcor*xcor) +(ycor*ycor))) > 50 [make "posso "false]
bk :quanto
pd st
wait 1
if :posso [ fd :quanto]
end
to ronza 
rt randomizza -10 5
possibile 5
ronza
end

 

Il comportamento sembra davvero "intelligente" alla ricerca dell'uscita dal mondo. Ma al solito, nello studiare l'intelligenza, dobbiamo avere un briciolo d'attenzione. Proviamo a cambiare con o anche a ingrandire la scatola in cui vive l'insetto e ci accorgiamo come il caso precedente potrebbe essere semplicemente un caso particolare di adattamento all'ambiente. Variazioni casuali sulla lunghezza del passo non mi sono sembrate significative.

Una guida istintiva migliore

È invece interessante un altro modo di evitare l'ostacolo: se sto per urtare mi giro di poco nella direzione contraria.


to ronza :raggio
local "so
make "so heading
rt randomizza -10 20
pu
fd 5 
ifelse not (sqrt ((xcor*xcor) + (ycor*ycor)))< :raggio ~
[bk 5 setheading :so raddrizza :raggio] [bk 5 pd fd 5]
ronza :raggio
end


to raddrizza :raggio
lt 1
pu fd 5
ifelse not (sqrt ((xcor*xcor) + (ycor*ycor))) > :raggio ~
[bk 5 pd fd 5] [bk 5]
end

 

Vediamo come basta questo affinché l'insetto arrivato alle pareti del suo mondo, quali che siano le sue dimensioni, continui a percorrerle quasi indicando una intenzione di uscire. Sarà proprio come un mosca contro i vetri delle nostre finestre?
È divertente notare come si tratti proprio di una "raddrizzata": cambiando con nella prima riga della procedura il comportamento è molto diverso.

L'olfatto

Vediamo adesso di simulare il movimento di un animale dotandolo via via dei sensi necessari. Una procedura fondamentale in questa classe di algoritmi è quella che ci fornisce la distanza tra due punti. Scriviamo dunque una funzione che calcola la distanza secondo la regola euclidea.


to dista :x1 :x2
op sqrt ((xcor - :x1)* (xcor - :x1)+ ~
(ycor - :x2)* (ycor - :x2) )
end

Proponiamoci adesso di simulare l’olfatto. Pensiamo ad un animale che si avvicina alla sorgente del cibo guidato esclusivamente da esso nel senso che sia in grado di percepirne le variazioni e cammini in modo da raggiungerlo. Supponiamo allora che l’odore che può captare sia proporzionale alla sua distanza dal cibo che poniamo sullo schermo alle coordinate cartesiane tramite la seguente procedura.

to cibo :x1 :x2
pu setpos list :x1 :x2 pd
circle 5 pu
home pd st
end
 

Nella nostra prima simulazione l'animale può muoversi di un passo costante fissato e girare di un angolo che invece gli forniamo in ingresso. Così se l’odore diminuisce rispetto a quello precedente, l’animale torna sui suoi passi e si gira. Le due variabili danno invece la posizione del cibo sullo schermo. Ragionevolmente fermiamo la tartaruga quando la sua distanza dal cibo è inferiore al passo che compie. Per mettere insieme tutta la nostra costruzione scriviamo anche la procedura che sistema il cibo e chiama poi la procedura chiave della nostra simulazione. Si vede come, nonostante la semplicità dell’algoritmo la tartaruga arrivi quasi sempre alla meta a meno di non assegnarle angoli particolarissimi. In particolare è interessante notare come il cammino è più mirato se l’angolo è più deciso.

to annusa :x1 :x2 :rot
local "mem
make "mem dista :x1 :x2
if :mem < 2 [stop]
fd 2
if (dista :x1 :x2 )> :mem  ~
[bk 2 rt :rot]
annusa :x1 :x2 :rot 
end
 

to segugio :x1 :x2 :rot
cibo :x1 :x2
annusa :x1 :x2 :rot
end
 

 

Eseguire la procedura con le chiamate come esempi di comportamento.

La vista

Simuliamo adesso la vista nel caso che l’animale la usi per mantenere costante la sua inclinazione rispetto alla fonte luminosa. Questo meccanismo consentirebbe il volo rettilineo degli insetti notturni che mantengono un angolo costante rispetto alla luna ma che cadono sulle nostre lampade. La procedura di base fa uso della funzione che restituisce la direzione verso cui occorre puntare la tartaruga per raggiungere in linea retta la posizione le cui coordinate sono assegnate come argomento.


to unocchio :angolo :x1 :x2
setheading norm towards list :x1 :x2
lt :angolo
fd 1
unocchio :angolo :x1 :x2
end

 

Il comportamento a spirale diventa sempre più mirato man mano che l’angolo è più piccolo.

Comportamento sociale (?!)

Scriviamo adesso il comportamento di due animali interagenti. Esaminiamo il caso preda-predatore in cui ipotiziamo inizialmente una preda che corre secondo una regola fissata


to mossapreda :vel :rot
fd :vel
rt :rot
end

e un predatore che si muove attratto dall’odore della preda ovvero secondo il principio del gradiente. Se il valore rappresenta l’odore al passo precedente la seguente procedura serve per regolare il movimento del predatore.


to odore :x1 :y1
ifelse (distanza :x1 :y1) > :prec  [make "prec distanza :x1 :y1 op "true]~
[make "prec distanza :x1 :y1  op "false]
end

La tartaruga assumerà la parte di entrambi i contendenti lavorando in time-sharing. Dovranno esistere variabili globali edove vengono memorizzati lo stato della preda e del predatore in modo da poter riprendere la situazione proprio dal punto in cui si era lasciata. Così la mossa del predatore avviene con la seguente procedura


to mossapredatore :vel :rot
make "x1 first item 2 :statopreda
make "y1 item 2 item 2 :statopreda
fd  :vel
if  odore : x1 :y1  [ rt :rot]
end

 

La posizione iniziale dei due animali viene assegnata in lettura


to iniziapreda
local "x1 local "y1
pr [scrivi l’ascissa della preda]
make "x1 rw
pr [scrivi l’ordinata della preda]
make "y1 rw
pu setpos list :x1 :y1 
make "statopreda list heading  pos
end

to iniziapredatore
local "x1 local "y1
local "x2 local "y2
pr [scrivi l'ascissa del predatore]
make "x2 rw
pr [scrivi l'ordinata del predatore]
make "y2 rw
make "x1 first item 2 :statopreda
make "y1 item 2 item 2 :statopreda
pu setpos list :x2 :y2
make "statopredatore list heading  pos
make "prec distanza :x1 :y1
end

 

Veniamo adesso alla procedura essenziale. Qui sfruttiamo le caratteristiche del LOGO. I parametri di una procedura possono essere a loro volta nomi di procedure. Ci vuole una procedura che porta la tartaruga allo stato di partenza (mettendo prima su la penna per non lasciare traccia ), e le faccia eseguire il suo passo.


to esegui.passo :processo :j :k :stato
pu
setheading first :stato setpos first bf :stato
pd run (list :processo :j :k)
end

La procedura fondamentale che permette l’esecuzione in time-sharing dei due processi, in quanto sarà chiamata con le due procedure munite dei valori dei loro parametri.


to caccia :proc.predatore :a :s :proc.preda :d :f
esegui.passo :proc.predatore :a :s :statopredatore
make "statopredatore list heading  pos
esegui.passo :proc.preda :d :f :statopreda
make "statopreda list heading  pos
caccia  :proc.predatore :a :s :proc.preda :d :f
end

 

Una possibile chiamata è offerta dalla procedura seguente che, con le scelte effettuate, mette la povera preda in balia del predatore


to inizia 
iniziapreda iniziapredatore
caccia "mossapredatore 2 45  "mossapreda  1 2
end