Playing with list and function

Objectifs
Ce petit exemple pourrait être le début d'un jeu de société avec un déplacement de pions. Chaque tortues se téléporte sur une case au hasard, et si celle-ci est blanche, alors elle ajoute 1 pour chaque couleur présente dans un voisinnage de 8 à son inventaire stockée sous forme de liste.
Dans cette exercice, nous commençons par utiliser les concepts suivants :
- l'écriture de fonctions
- création et manipulation de listes pour stocker des éléments : ajout, modification
- l'utilisation de fonctions pour parcourir les listes commes
foreach
- l'utilisation d'une fonction de voisinnage
neighbors
- l'utilisation de
myself
- la réécriture de code avec
of
Algorithme
L'algorithme est le suivant :
- (1) créer 10 tortues de position xy random, de couleurs noires
- (2) créer un raster qui mélange 3 couleurs (vert, orange, bleu) et l'absence de couleurs (blanc)
- (3) les tortues se téléporte sur une nouvelle case xy random
- (4) pour chaque tortue qui est sur une case blanche, celle-ci
- (4.1) récupère les couleurs des patchs dans son voisinage de 8
- (4.2) classifier les couleurs dans un tableau avec la fonction
classify-colors
- (4.3) elle aggrege les couleurs classifiés renvoyées par (5) et les ajoute au décompte existant pour cette tortue
- (5) une fois toutes les tortues blanches traitées revenir à l'étape (3)
Mise en oeuvre
Comme habituellement on définit deux grandes fonctions setup
et go
, la première servira à l'initialisation du monde, la deuxième à la mise en route du monde avec l'execution des règles à chaque pas de temps.
On définit deux attributs pour nos tortues :
on-white?
sera un booléen qui indique si la tortue est sur une case blanchecount-colors
contiendra une liste des couleurs qui récapitule les couleurs croisées par chaque tortue :- le premier item de cette liste représente les occurences de couleurs verte
- le deuxième item de cette liste représente les occurences de couleur orange
- le troisième item de cette liste représente les occurences de couleur bleu
- le quatrième item de cette liste représente les occurences de couleur blanche
Nous avons 4 procédures et 2 fonctions dans notre programme :
to setup
...
end
to-report aggregate-colors [o-colors n-colors] ; (1)
...
end
to-report classify-colors [n-colors] ; (2)
...
end
to count-on-patch ; (3)
...
end
to teleport
...
end
to go
end
- Cette fonction est appelé pour chaque tortue, prend en paramètre le tableau des couleurs précédentes
o-colors
et le tableau des nouvelles couleursn-colors
, elle renvoie le cumul des deux tableaux. - Cette fonction est appelé pour chaque tortue, elle prend en paramètre le tableau des couleurs récupérée dans le désordre et renvoie décompté en suivant l'ordre
[green orange blue white]
. Sin-colors
vaut[105 105 25 55 ]
avec 105 (blue), 55 (green), 25 (orange) la fonction renverra[1 1 2 0]
- Cette procédure contient la partie (4) de l'algorithme, et c'est elle qui va apeller les fonctions (1) et (2) pour classifier puis agréger ces couleurs. Par exemple si
o-colors
vaut[4 2 2 0]
etn-colors
vaut[1 1 2 0]
alors la fonction renverra[5 3 4 0]
La procédude setup
initialise les couleurs de patches
puis crée et initialise les 10 tortues sur le plateau de jeu.
A l'initialisation l'attribut count-colors
est définit comme une liste vide de 4 items, pour les 4 couleurs que l'on doit compter au cours du jeu.
to setup
clear-all
reset-ticks
ask patches [
set pcolor first shuffle [green orange blue white]
]
crt 10 [
set xcor random-pxcor
set ycor random-pycor
set shape "dot"
set size 2
set color black
set count-colors [ 0 0 0 0 ]
]
end
to teleport
ask turtles [
set xcor random-pxcor
set ycor random-pycor
]
end
to go
teleport
count-on-patch
tick
end
Dans la procédure go
, on déplace les tortues de façon aléatoire teleport
à chaque pas de temps tick
, et surtout, le plus important on demande à compter les couleurs pour chaque tortues.
Afin de garder le code go
relativement simple et lisible, on a choisi d'encapsuler le code relatif au décompte des couleurs dans la procédure count-on-patch
.
Note
Il faut utiliser les fonctions et procédure pour rendre plus générique votre code, mais aussi pour l'organiser et le découper de façon logique. Il est plus intéressant pour le concepteur et relecteur du code d'avoir des corps de procédures et de fonctions de tailles raisonnables, et dont l'utilisation est claire et explicite du fait d'un nom bien choisi.
to count-on-patch
ask turtles [
let new-colors [] ; (1)
ask patch-here [ ; (2)
(ifelse
pcolor = white [
ask myself [ ; (3)
set on-white? true
set new-colors (classify-colors [pcolor] of neighbors) ; (4)
]
][
ask myself [ ; (5)
set on-white? false
]
]
)
]
if on-white? [
set count-colors aggregate-colors count-colors new-colors ; (6)
]
]
end
- La variable locale
new-colors
est réinitialisé pour chaque tortue, elle permet de stocker au niveau du contexte de la tortue le résultat de la classification qui a lieu dans le contexte imbriqué dupatch-here
- On ouvre un contexte questionant le patch sous jacent à la tortue
- Au sein du contexte du patch, on a besoin de mettre à jour la variable locale
new-colors
avec le résultat de la classification. On a donc le droit d'utiliser le mot clefmyself
qui se rapport ici à l'agent tortue. - On apelle la fonction
classify-colors
avec comme paramètre la liste des 8 couleurs renvoyés par[pcolor] of neighbors
- même chose que pour 3, si la couleur du patch n'est pas white il est important de remettre la valeur de
on-white?
àfalse
. - On apelle la fonction
aggregate-colors
avec la liste de couleurs rencontrés stockées dans la tortue dans la variable (count-colors
) et les valeurs classifiés précédemment dans (4)
Cette fonction pourrait être écrite autrement, et de façon plus simple, en s'appuyant sur d'autres primitives netlogo, à vous d'essayer.
La fonction de classification utilise foreach
, un type de boucle dédié au parcours de listes.
Note
Pour rappel la différence entre les Listes et les AgentSet est lié à l'ordonnancement des éléments. A chaque appel de fonction qui retourne un AgentSet on a la garantie que le tirage se fait de façon aléatoire. Mais parfois, comme dans cet exercice, nous avons besoin de garder les éléments ordonnés, c'est pourquoi on utilise une liste et les fonctions associées aux manipulation de listes.
La fonction classify-colors
est la suivante :
to-report classify-colors [n-colors]
; g o b w
let n-colors-classified [ 0 0 0 0 ] ; (1)
foreach n-colors [ c -> ; (2)
(ifelse c = green
[
set n-colors-classified replace-item 0 n-colors-classified (item 0 n-colors-classified + 1) ; (3)
]
(c = orange)
[
set n-colors-classified replace-item 1 n-colors-classified (item 1 n-colors-classified + 1)
]
(c = blue)
[set n-colors-classified replace-item 2 n-colors-classified (item 2 n-colors-classified + 1)]
(c = white)
[set n-colors-classified replace-item 3 n-colors-classified (item 3 n-colors-classified + 1)]
)]
report n-colors-classified ; (4)
end
- On initialiste une variable locale qui contiendra le résultat renvoyé par (4)
- Pour chaque élément
c
(couleur) du tableau, on execute la conditionifelse
qui détermine de quel couleur il s'agit exactement. - Si par exemple, la couleur c est verte, alors on va remplacer l'item 0 (
replace-item
) de la listen-colors-classified
par(la valeur précédente + 1)
c'est à dire((item 0 n-colors-classified)+ 1)
- Une fois arrivée à la fin de la boucle, on a forcément classé tous les éléments, donc on peut renvoyer le tableau classifié
n-colors-classified
La fonction aggregate-colors
est la suivante :
to-report aggregate-colors [o-colors n-colors]
let i 0
let ag-colors [ 0 0 0 0 ] ; (1)
foreach n-colors [ elem -> ; (2)
set ag-colors replace-item i ag-colors (elem + item i o-colors) ; (3)
set i (i + 1) ; (4)
]
report ag-colors ; (5)
end
- On créé une variable locate pour stocker le résultat de la somme entre le tableau de couleur stocké dans l'attribut de la tortue et le nouveau tableau de couleurs issue de la collecte une fois classifié.
- On parcourt chaque élément du tableau, peu importe lequel d'ailleurs vu qu'ils on les deux 4 valeurs. Il nous faut juste un compteur i qui va permettre d'identifier ou l'on se situe dans le tableau.
- On va mettre à jour le tableau
ag-colors
en se basant sur le cumul des valeurs du nouveau tableau (icielem
) et celui du tableau existant pour la tortue(item i o-colors)
- On met à jour le compteur i explicitement, foreach ne gérant pas çà.
- On renvoie le tableau final après la fusion des deux tableau, celui ci va écraser la liste existante (cad
count-colors
) déjà pour la tortue ayant fait l'appel à cette fonction.
Add a Plot
Pour construire un plot sur l'interface, il faut faire un clic-droit puis choisir Plot.

Vous pouvez ensuite définir les valeurs qui vont être utilisé par chaque Pen (Stylo) au sein du plot, sachant que ces valeurs seront forcément recalculé à chaque pas de temps (tick
)
New rules
Nous allons ajouter un peu de piment à ce jeu avec une nouvelle règle.
Chaque pion/tortue possède donc un tableau de couleurs qui est le cumul des couleurs qu'il a rencontré dans son voisinnage pendant la partie.
Nous allons ajouter un tableau des scores pour stocker les scores de chacune des tortue, celui-ci est un attribut my-score
de la tortue et il est initialisé à [0 0 0 0]
.
Lorsque deux pions se croisent dans un certains radius, par exemple 3, alors ils s'affrontent selon les règles suivantes :
- si il y a plus d'un pion dans le voisinnage, on en prend un au hasard, on reste dans le cadre d'un duel
- on compare les couleurs
count-colors
des deux pions paire à paire entre les 2 combattants. - si un pion est plus fort sur une couleur alors il gagne le combat pour cette couleur, ce qui revient à ajouter +1 à
my-score
à la position correspondante à la couleur. Le perdant lui perd 1 point, sachant que le score ne peux pas descendre en dessous de zéro...
Par exemple :
- Le pion A possède un
count-colors
=[2 3 4 1]
- Le pion B possède un
count-colors
=[1 4 4 2]
Ce qui donnera à la fin du combat :
- Le pion A ajoutera a son tableau des score existants, les valeurs :
[ +1 -1 0 -1 ]
- Le pion B ajoutera a son tableau des score existants, les valeurs :
[ -1 +1 0 +1 ]
Nous allons ajouter deux nouvelles variables à nos tortues :
already-battle?
est un booléen qui détermine si la tortue a déjà combattu ou pascount-battle
contiendra le résultat aggrégé des combats
On initialise ces valeurs dans le create
des tortues.
turtles-own [ on-white? count-colors already-battle? count-battle]
to setup
...
crt 10 [
set xcor random-pxcor
set ycor random-pycor
set shape "dot"
set size 2
set color black
set count-colors [ 0 0 0 0 ]
set count-battle [ 0 0 0 0 ]
set already-battle? false
]
count-on-patch
end
La partie combat se situera juste après l'opération de déplacement et le décompte des couleurs par les tortues. Nous allons concentrer le code correspondant dans la procédure battle
ask turtles [
let my-ennemy one-of other turtles in-radius 3 ; (1)
let count-this-battle [0 0 0 0]
if is-turtle? my-ennemy and already-battle? = false [ ; (2)
show (word "battle with => " my-ennemy)
let my-count-colors count-colors ; (3)
let ennemy-count-colors [count-colors] of my-ennemy
let i 0
; Comparaison des tableaux entre protagnonistes
; Mise à jour des valeurs de tableau des protagonistes
]
]
end
Processus de comparaison entre les tableaux des protagonistes :
foreach my-count-colors [ mc ->
let result-battle (battle-between (item i count-colors) (item i ennemy-count-colors)) ; (4)
;; remplace à la position i dans count-this-battle
set count-this-battle replace-item i count-this-battle result-battle ; (5)
set i i + 1
]
Ici on utilise une nouvelle fonction qui permet simplement de rapporter pour chaque duel de couleurs la valeur correspondante.
to-report battle-between [c1 c2]
(ifelse
c1 > c2 [ report 1 ] ; c1 gagne c2
c1 = c2 [ report 0 ] ; match nul
[report -1] ; c1 perd c2
)
end
La mise à jour des valeurs chez les protagonistes :
;; les deux sont marqués comme s'étant déjà battus
set count-battle (aggregate-battle count-battle count-this-battle) ; (6)
ask my-ennemy [ ; (7)
set already-battle? true
set count-battle (aggregate-battle count-battle count-this-battle)
]
La fonction aggregate-battle
utilisé ici est strictement identique à la fonction déjà existante aggregate-colors
. Le plus logique serait de les remplacer toutes deux par une seule fonction aggregate-list-by-colum
plus générique.