Byzantines circonvolutions
Imaginez faire partie de l'aristocratie. Une aristocratie déchue,
exclue, délaissée… Dans cet univers froid où vous cherchez
des réponses à vos questions, vous savez au moins que celles-ci sont
liées à un étrange manoir, intimement mais subtilement relié à votre
passé. Un passé dont vous ne connaissez que des bribes. Mais au fond
de ce manoir, vous savez qu'il existe une salle permettant de
comprendre la clé de ce passé, et c'est devenu votre unique raison
de vivre. Le problèmes, c'est que le manoir en question possèdes des
propriétés fantasmagoriques. D'une part, vous ne pouvez l'explorer
qu'un temps fini à chaque fois, avant que la fatigue ne vous
submerge. Et d'autre part, tel un cauchemar fantastique,
l'arrangement des pièces du manoir change à chaque exploration. Tout
ce dont vous êtes certain, c'est qu'il est constitué de pièces
carrées avec plus ou moins de sorties possibles à chaque fois, comme
par exemple :
L'objectif de ce jeu étrange, puisque c'en est un, est d'arriver à
atteindre la sortie de ce manoir, en partant de son entrée, et en
explorant au fur et à mesure les pièces pour tenter d'atteindre la
position de l'ultime salle, celle où toutes les questions trouveront
toutes les réponses. Quitte, tel un Sisyphe moderne, à recommencer
sans relâche pour sortir de ce mauvais rêve.
Les inspirations de ce sujet sont tirées du jeu Blue Prince développé par le studio DoguBomb. Le sujet ne demande aucune connaissance particulière du jeu, et n'envisage en aucun cas d'arriver à reproduire tout ou partie de sa mécanique. Néanmoins, la composante labyrinthique est au coeur de ce sujet. Et l'exploration du wiki en question peut fournir des idées pour l'écriture du jeu.
|
Un ensemble de salles
possibles, comportant plus ou moins de sorties chacune
(
Source sur le Wiki du jeu Blue Prince) |
Les inspirations de ce sujet sont tirées du jeu Blue Prince développé par le studio DoguBomb. Le sujet ne demande aucune connaissance particulière du jeu, et n'envisage en aucun cas d'arriver à reproduire tout ou partie de sa mécanique. Néanmoins, la composante labyrinthique est au coeur de ce sujet. Et l'exploration du wiki en question peut fournir des idées pour l'écriture du jeu.
À propos des achievements : ce sujet de
projet est construit sur la notion
d'achievement. Un achievement est un
ensemble de fonctionnalités à développer pour le projet. Les
achievements doivent être réalisés dans l'ordre, et de manière
cumulative. Ils seront débloqués à chaque fois qu'au moins une
équipe de projet aura rempli les conditions de l'achievement
précédent et aura fait valider la chose par son encadrant. La
version de base correspond à l'achievement initial. L'évaluation
du projet est basée en partie sur le nombre
d'achievements réalisés, et la version de base n'est pas forcément
représentative du travail nécessaire pour atteindre la moyenne.
Ressources et tuiles
Le jeu se déroule dans un manoir rectangulaire, aux
dimensions
Toutes les actions réalisées dans le manoir peuvent coûter ou générer des ressources (à travers le type
Chaque case du manoir est censée contenir une tuile (de type
(BOARD_WIDTH, BOARD_HEIGHT). Le nombre de
cases du manoir est noté comme BOARD_SIZE := BOARD_WIDTH *
BOARD_HEIGHT. Chaque case de ce manoir est identifiée par
une position (et le type struct
location_t), représentant une abscisse et une ordonnée. Les
positions valides pour jouer ont des abscisses dans [0;
BOARD_WIDTH[ et des ordonnées dans [0;
BOARD_HEIGHT[. Pour les besoins du projet, les valeurs
de BOARD_WIDTH et BOARD_HEIGHT pourront
varier, mais elles seront toujours au moins égales à 3. L'interface
pour manipuler les positions est définie dans le fichier location.h. Pour identifier
le lien entre deux positions adjacentes, on utilise
une direction (à travers le type enum
dir_t), qui énumère les quatre directions cardinales. La
direction NORTH correspond aux ordonnées croissantes,
tandis que la direction EAST correspond aux abscisses
croissantes. L'interface pour manipuler les directions est définie
dans le fichier dir.h.
Toutes les actions réalisées dans le manoir peuvent coûter ou générer des ressources (à travers le type
enum
resource_t1). Le nombre de ressources différentes en jeu
est NUM_RESOURCES. Pour les besoins du projet, il est
possible de faire varier NUM_RESOURCES, mais ce nombre
est censé être compris dans l'intervalle [2;
MAX_RESOURCES[, avec MAX_RESOURCES fixé à 10.
L'interface pour manipuler les ressources est définie dans le
fichier resource.h. Pour représenter
des ensembles de ressources, on utilise
des poches2 (dans le
code des struct stash_t), qui ne sont guère plus que
des tableaux de ressources stockés sous la forme de vecteurs
d'entiers. Une poche est dite finie
lorsqu'elle ne contient aucune ressource en
quantité UINT_MAX (sinon elle est dite
infinie). L'interface pour manipuler les
poches est définie dans le fichier stash.h. Elle donne des
exemples d'une poche finie ONE_STEP_STASH représentant
un unique pas, ainsi qu'une poche infinie INFINITE_STEP_STASH.
Chaque case du manoir est censée contenir une tuile (de type
struct
tile_t). Une tuile est caractérisée par :
- son nom (par exemple, "Chambre à coucher");
- le coût pour traverser la tuile (par
exemple un unique pas, i.e
ONE_STEP_STASH, mais d'autres coûts sont envisageables); - le coût pour ouvrir la tuile dans une direction donnée (cf. plus tard).
- les tuiles vides, qui ne sont pas réellement des tuiles, mais simplement des marqueurs pour indiquer que la position correspondante n'a pas encore été découverte;
- la tuile de départ, qui peut s'ouvrir dans les 4 directions possibles, qui ne peut apparaître qu'une seule fois dans le manoir, et qui sert de point de départ au joueur;
- la tuile d'arrivée, qui peut s'ouvrir dans les 4 directions possibles, qui ne peut apparaître qu'une seule fois dans le manoir, et qui sert d'objectif d'arrivée.
Le type utilisé pour représenter les tuiles est
un type abstrait et il doit être défini dans
un fichier
Le manoir associe à chaque position valide une tuile. Au début de la
partie, il doit contenir une tuile de départ (située dans le jeu
original au milieu des positions d'ordonnée tile.c. Conséquence du fait que ce type
soit abstrait, dans les fichiers autres que tile.c,
on est obligé de ne manipuler les tuiles qu'à travers des
pointeurs. Le bénéfice apporté par cette complication est la
possibilité de proposer d'autres implémentations des tuiles, que
l'on pourra (un jour) substituer en fonction
des besoins de jeu.
0), ainsi
que d'une tuile d'arrivée (située au milieu des positions
d'ordonnée BOARD_HEIGHT-1). Toutes les autres tuiles
sont initialement des tuiles vides.
Pour les besoins du projet, il est interdit de
modifier les 5 fichiers d'interface précédents. Il est par
contre tout à fait possible, voire encouragé, d'en ajouter de
nouveaux.
Déroulement de la partie
Le jeu commence avec avec le joueur initialement placé sur la tuile
de départ. L'objectif du jeu consiste à réussir, en partant de la
tuile de départ, à atteindre la tuile d'arrivée, en se déplaçant de
case en case, en
L'exemple suivant permet d'illustrer un petit morceau de partie. La tuile de départ (ici le "Entrance Hall") est située à la position
Dans ces images, l'existence d'une porte ouverte sur une tuile donnée correspond à un coût d'ouverture fini. La dernière action de la partie (i.e le déplacement de la "Pantry" vers la "Security") est possible parce que les deux tuiles sont adjacentes, non vides, et supposément avec des coûts d'ouverture finis. La situation suivante donne un exemple dans lequel il n'est pas possible de se déplacer entre la case "Pantry" et la case "Security", du fait d'un coût infini dans un seul des deux sens (ici depuis la "Pantry", vers l'est) :
Dans cette situation, le coût d'ouverture de la "Security" vers l'ouest est fini (la tuile contient une ouverture), mais le coût d'ouverture de la "Pantry" vers l'est est infini (la tuile contient un mur). Comme les deux coûts ne sont pas finis, il n'est pas possible de passer d'une tuile à l'autre.
ouvrant successivement des tuiles dans
le manoir. Le jeu est gagné si le joueur arrive à se déplacer sur la
tuile d'arrivée. Pour aider au débuggage, le jeu est perdu si le
nombre de tours joué dépasse MAX_TURNS (initialement
fixé à 100). Le joueur possède initialement un nombre
de ressources égal à 4 * BOARD_SIZE / 3
pas (arrondi à l'entier supérieur).
A chaque tour de jeu, le joueur peut réaliser l'une des deux actions
suivantes :
-
Ouverture d'une tuile adjacente :
Il n'est possible d'ouvrir une tuile que si celle-ci est vide et
adjacente à la position courante, et que le coût d'ouverture
depuis la tuile courante est fini. Si c'est le cas, on applique
une mécanique de tirage de tuiles
(drafting) :
-
Le jeu tire aléatoirement au
plus
NUM_TILES_PER_TURNtuiles différentes qu'il propose au joueur. Le jeu assure que chacune de ces tuiles est telle que les coûts pour ouvrir dans les deux sens sont finis. S'il n'y pas de tuile vérifiant ces propriétés, l'ouverture d'une tuile sur la case sélectionnée n'est pas possible. - Le joueur sélectionne alors une des tuiles en question, et il doit simplement payer le coût d'ouverture de la tuile d'arrivée. Il ne se déplace alors pas. La tuile dans le manoir à la position adjacente est alors remplacée par la tuile sélectionnée. Si le joueur ne peut payer le coût d'aucune des tuiles proposées, c'est une fin de partie.
-
Le jeu tire aléatoirement au
plus
- Déplacement sur une tuile adjacente : Il n'est possible de se déplacer d'une tuile à une tuile adjacente que si ces deux tuiles sont non vides, et que les coûts pour ouvrir dans les deux sens sont finis. Dans ce cas, le joueur doit simplement payer le coût de traversée de la tuile d'arrivée, et appliquer le déplacement.
L'exemple suivant permet d'illustrer un petit morceau de partie. La tuile de départ (ici le "Entrance Hall") est située à la position
(0,0), et le joueur commence lui aussi à cette
position. La position du joueur est indiquée par une pastille rouge.
La liste des actions suivantes pour le joueur représente alors une
partie valide, en supposant que tous les coûts de déplacements et
d'ouverture sont abordables pour le joueur :
|
|
|
… |
|
||||||||||||||||||
Dans ces images, l'existence d'une porte ouverte sur une tuile donnée correspond à un coût d'ouverture fini. La dernière action de la partie (i.e le déplacement de la "Pantry" vers la "Security") est possible parce que les deux tuiles sont adjacentes, non vides, et supposément avec des coûts d'ouverture finis. La situation suivante donne un exemple dans lequel il n'est pas possible de se déplacer entre la case "Pantry" et la case "Security", du fait d'un coût infini dans un seul des deux sens (ici depuis la "Pantry", vers l'est) :
|
|
|
Passage impossible (images tirées
du wiki sur
Fandom)
|
|
Dans cette situation, le coût d'ouverture de la "Security" vers l'ouest est fini (la tuile contient une ouverture), mais le coût d'ouverture de la "Pantry" vers l'est est infini (la tuile contient un mur). Comme les deux coûts ne sont pas finis, il n'est pas possible de passer d'une tuile à l'autre.
Quelques tuiles pour démarrer
Ensemble de tuiles : selon l'ensemble de
tuiles avec lesquelles on joue, le jeu peut devenir plus ou moins
intéressant. Ainsi, si toutes les tuiles sont des Cloîtres qui
s'ouvrent dans les 4 directions, ne coûtent rien à ouvrir et à
traverser, le jeu devient trivialement facile. A l'opposé, si toutes
les tuiles sont des Terrasses qui ne s'ouvrent que dans une unique
direction, le jeu devient impossible.
Pour pallier ce problème, nous proposons de jouer avec l'ensemble de
tuiles suivant par défaut. Noter que ce n'est qu'une suggestion et
que cela ne fait pas partie des exigences du projet :
Le choix des coûts des tuiles est une question épineuse pour
l'équilibrage du jeu, mais a peu d'importance dans la version de
base. Néanmoins, pour fixer les choses, dans le jeu de base, chaque
tuile a un coût de traversée de
|
|
|
|
| Cloister (poids 2) | Terrace (poids 1) | E-W Corridor (poids 1) | N-S Corridor (poids 1) |
ONE_STEP_STASH, et un
coût d'ouverture de ONE_STEP_STASH.
Processus de sélection : le jeu demande à
chaque ouverture de case de pouvoir
tirer NUM_TILES_PER_TURN tuiles avec des propriétés
particulières.
Pour commencer, on assigne un poids à chaque
tuile dans le jeu. Dans l'exemple précédent, chaque tuile pourrait
avoir le même poids 1, sauf le cloître qui aurait le
poids 2.
Ensuite la sélection se fait de la manière suivante (cet algorithme
de sélection fait partie de la version de base, contrairement aux
ensembles de tuiles précédents) :
- Tout d'abord, on sélectionne uniquement les tuiles qui satisfont les critères pour pouvoir être placées sur la case sélectionnée (typiquement les coûts d'ouverture);
- Ensuite, on tire les tuiles au hasard parmi l'ensemble des tuiles précédentes, et cela proportionnellement à leur poids;
- Enfin, si le processus précédent a engendré des doublons, on retire les tuiles en double.
NUM_TILES_PER_TURN tuiles au final. Les tuiles en
question sont ensuite passées au joueur, qui sélectionne celle qui
lui semble la plus appropriée pour poursuivre sa partie.
Base version
Pour valider la version de base du projet, il est demandé de
produire un programme avec les caractéristiques décrites dans ce
paragraphe.
Le programme doit mettre en place une boucle permettant de jouer
une partie en suivant les règles précédemment énoncées. Toute
décision impliquant plusieurs choix possibles sera prise de
manière aléatoire. Le pseudo-algorithme de la boucle principale
est le suivant :
Le projet suggère la décomposition des tâches suivante :
-
init_tiles(0); // Use parameter equal to 0 by default game_state = init_state(); current_turn = 1; while current_turn <= MAX_TURNS and exit_not_reached(game_state) possible_locations = list_empty_locations_with_finite_opening_cost(game_state) if available_directions.count > 0 and decides_to_place_tile() selected_location = select_one(possible_locations) available_tiles = generate_some_tiles(NUM_TILES_PER_TURN) selected_tile = select_one(available_tiles) place_tile(game_state, selected_location, selected_tile) else // decides_to_move() possible_locations = list_open_locations(game_state) selected_location = select_one(possible_locations) move_player(game_state, selected_location) player_has_won = exit_reached(game_state) -
Exemple de début de partie :
tiles = { Entrance, Security, Pantry, Den }; board = { Entrance at (0,0), (1,0), (2,0), (3,0), (0,1), (1,1), (2,1), (3,1), (0,2), (1,2), (2,2), Exit at (3,2), }; game_state = { steps = 10, board = board, location = (0,0) }; place_tile(Den, (1,0)); // pay the cost of opening Den (from the West) place_tile(Pantry, (0,1)); // pay the cost of opening Pantry (from the South) move_player((1,0)); // pay the traversal cost of the Den ...
Le projet suggère la décomposition des tâches suivante :
-
Écrire des fonctions pour manipuler les positions et les
directions
Pour cela, il suffit de créer des
fichiers
location.cetdir.cqui implémentent les fonctionslocation_*etdir_*dont les prototypes sont définis dans les interfaceslocation.hetdir.h. Les fonctions en question peuvent renvoyer des chaînes de caractères vides (par exemple à des fins de tests), ou alors utiliser les codes couleurs ANSI pour les plus aventureux. Ne pas oublier de tester chacune de ces fonctions. -
Écrire des fonctions pour manipuler les ressources et des poches
Lesdites ressources et poches doivent respecter (sans les
modifier) les interfaces
resource.hetstash.hspécifiées dans la section Ressources et Tuiles. Ne pas oublier de tester chacune de ces fonctions. Sur le long terme, il est envisageable de factoriser les fonctions permettant de mettre en place les tests. -
Créer des tuiles et les fonctions pour les générer
Lesdites tuiles doivent respecter (sans les modifier)
l'interface
tile.hspécifiée dans la section Ressources et Tuiles. Les tuiles du jeu par défaut doivent être implémentées, mais il est encouragé de créer ses propres ensembles de tuiles. Sans surprise, le processus de sélection de tuiles devrait avoir son propre jeu de tests. - Créer un manoir matérialisant l'état des tuiles découvertes au cours de la partie Écrire les opérations de base sur le manoir, typiquement celles qui permettent de chercher les positions de départ, d'arrivée et les tuiles vides, mais aussi de placer des tuiles et de vérifier s'il existe un chemin entre deux tuiles. Il est évidemment possible d'ajouter d'autres fonctions selon votre code.
- Créer une représentation de complète de l'état du jeu Ecrire les opérations permettant de gérer la position et les ressources du joueur, de tester si un coup particulier est possible, ainsi que de jouer un coup dans le jeu, qu'il s'agisse d'un déplacement du joueur ou du placement d'une tuile.
- Mettre en place la boucle de jeu L'idée est de suivre le pseudo-algorithme ci-dessus, de vérifier des coups, de gérer les cas de fin de parties et de décider de la victoire éventuelle du joueur.
Achievement 1 : Rotation des tuiles
Dans la version de base, les tuiles sont rigides, au sens où une
fois décidé de leur orientation, il n'est plus possible d'en
changer. Évidemment, il est possible de dupliquer les tuiles pour
chaque sens de rotation (comme il est fait pour les Corridors dans
la description des
tuiles
par défaut). Mais d'une part, ce n'est pas très élégant, au
sens où cela entraîne de la duplication de code, et cela complique
le réglage des algorithmes de tirage de tuiles.
On demande donc à ce que le jeu permette d'effectuer des rotations des tuiles avec lesquelles on joue (uniquement dans les directions cardinales, à savoir 90°, 180° ou 270°). Cela entraîne deux types de modification :
On demande donc à ce que le jeu permette d'effectuer des rotations des tuiles avec lesquelles on joue (uniquement dans les directions cardinales, à savoir 90°, 180° ou 270°). Cela entraîne deux types de modification :
-
Le processus de sélection est modifié de la manière
suivante : la première étape du processus
produit une première liste de tuiles sélectionnables de la
manière suivante :
- une première liste de tuiles satisfaisant les critères
pour l'ouverture est générée (en utilisant la
fonction
tile_draftavec la bonne direction); - une seconde liste de tuiles quelconque est générée (en
utilisant la fonction
tile_draftavec la directionINVALID_DIR), puis, pour chaque tuile dans cette second liste ne satisfaisant pas les critères pour l'ouverture, on applique une rotation aléatoire (90°, 180° ou 270°), et on l'enlève de la liste si après rotation elle ne satisfait pas les critères d'ouverture; - le résultat final est l'union des deux listes.
- une première liste de tuiles satisfaisant les critères
pour l'ouverture est générée (en utilisant la
fonction
-
Le joueur dispose au départ de sa partie
de
NUM_INITIAL_ROTATIONSrotations disponibles (fixé initialement àBOARD_WIDTH * BOARD_HEIGHT / 10). Tant qu'il possède des rotations disponibles, lorsqu'il pose une tuile sélectionnée par le processus de sélection, il calcule les rotations appliquables qui préservent les critères pour l'ouverture, en tire une au hasard et l'applique.
Achievement 1.5 : Vérification des ressources
Cet achievement est une petite amélioration de jeu proposée par
plusieurs élèves. Actuellement, lors de l'ouverture d'une tuile,
il est possible que la fonction
Implémenter une fonction permettant ce raffinement dans le
fichier
tile_draft ne propose
au joueur que des tuiles pour lesquelles il ne peut pas se payer
les coûts d'ouverture. On se propose donc d'ajouter la fonction
suivante au fichier tile.h :
/** The same function as `tile_draft`, but takes a `resources`
parameter, and ensures that the opening cost for the each tiles
stored in the `results` array does not exceed `resources`. */
unsigned int tile_draft_with_resources(enum dir_t d, struct stash_t resources,
struct tile_t** results);
tile.c. A partir de cet achievement, il doit
toujours être possible au joueur de déterminer s'il pourra ouvrir
une tuile ou pas pendant son tour (quitte à faire le
draft avant de lui laisser le choix et vérifier s'il y a
bien une tuile dans le résultat). S'il ne peut pas ouvrir de tuile,
il doit alors obligatoirement se déplacer.
Achievement 2 : Objectifs à atteindre
A partir de cet achievement, on considère que
Cet achievement demande donc de faire les modifications suivantes :
NUM_RESOURCES
>= 4. Les ressources d'arrivée sont
définies comme les trois ressources [ NUM_RESOURCES-2,
NUM_RESOURCES-1, NUM_RESOURCES ]. Les coûts d'ouverture de
la tuile d'arrivée sont tous égaux à une poche contenant
exactement un exemplaire de chaque ressource d'arrivée. Avant le
démarrage de la partie, le manoir sélectionne aléatoirement trois
positions différentes de celle de la tuile de départ et de la
tuile d'arrivée. Ces positions sont appelées
les positions-objectifs. A chacune de ces
positions-objectif est associée une ressource d'arrivée
différente. Les positions-objectifs sont connues du joueur. L'idée
de cet achievement est de rendre nécessaire pour le joueur
d'ouvrir une tuile sur chaque position-objectif pour gagner la
partie.
Cet achievement demande donc de faire les modifications suivantes :
- Lorsque le joueur ouvre pour la première fois une tuile sur une positions-objectif, il gagne un exemplaire de la ressource d'arrivée associée.
- Au début de la partie, le joueur calcule sa distance de Manhattan à chaque position-objectif, et les ordonne selon cette distance.
- A chaque tour de jeu, il cherche à jouer de manière à se
rapprocher de la première position-objectif qu'il n'a pas
encore visitée. Cela implique de :
- calculer l'ensemble des positions des tuiles que l'on peut ouvrir,
- de sélectionner la plus proche du point de vue de la distance de Manhattan de la position-objectif,
- de trouver une tuile déjà ouverte adjacente,
- de s'y déplacer éventuellement si on ne s'y trouve pas déjà, puis d'ouvrir la tuile qui rapproche de la position-objectif.
Achievement 3 : Equipements fonctionnels
Cet achievement se propose d'implémenter des équipements pour le
joueur permettant de modifier l'état du jeu de manière variée. A
chaque ouverture de salle, le joueur a une chance
sur
BOARD_WIDTH * BOARD_HEIGHT de trouver un
équipement au hasard qu'il ne possède pas déjà. Ensuite,
l'équipement modifie les règles du jeu de la manière
suivante :
- la pelle : à chaque ouverture de pièce, le joueur gagne une
ressource aléatoire (entre
1etNUM_RESOURCES) avec une probabilitéBOARD_WIDTH * BOARD_HEIGHT; - le porte-monnaie : chaque fois que du GOLD est gagné (par exemple avec la pelle), et quelle que soit la quantité, le joueur gagne un GOLD supplémentaire,
- les chaussures de course : chaque fois qu'un déplacement est réalisé, il y a une chance sur trois que les chaussures remboursent un STEP au joueur,
- les chaupelles de monnaie : à chaque ouverture de pièce, à chaque gain de ressource et à chaque déplacement, le joueur gagne un STEP.
void (*on_tile_open)(struct game_state_t*, const struct tile_t*); // the game state and the tile that was just opened
void (*on_ressource_gain)(struct game_state_t*, const struct stash_t*); // the game state and the stash that was just gained
void (*on_move)(struct game_state_t*, const struct stash_t*); // the game state and the cost of the last move
Achievement 4 : Equilibrage des ressources
Cet achievement propose d'équilibrer les coûts en ressources des
différentes tuiles du jeu. Pour cela, on propose de jouer un
nombre
NUM_RANDOM_GAMES de parties au hasard (fixé à
1000 par défaut), et de faire des analyses statistiques sur le
nombre de fois qu'une entrée d'une tuile est utilisée pour être
ouverte, ainsi que le nombre de fois que le joueur la
traverse. L'un des attraits de cet achievement est de rendre le
code capable de jouer plusieurs parties de manière indépendante en
une seule exécution.
- Générer des coûts d'ouverture aléatoires pour les tuiles;
- Écrire le code nécessaire pour calculer les statistiques précédentes (en supposant que le joueur a des ressources infinies);
- Estimer (empiriquement) le coût moyen d'une partie;
- Adapter les ressources de départ du joueur de manière à assurer que le joueur gagne 80% de ses parties.
Achievement 5
Écriture du code
Le code écrit est censé respecter les standards de codage suivant :
-
Le code devra être organisé en répertoires comme suit :
Sans surprise, le répertoire
Makefile src/ test/
srccorrespond au code source, tandis que le répertoiretestdoit contenir le code nécessaire aux tests. Les différents achievements devront être marqués dans le dépôt à l'aide d'étiquettes (ou tags)git. Ajouter une étiquette se fait avec les commandes suivantes :
La version de base est considérée comme l'achievement 0, les autres achievements sont numérotés à partir de 1.git tag achiev0 # Create tag 'achiev0' git push --tags # Push tags on server git tag # List available tagsLes tags marquent les achievements réalisés, et suivent une syntaxe bien particulière, à savoirachiev[0-9\.]+. Noter qu'une fois un tag mis sur le dépôt, il n'est plus possible de le modifier. Seul le code avec le dernier tag sera considéré pour le rendu final. S'il n'existe pas de tel tag, il sera considéré qu'il n'y a pas de code rendu. Un projet sans code rendu ne valide pas les projets. Afin de simplifier la vie, la syntaxe des tags autorise les tags suivants correspondant tous à l'achievement 0 :achiev0,achiev0.1ou encoreachiev0.1.2.3.4. -
L'exécutable du projet doit se nommer
project, doit pouvoir exécuter une partie du jeu décrit dans ce projet. En plus de cela, il doit pouvoir prendre comme paramètres optionnels :- un paramètre optionnel
-spour initialiser la graine du générateur aléatoire. - un paramètre optionnel
-tqui sera passé à la fonctioninit_tiles. - un paramètre optionnel
-nqui fixe le nombre maximum de tours de jeu possible.
getopt(voirgetopt(3)etgetopt_long(3)). La page de manuel degetoptdonne un exemple de code d'utilisation. Les exemples suivants donnent des idées sur les possibilités d'utilisation :
Il est tout à fait envisageable d'ajouter d'autres types de paramètres à cet exécutable../project # No parameters, manor BOARD_WIDTH*BOARD_HEIGHT # random initialization of the RNG, init_tiles is called with parameter equal to 0 ./project -s 156 -t 2 -n 10 # One optional parameter to pass to init_tiles (here 2) # an optional parameter limit the number of turns (here 10) # and an optional parameter to initialize the RNG (here 156) - un paramètre optionnel
-
Le dépôt doit contenir un fichier Makefile permettant d'exécuter les tests. Pour cela, il devra comporter
au moins les
règles ci-après :
-
Une règle
principale permettant de compiler l'ensemble du code et des
tests, dont en particulier un exécutable
appelé
projectdécrit dans le paragraphe précédent. La règle de compilation doit permettre de paramétrer les tailles des plateaux de jeu en passant trois variables d'environnementBOARD_WIDTH,BOARD_HEIGHTetNUM_RESOURCES. Ainsi, la ligne suivante doit produire un exécutable jouant dans un manoir de taille 5x6, avec seulement 3 ressources différentes :
Le fichier Makefile fourni en exemple permet déjà de réaliser ce paramétrage. Il conviendra à chacun de comprendre comment il fonctionne ou de poser la question à un de ses encadrants.make BOARD_WIDTH=5 BOARD_HEIGHT=6 NUM_RESOURCES=3 -
Une règle
testlançant l'ensemble des tests pour le dernier achievement réalisé. Il est tout à fait envisageable d'avoir plusieurs exécutables de tests différents, mais ils devront tous être lancés par la commandemake test.
-
Une règle
principale permettant de compiler l'ensemble du code et des
tests, dont en particulier un exécutable
appelé
Un code qui ne compile pas est un code sans
intérêt, et un code qui n'intéresse pas le responsable des projets
est un code qui ne valide pas les projets.
Contact
Pour toute information complémentaire, contacter : David RENAULT
disponible aux séances d'encadrement le mardi et le vendredi.