PYTHON : PROJETS 2023
L'objectif des projets proposés sur cette page, est de combiner le paradigme de
programmation objet avec la réalisation d'un programme mettant en oeuvre
la séparation noyau/interface. Pour chacun des projets, il faudra donc
mettre en place une organisation du code composée de deux ensembles de classes :
- D'un côté, on va implémenter un ensemble de classes dont le rôle est de
réaliser les fonctionnalités figurant dans le cahier des charges,
indépendamment de toute interface utilisateur, en modélisant le problème à
résoudre de manière purement formelle.
- De l'autre côté, on va créer un second ensemble de classes qui utiliseront
ce noyau pour proposer une interface utilisateur pour le programme. Selon le
type de projet, on pourra même implémenter deux interfaces différentes, l'une
en mode texte et l'autre en mode graphique.
Projet 1 : "Penté" à deux joueurs
Ecrire un programme Pente qui implémente une
version à deux joueurs du jeu du même nom. Le "Penté" se joue sur une grille
carrée de taille variable, et constitue une variante plus complexe du très
classique "Morpion". Chaque joueur pose un pion, à tour de rôle, et tente de
former l'une des deux combinaisons gagnantes :
- soit créer un alignement (horizontal, vertical ou diagonal) de 5 pions
adjacents de sa couleur (cette combinaison vaut 5 points)
- soit capturer deux pions adjacents de l'adversaire en les encadrant avec
deux de ses propres pions (cette combinaison vaut 1 point)
Le vainqueur de la partie est celui qui obtient le nombre de points convenu au
départ entre les joueurs (habituellement, une partie se joue en 5, 10 ou 15
points). Afin de limiter au maximum, l'avantage du joueur qui démarre la
partie, les règles modernes de "Penté" incluent une restriction pour la pose
des pions : aucun joueur ne peut poser un pion sur l'une des 8 cases
entourant le pion qu'il a posé au tour précédent, ce qui interdit
certaines combinaisons permettant de gagner à coup sûr. Le jeu peut être testé
sur une version en
ligne mais malheureusement celle-ci n'implémente pas la restriction des
placements de pions.
La figure ci-dessus illustre le type d'interface graphique qui est demandée :
- Au lancement du programme, une fenêtre de configuration doit s'afficher
(cf. image de gauche) permettant de saisir le nom des deux joueurs, ainsi que
les deux paramètres numériques : la taille du plateau (entre 8x8 et 16x16)
ainsi que le nombre de points à réaliser pour gagner la partie (entre 5 et 15)
- Ensuite, lorsqu'on clique sur le bouton "START", cette fenêtre de
configuration est remplacée par une nouvelle fenêtre qui affiche la grille
de jeu (cf. image de droite). Dans la zone du haut sont rappelés les noms
et les scores de chaque joueur, et au centre, la lettre A ou B
indique le joueur actif. La zone du bas présente l'état courant du plateau de
jeu, avec les cases vides (en noir), les pions du joueur A (en bleu), les pions
du joueur B (en vert). A chaque coup, le programme colorie en gris les cases
vides voisines du pion posé par le joueur actif lors de son dernier tour. Selon
la règle de restriction de pose, ces cases sont donc invalidées pour ce tour de
jeu. Pour jouer son coup, chaque joueur clique simplement sur la case choisie.
- Le programme doit évidemment détecter les configurations d'alignement et de
capture des pions et actualiser les scores en conséquence (concrètement, à
chaque coup, il suffit d'explorer le voisinage de la case joué pour vérifier si
ce coup engendre un alignement ou une capture). La victoire de l'un ou l'autre
des joueurs donnera lieu à un affichage spécifique (animation des pions sur le
plateau ou fenêtre pop-up)
La partie "noyau" de l'implémentation demandée doit se baser sur une classe
qui permet la gestion d'une grille de jeu carrée de dimension quelconque. A
l'intérieur de ce noyau, la grille va être représentée par une matrice 2D dont
les éléments sont des valeurs entières caractérisant de
manière symbolique, le contenu de chaque case de la grille (par exemple, 0
représentera une case vide, 1 une case avec un pion bleu, etc). La
classe correspondante devra notamment gérer l'ajout et la suppression des pions
durant les tours de jeu, la détection des configuration d'alignement et de
capture, ainsi que le calcul du score (il faut noter que plusieurs alignements
et/ou capture peuvent être obtenus durant le même coup).
La partie "interface" de l'implémentation devra mettre en oeuvre tout ce
qui concerne la sélection de la taille de la grille de jeu, l'affichage du
plateau, des pions et des scores, le contrôle des actions effectuées à la
souris par les joueurs. Pour simplifier la mise en oeuvre, on fournit
une archive ZIP contenant la grille
des images correspondant aux 4 états possible d'une case (case vide valide, pion
bleu, pion vert, case vide invalide) ainsi qu'un fichier squelette pour le code
à réaliser.
Exemples à relire avant de commencer le projet :
- Exemple D6_peg.py (pour la séparation noyau/interface d'un programme)
- Exemple E5C_win.py (pour la gestion de l'alternance entre les 2 fenêtres)
- Exercice E6A_slide.py (pour la mise en oeuvre globale de l'interface)
Projet 2 : Drop
Ecrire un programme Drop qui implémente le jeu du
même nom disponible sur différentes plateformes de jeu en ligne, telles que
CoolMathGames. Le
gameplay de Drop se présente comme un mélange de deux grands classiques
du jeu vidéo,
Columns d'un côté, et
2048 de l'autre.
En effet, le but du jeu est (comme dans Columns) d'arriver à former des
assemblages d'au moins 3 blocs adjacents de la même couleur, et (comme dans
2048) chaque fois qu'un tel assemblage est obtenu, il va être effacé et remplacé
par un bloc de plus haut niveau, caractérisé par une nouvelle couleur. Le score
du joueur est calculé en fonction de la taille des assemblages réalisés, multipliés par le niveau des blocs utilisés.
La figure ci-dessus illustre le type d'interface graphique qui est demandée, et
qui est relativement proche de la version disponible en ligne :
- Au lancement du programme, une fenêtre de configuration doit s'afficher
(cf. image de gauche) permettant de saisir le nombre de lignes (entre 6 et 12)
et le nombre de colonnes (entre 6 et 12, également) de la grille de jeu, ainsi
que le nombre de couleurs (entre 1 et 12) qui peuvent apparaître dès le départ
pour les blocs à assembler. Au cours de la partie, le nombre de couleurs pour
les blocs va augmenter au fur et à mesure que le joueur obtient de nouvelles
couleurs par assemblage des blocs.
- Ensuite, lorsqu'on clique sur le bouton "START", cette fenêtre de
configuration est remplacée par une nouvelle fenêtre qui affiche la grille
de jeu (cf. image de droite). La partie haute montre le bloc bicolore que le
joueur peut déplacer avant de le faire tomber dans la partie centrale, où se
produisent les assemblages de couleurs. La partie basse affiche, à gauche, 12
cases correspondants aux 12 couleurs identifiant les différents niveaux des
blocs, et à droite, la valeur du score pour la partie en cours.
- Le jeu se pilote entièrement au clavier : les flèches droites et gauche
permettent de déplacer le bloc tant qu'il est sur la ligne du haut, la flèche
vers le haut permet de permuter les 2 couleurs du bloc, et la flèche vers le
bas actionne la chute du bloc qui va s'arrêter en contact des blocs déjà
présents dans la grille.
Pour simplifier la mise en oeuvre, on fournit
une archive ZIP qui contient un squelette
pour le code à réaliser. La partie "noyau" de l'implémentation demandée pourra
s'inspirer de la classe GravityGrid (Exercice D9) qui permet de gérer
une grille de taille variable où les éléments stockés dans les cases sont
soumis à la gravité (le code complet de cette classe se trouve également dans
l'archive ZIP. La partie "interface" de l'implémentation devra mettre en oeuvre
tout ce qui concerne l'affichage de la grille de jeu et du score courant, le
contrôle des actions effectuées par le joueur, et la création de l'animation en
fin de partie.
De manière optionnelle, on pourra également rajouter un mode de jeu "dynamique"
dans lequel les pièces descendent automatiquement dans la grille lorsque le
temps s'écoule, pour forcer le joueur à prendre ses décisions plus rapidement.
Exemples à relire avant de commencer le projet :
- Exemple D6_peg.py (pour la séparation noyau/interface d'un programme)
- Exemple E5C_win.py (pour la gestion de l'alternance entre les 2 fenêtres)
- Exercices D9A_gravitygrid (pour la gestion de la grille et de la gravité)
- Exercice E6B_slide.py (pour la mise en oeuvre globale de l'interface)
Projet 3 : ComboColor
Ecrire un programme ComboColor qui implémente une
variante simplifiée du jeu de société Combo Color édité par Asmodée.
C'est un jeu de conquête territoriale pour 2 à 4 joueurs, qui se joue en
coloriant au feutre les cases d'un plateau de jeu, afin de réaliser les
combinaisons permettant de marquer le plus de points. La description du jeu
se trouve
sur le site du "Labo des Jeux" et l'explication complète des règles
en vidéo, se trouve
sur la chaîne"Explique-moi ça".
Dans la variante simplifiée proposée dans le cadre du projet, le jeu se limite
à 2 joueurs possédant chacun 2 couleurs (rouge et bleu pour le joueur A, vert
et jaune pour le joueur B). De plus, le plateau de jeu est une simple grille
composée de 24 zones de 5 cases chacune (cf. image ci-dessus). Chaque zone est
taggée avec l'un des trois types de labels permettant d'identifier la valeur de
la zone pour le décompte final des points : un label bleu correspond à des
points additifs, un label rouge à des points soustractifs et un label vert à
des points multiplicatifs. Les scores se calculent par couleur: on effectue le
total des points bleus auxquels on retranche le total des points rouges, et on
multiplie le résultat obtenu par le total des points verts. Le score global
de chaque joueur est défini à tout moment comme le produit des scores
correspondants à ses deux couleurs. C'est évidemment le programme qui se charge
à chaque tour de jeu, du calcul et de l'affichage de l'évolution des scores.
Pour le coloriage des différentes zones lors des tours de jeu, le plus simple
est d'utiliser
la bibliothèque Pillow
qui fournit de nombreux algorithmes de traitement d'images, et en particulier,
une fonction floodfill qui permet de remplir une zone homogène
d'une image avec une couleur spécifiée.
On fournit une archive ZIP
contenant une série de fichiers utiles pour la réalisation du projet. Il y a
notamment les images PNG de 6 plateaux de jeu différents (chacun basé sur une
grille de taille 11x11), ainsi qu'un encodage de ces 6 plateaux sous la forme
d'un fichier texte semi-structuré, permettant d'identifier la nature des labels
de score pour chacune des cases. L'archive contient également un fichier
"combocolor.txt" qui donne quelques explications complémentaires sur
certains éléments à mettre en oeuvre dans le code à fournir.
Comme pour les projet précédents, il faudra créer une (ou plusieurs) classe(s)
pour le noyau du jeu, permettant de modéliser le jeu de manière abstraite, puis
une (ou plusieurs) classe(s) pour l'interface graphique qui mettra en oeuvre la
visualisation du plateau de jeu et la gestion des actions des deux joueurs. Mais
à la différence des projets précédents, on ne fournit pas de fichier squelette
comme point de départ pour le code à réaliser.
Exemples à relire avant de commencer le projet :
- Exemple E5C_win.py (pour la gestion de l'alternance entre les 2 fenêtres)
- Exemple D6_peg.py (pour la séparation noyau/interface d'un programme)
- Exercice E9A_pixelart.py (pour la lecture et l'analyse du fichier texte)
- Exemple E8C_pillow.py (pour le coloriage d'une zone de forme quelconque)
Projet 4 : OSCAR
Depuis leur développement il y a près de 30 ans, les systèmes multi-agents
(SMA) ont été utilisés avec succès dans divers domaines. En éthologie, par
exemple, les SMA servent comme outil de simulation pour étudier le comportement
des insectes sociaux (fourmis, abeilles, etc). Dans ce contexte, les SMA
consistent à définir un ensemble d'automates communicants qu'on laisse évoluer
au cours du temps, afin de voir si l'on peut reproduire des comportements
sociaux observés dans la nature. Le but du projet est de réaliser un petit
simulateur appelé oscar (Outil de Simulation
Comportemental par Attraction-Répulsion) consistant à animer des agents sur une
grille, en fonction d'un modèle de comportement très simple basé sur un concept
d'attraction-répulsion.
Voici les principales caractéristiques du modèle utilisé :
- La simulation se déroule sur une grille rectangulaire (appelée WORLD) sur
laquelle vont évoluer des entités statiques ou dynamiques (appelés AGENT)
- Chaque case de la grille ne peut contenir qu'un seul agent à un instant T
de la simulation temporelle.
- Chaque agent appartient à l'un des trois ordres suivants :
MINERAL, VEGETAL ou ANIMAL.
- Un agent d'ordre MINERAL ne se déplace pas et ne se reproduit pas.
- Un agent d'ordre VEGETAL ne se déplace pas mais peut se reproduire (sur une
case adjacente à sa position courante).
- Un agent d'ordre ANIMAL peut se déplacer et se reproduire (dans les deux
cas, sur une case adjacente à sa position courante).
- A chaque instant, un agent est dans un certain état (appelé STATUS) qui est
identifié par l'utilisation d'une icône spécifique (ou simplement un rectangle
de couleur spécifique) pour la visualisation de cet agent sur la grille.
- Chaque agent est caractérisé par un ensemble de paramètres numériques
(appelés VAR) dont les modifications entraînent un changement d'état lorsque
les valeurs de ces paramètres franchissent des seuils prédéfinis. Le changement
d'état d'un agent est visualisé par une modification de l'icône utilisée pour
sa représentation sur la grille.
- Un agent peut émettre autour de lui, un champ de potentiel (appelé FIELD)
qui a une valeur maximale à la position courante de l'agent, et subit une
décroissance isotrope (i.e. toutes les cases à une même distance de l'agent
possèdent la même valeur de potentiel). Au-delà d'une certaine distance définie
par la vitesse de décroissance, le potentiel émis par un agent devient
nul.
- Pour chaque case de la grille, et à chaque pas temporel, on va sommer tous
les champs de potentiel qui portent le même nom, indépendamment de l'agent
émetteur
- Un agent peut capter un tel champ avec une acuité plus ou moins forte par
le biais d'un capteur (appelé SENSOR). Selon le signe du capteur, les champs
perçus peuvent être identifiés comme positifs, neutres ou négatifs.
- Le déplacement et/ou la reproduction d'un agent s'effectue toujours
sur la case adjacente la plus favorable (i.e. la case où la somme obtenue en
ajoutant les champs perçus positivement et en retranchant les champs perçus
négativement, est maximale).
- Lorsque deux agents se rencontrent (c'est-à-dire, qu'ils sont placés sur
deux cases adjacentes) chacun absorbe les champs émis par l'autre auxquels il
est sensible, ce qui va faire augmenter ou diminuer certains des paramètres
numériques caractérisant ces deux agents, et donc potentiellement entraîner un
changement d'état pour les deux agents. Par exemple, un agent ANIMAL herbivore
va absorber le paramètre "nutrition" d'un agent VEGETAL, ce qui va simultanément
faire augmenter le paramètre "nutrition" de l'agent ANIMAL et mettre à zéro,
ce même paramètre pour l'agent VEGETAL, ce qui devrait aboutir à sa suppression
de la grille, si un seuil critique est défini pour ce paramètre.
- Afin de simplifier l'écriture des fichiers de configuration, on peut mettre
en oeuvre un certain nombre d'états prédéfinis qui peuvent s'appliquer à tout
type d'agent. Par exemple, l'état void identifie une case vide (et peut
donc représenter la suppression d'un agent lors d'une transition), l'état
end enclenche la fin du processus de simulation, l'état trace
peut être utilisé pour identifier la case quittée lors d'un déplacement d'un
agent de type ANIMAL, etc...
La description du terrain ainsi que des caractéristiques des agents utilisés
pour une simulation particulière s'effectue par le biais d'un fichier texte,
qui sera interprété par le programme avant de démarrer la simulation.
Ce fichier de structure très simple se compose d'une série de blocs
(c'est-à-dire des groupes de lignes séparées par au moins une ligne vide),
chacune définissant une des 5 commandes possibles: world, mineral,
vegetal, animal et agent. De plus, lorsqu'un caractère
# apparait sur une ligne du fichier, les caractères à sa droite jusqu'à
la fin de la ligne, sont considérés comme du commentaire.
Mise à part la commande world, toutes les commandes peuvent apparaitre
plusieurs fois, pour définir différents états ou différents agents. Dans la
syntaxe ci-dessous, les termes en gras correspondent à des mots-clés et doivent
donc apparaitre littéralement, alors que les autres termes correspondent à des
paramètres et doivent être remplacés par une valeur numérique (pour les termes
en italiques) ou alphanumérique (pour les termes en romanes). Le symbole
<|> signifie que l'on peut mettre soit le signe inférieur soit le
signe supérieur, afin de définir le sens de comparaison d'un paramètre par
rapport à une valeur de seuil.
world NumberRows NumberCols VoidIcon_or_VoidColor
mineral StatusName StatusIcon_or_StatusColor
var VariableName InitValue TimeStepValue
status VariableName <|> ThresholdValue NewStatusName
field FieldName DistanceStepValue
sensor SensorName FieldName SensitivityValue
vegetal StatusName StatusIcon_or_StatusColor
var VariableName InitValue TimeStepValue
status VariableName <|> ThresholdValue NewStatusName
birth VariableName <|> ThresholdValue NewbornStatusName
field FieldName DistanceStepValue
sensor SensorName FieldName SensitivityValue
animal StatusName StatusIcon_or_StatusColor
var VariableName InitValue TimeStepValue
status VariableName <|> ThresholdValue NewStatusName
birth VariableName <|> ThresholdValue NewAgentStatusName
trace VariableName <|> ThresholdValue NewTraceStatusName
field FieldName DistanceStepValue
sensor SensorName FieldName SensitivityValue
agent StatusName (row,col) (row,col) ...
agent (StatusNameA,StatusNameB,...) (rowA:rowB:step,colA:colB:step) ...
Pour mieux comprendre le rôle de ce fichier de configuration, voici quelques
exemples simples qui correspondent à des simulations classiques:
Les 3 premiers exemples n'utilisent que l'ordre mineral des
agents, ce qui permet de valider votre moteur sans avoir besoin d'implémenter
l'ensemble des fonctionnalités. Les autres exemples utilisent également les
ordres vegetal et animal. Ces six fichiers de
configuration sont regroupés dans une
une archive ZIP qui contient également
un fichier "oscar.txt" donnant quelques explications complémentaires sur
certains éléments à mettre en oeuvre dans le code à fournir.
...
Christophe Schlick