Dans ce travail pratique, vous devez implémenter un agent (ex.: un robot) qui explore un univers constitué de salles liées par des portails (ou téléporteurs).
Chaque salle est identifiée par un numéro (son indice dans le tableau de salles) et composée de cellules. Une cellule est caractérisée par ses coordonnées, c'est-à-dire un triplet d'entiers: le numéro de la salle dans laquelle elle se trouve ainsi que sa position (i,j) dans la salle. Il existe trois types de salles, selon leur forme : carré, diamant ou triangle. La forme d'une salle donnée est déterminée par le nombre de cellules et leur disposition. Un paramètre d'entrée n (entier) donne les dimensions d'une salle, selon sa forme. À titre d'exemple, la figure ci-dessous illustre les salles carre 4 (Salle 0), diamant 5 (Salle 1) et triangle 7 (Salle 2). Notez que n doit être impair pour les salles de type Diamant et Triangle (ce sera toujours le cas dans les tests) mais peut être pair ou impair pour les carrés. Une salle de type triangle est toujours orientée comme dans l'image, c'est-à-dire que la "pointe" du triangle (position (3,3) dans la figure 1) est toujours dirigée vers le bas.
Par défaut, les seuls déplacements possibles se font d'une cellule courante vers une cellule adjacente, à l'horizontale ou à la verticale, dans la même salle. Par exemple, à partir de la position (i,j), il est possible de se rendre (en un déplacement unitaire) aux positions (i-1,j), (i+1,j), (i,j-1) et (i,j+1), sous réserve bien sûr que ces positions soient valides dans la salle courante.
En outre, certaines cellules contiennent un portail permettant de se rendre directement à une autre cellule (située dans la même salle, ou dans une salle différente). Par exemple, dans la figure suivante, l'espace constitué des trois salles contient trois portails, identifiés ici par les lettres A, B et C. Le portail A permet d'aller de la cellule (1,0) dans la salle 0 à la cellule (0,2) dans la salle 1, et vice versa. On peut se rendre de la salle 0 à la salle 2 directement en utilisant le portail B, ou via la salle 1 en empruntant les portails A et C. On peut aussi se déplacer sur une cellule sans emprunter le portail qui s'y trouve. En l'absence de portail, il n'y a aucune façon de se rendre d'une salle à l'autre.
Le nombre de salles contenues dans chaque univers n'est pas connu à l'avance par l'agent, tout comme le nombre de portails et les tailles des salles.
Un squelette de départ est disponible dans tp2.zip.
Votre programme doit pouvoir être lancé en ligne de commande avec la syntaxe suivante :
./tp2 univers.txt [requetes.txt]
où univers.txt est le nom du fichier décrivant l'univers (salles et portails), et requetes.txt (optionnel) est le nom du fichier contenant les requêtes.
Si requetes.txt est spécifié, alors votre programme doit lire dans requetes.txt au moyen d'un flux de lecture C++ std::ifstream. Sinon, votre programme doit lire dans l'entrée standard (stdin) au moyen du flux d'entrée C++ std::cin. À noter que cette fonctionnalité est déjà implémentée dans le squelette de départ.
Un fichier d'univers contient une liste de salles, suivie d'une liste de portails. Un portail est représenté par les deux cellules qu'il relie, séparées par une flèche bilatérale. Pour chaque cellule, on donne le numéro s (id) de salle suivi de la position (i,j) dans la salle. Par exemple, le fichier univers0.txt ci-bas décrit l'univers de l'exemple de la section "Contexte".
carre 4 diamant 5 triangle 7 ----- 0 (1,0) <--> 1 (0,2) 0 (0,3) <--> 2 (0,6) 1 (3,3) <--> 2 (3,3)
Le numéro d'une salle devrait être son indice dans le tableau, considérant qu'on ajoute chaque nouvelle salle à la fin du tableau en lisant le fichier d'univers. Les numéros de salles doivent être corrects pour correspondre à ceux des portails lus en entrée.
Une requête est constituée de deux cellules, sur le même modèle que dans le fichier d'univers. La première cellule est le point de départ d et la seconde le point d'arrivée a dans l'algorithme principal. Par exemple, le fichier univers0-req0.txt ci-dessous contient deux requêtes (une par ligne).
0 (3,0) --> 2 (2,3) 1 (2,1) --> 2 (0,0)
Pour chaque requête, votre programme doit écrire sur une ligne le nombre de chemins possibles permettant de se rendre de d à a sans passer deux fois par une même cellule.
Le résultat de la commande ./tp1 univers0.txt univers0-req0.txt produit le résultat suivant :
6050 45370
Pour chaque requête, il suffit de chercher tous les chemins possibles de d à a et incrémenter un compteur nbChemins chaque fois qu'on en trouve un nouveau. La variable nbChemins peut être une variable globale ou une référence int& passée en paramètre de la fonction. Afin de ne pas passer plus d'une fois par une même cellule dans un même chemin, on utilise un marqueur visité (voir section 3.7).
Algorithme général pour une requête :
nombreChemins(Cellule d, Cellule a) si d et a sont dans l'univers: nbChemins ← 0 d.visité ← vrai nombreCheminsRec(d, a) retourner nbChemins retourner -1 nombreCheminsRec(Cellule c1, Cellule c2) si c1 == c2: nbChemins ← nbChemins + 1 sinon: pour chaque v dans les voisins de c1 // voir section 3.6 si v.visité == faux: v.visité ← vrai nombreCheminsRec(v, c2) v.visité ← fauxNotes : si d ou a n'est pas dans l'univers, on retourne -1. Si d == a (dans l'univers) alors on considère que le nombre de chemins est 1. C'est ce qu'on obtient avec l'algorithme ci-dessus.
Les voisins de c1 sont les cellules adjacentes à c1 dans la salle où elle se trouve, telles que décrites à la section 2, ainsi que la cellule liée à c1 par un portail le cas échéant (s'il y a un portail sur c1). Une cellule aura donc entre 0 et 5 voisines, selon la présence ou non d'un portail, et selon que les positions adjacentes sont valides ou non dans la salle.
La solution la plus simple est d'avoir une variable booléenne visite dans la struct Cellule. Évidemment, il ne faut pas oublier de remettre la variable visite à false au début ou à la fin de l'algorithme. Le mot clé mutable dans la déclaration permet de rendre une variable «modifiable» même si elle est modifiée dans un contexte constant. Par exemple, les paramètres de la fonction nombreChemins() peuvent être des références constantes, et tout attribut mutable pourra quand même être modifié. On utilise typiquement ce mot pour des variables d'instance ne servant qu'à des algorithmes particuliers (ceci sera vu plus en détail au lab11).
L'objectif est d'appliquer votre propre classe générique
Tableau au TP2. En théorie, vous pourriez utiliser la classe
Matrice faite au TP1. Or, pour les salles de types
Diamant et Triangle, cela impliquerait un gaspillage
de mémoire, car certaines cellules de la matrice ne seraient pas
utilisées. La consommation mémoire de vos programmes sera évaluée (un
des critères dans le critère B de la grille de correction), et pour
cette raison, il vous est fortement recommandé
d'utiliser votre classe Tableau pour ce TP.
L'utilisation des conteneurs de la bibliothèque standard de C++ (ex.:
std::vector ou std::list) et de la bibliothèque
algorithm (#include <algorithm>) est permise,
mais sujette à pénalités (voir critère F dans la grille d'évaluation).
Relisez les Politiques et les directives sur les outils informatiques dans le cours INF3105. Votre TP2 doit pouvoir être compilé avec g++ version 11 (version installée sur le serveur java.labunix.uqam.ca).
Vous pouvez faire ce travail en équipe de 1 ou 2. Toutefois, tous les membres de l'équipe doivent contribuer à l'ensemble du travail et non à seulement quelques parties. Le travail d'équipe vise à favoriser les discussions et l'entraide. Le travail d'équipe ne vise pas à réduire la tâche. Ainsi, se diviser la tâche en deux n'est pas une méthode de travail d'équipe appropriée dans ce cours. La participation inadéquate des membres de l'équipe peut être considérée comme du plagiat. Le professeur et le correcteur pourront sélectionner quelques équipes au hasard afin de vérifier que tous les membres sont capables d'expliquer l'ensemble du travail.
Des tests sont fournis avec le squelette de départ dans tp2.zip.
Pour lancer les tests, lancez le script evaluer.py sur une machine Linux. Notez que le package time doit être installé, ce qui est généralement le cas par défaut. Vous pouvez par exemple vous connecter sur le serveur java :
ssh codeMS@java.labunix.uqam.caQue vous soyez sur java ou votre propre machine Linux, suivez les instructions suivantes :
cd tp2 make unzip tests.zip cd tests g++ -O3 -o valideur valideur.cpp cd .. ./tests/evaluer.pyVoici un exemple de résultat d'exécution (sur java) :
Évaluation du programme tp2 d'INF3105 Mode etudiant_evaluer Nom_test CPU Mém nbReqs nbExistenceOK NbCheminsOK C1_1c-req0 0.00 3524 20 20 20 C1_1d-req0 0.00 3484 15 15 15 C1_1t-req0 0.00 3516 15 15 15 C1_2-req0 0.02 3368 10 10 10 C1_3-req0 0.22 3444 10 10 10 C1_4-req0 0.21 3532 10 10 10 C1_5-req0 20.25 3528 10 10 10 C1_6-req0 39.61 3440 10 10 10 ... C2_9-req0 2.02 3468 10 10 10 ... C3_5-req0 0.02 3432 10 10 10 C4_1-req0 112.16 3440 20 20 20 ... C4_6-req0 4.43 3660 15 15 15
Signification des colonnes:
Notez que l'exemple donné dans l'énoncé n'est pas le plus simple pour commencer. Les fichiers sont nommés en référence au critère C de la grille de correction (section 7.1). Les trois premiers fichiers (commençant par "C1_1") contiennent respectivement 1 carré, 1 diamant et 1 triangle, afin de tester votre représentation pour chaque forme, sur une salle sans portail.
Vous devez au plus tard le 16 juin 2024 à 23h59m59s EST (heure de Montréal) :
Fichiers à remettre:
Vous pouvez soumettre votre TP et les réponses au questionnaire autant de fois que vous voulez. Seule la dernière soumission sera considérée.
Ce travail pratique compte pour 10% de la note finale.
Critère | Description | Points | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A. | Respect des directives pour la remise
|
10 | ||||||||||||||||||||||||
B. | Appréciation générale
|
20 | ||||||||||||||||||||||||
C. | Fonctionnement correct Le programme retourne les bons résultats :
L'efficacité n'est pas directement évaluée. Cependant, l'efficacité peut être indirectement évaluée lorsqu'un programme ne parvient pas à produire des résultats dans des délais raisonnables. Votre programme doit également consommer une quantité de mémoire raisonnable. |
50 | ||||||||||||||||||||||||
D. | Exactitude de l'auto-évaluation déclarée dans le formulaire de remise
Attention : les tests fournis ne sont pas nécessairement ceux qui seront utilisés pour la correction. De nouveaux tests pourront être utilisés. |
10 | ||||||||||||||||||||||||
E. | Analyse et complexité Réponses correctes au questionnaire du TP2. |
10 | ||||||||||||||||||||||||
Total | 100 | |||||||||||||||||||||||||
F | Non-respect de la contrainte 4.1 «Bibliothèques permises»
Voir Section 4.1 «Bibliothèques permises» plus haut. Par exemple, utilisation de std::vector plutôt que Tableau. Jusqu'à 10 points peuvent être retranchés. | -10 |