You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Bastien e1a51cc117 remise en forme du readme 3 years ago
ai Suppression des copies des IA. 6 years ago
img Ajout image nameboard 6 years ago
.gitignore Ajout du fichier .gitignore. 6 years ago
Makefile api.h en dépendance de arbitre.o 6 years ago
README.md remise en forme du readme 3 years ago
arbitre.c Correction warnings 32bit arbitre.c 6 years ago
arbitre.h "allow to returning" n'est pas de l'anglais valide, corrigé 6 years ago
dameschinoises.h Initial commit 7 years ago
dameschinoises.patch Suppression du « [4] » dans le commentaire 6 years ago
dummy.c dummy.c : warning sur %d sur un size_t corrigé (int va bien pour 0..121) 6 years ago
gui.c GUI : correction du segfault si image manquante 6 years ago
gui.h GUI : ajout de la fonction d'affichage du nom du joueur. 6 years ago
guidemo.c GUI : ajout de la fonction d'affichage du nom du joueur. 6 years ago
main.c Variable mal affectée 6 years ago
readme Ajout du clic droit pour la fin de la suite de sauts 6 years ago
stack.c Ajout de la pile 6 years ago
stack.h Ajout de la pile 6 years ago

README.md

Compilation du projet

Dépendances : SDL, sdl_image, sdl_gfx, sdl_ttf Lancer make pour compiler.

main est l’exécutable du binôme ayant réalisé la partie graphique ai/ai.so est la bibliothèque du binôme stratégie

Présentation de l’interface

La syntaxe est la suivante : ./main [<./bibliothèque.so> …] Si moins de bibliothèques que de joueurs sont spécifiées, des joueurs réels complèteront le nombre. Attention : pour utiliser une même bibliothèque plusieurs fois, effectuer des copies du fichier .so.

Lors du jeu, les joueurs réels valideront la fin de leur suite de sauts par un clic droit.

Une démonstration de toutes les fonctions de l’interface est disponible : lancer ./guidemo

Explications sur le fonctionnement du programme arbitre

define

le « #define allow_returning_to_first_position_in_a_round 1 » permet d’autoriser ou d’interdire le fait qu’un joueur revienne à son point de départ pendant un même coup. Par exemple en effectuant un mouvement x→y puis le mouvement y→x. le « #define allow_to_block_opposite_player 0 » permet d’activer ou de désactiver la règle qui indique qu’un joueur doit sortir de sa branche quand le joueur d’en face n’a plus de coup qui le fait progresser.

structure « player_t »

struct player_t { char name[50]; enum hole_t branch; unsigned int error; void ia_lib_p; / NULL si le joueur n’est pas une stratégie */ };

Le champ name contient le nom du joueur. Le champ branch contient le numéro de la branche associée au joueur. Le champ error contient le nombre d’erreurs (coups invalides) que le joueur a fait. Le champ ia_lib_p est un pointeur vers la stratégie (ou NULL si le joueur est un joueur réel)

fonction « char_to_int »

La fonction « enum bool char_to_int(char *argv, int *nombre) » permet de convertir un nombre contenu dans une chaîne de caractères en un entier. La fonction retourne « true » si la conversion a réussi et « false » si la conversion a échoué. Le nombre converti est stocké à l’adresse pointée par le paramètre « nombre ».

fonction « number_length »

La fonction « unsigned int number_length(int n) » retourne le nombre de caractères nécessaires pour convertir ce nombre en une chaîne de caractères en base 10. Ainsi number_length(-100) retourne 4.

fonction « ia_call_function »

La fonction « enum bool ia_call_function(const struct player_t player, const enum api_function_t api_function, void *result, …) » permet d’appeler les fonctions de la stratégie.

Le paramètre « const struct player_t player » correspond au joueur (stratégie) pour lequel il faut appeler une fonction. Le second paramètre « const enum api_function_t api_function » correspond à la fonction à appeler Le troisième paramètre « void *result » permet de récupérer la valeur de retour de la fonction qui sera appelée (dans le cas d’une fonction void, ce paramètre vaut NULL) Les arguments suivants sont les arguments attendus par la fonction de la stratégie.

La fonction va d’abord affecter à la variable « function_name » la chaîne de caractères correspondant au nom de la fonction à appeler. Ce nom est déduit de la valeur du paramètre « api_function ». Ensuite on essaie de récupérer un pointeur vers la fonction en question grâce à l’utilisation de « dlsym ». Si cela échoue, la fonction « ia_call_function » retourne « false » et se termine. Sinon, selon la fonction demandée, on lit éventuellement les paramètres avec « va_arg() ». Enfin on appelle la fonction de la stratégie.

fonction « star_branch »

La fonction « enum hole_t star_branch(const unsigned int nb_player, const size_t index) » permet de déterminer sur quelle branche de l’étoile doit se trouver la position de départ d’un joueur donné.

On numérote chaque branche de l’étoile :

     / \
____/ 1 \____
\ 6       2 /
 \         /
 /         \
/ 5       3 \
————\ 4 /————
     \ /

Ainsi quand le nombre de joueurs est égal à 2, le joueur 0 doit être placé sur la branche 1 et le joueur 1 sur la branche 4. On a donc dans ce cas « star_branch(2,0) = 0 » et « star_branch(2,1) = 3 ».

Avec trois joueurs, on place les joueurs sur les branches 1, 3 et 5. Avec quatre joueurs, on les place sur les branches 1, 2, 4 et 5. Avec six joueurs, on utilise chaque branche.

fonction « move_calculation »

La fonction « ssize_t move_calculation(const size_t index, const enum direction_t move) » permet de déterminer l’indice du voisin d’une case sur le plateau.

La fonction prend deux paramètres. Le premier paramètre, « index » est l’indice de la case dont on veut un voisin. Le second paramètre « move » permet de préciser quel voisin on souhaite.

La fonction retourne l’indice du voisin ou bien -1 si le voisin demandé n’existe pas (par exemple la case d’indice 0 n’a pas de voisin à gauche).

La fonction va d’abord déterminer sur quelle ligne se trouve la case dont on cherche le voisin ainsi que sa position sur la ligne (offset). La fonction détermine aussi dans quel « bloc » de l’étoile, la case se trouve. Les blocs sont définis comme ceci :

     / \        
____/   \____   bloc 0
----------------------
\           /   bloc 1
 \         /
----------------------
 /         \    bloc 2
/           \
----------------------
————\   /————   bloc 3
     \ /

Ensuite on applique une formule différente selon le voisin dont on veut déterminer l’indice.

Pour le voisin à gauche : si l’offset est de 0, c’est à dire qu’on est sur la case la plus à gauche de la ligne alors on renvoie -1 (il n’y a pas de voisin gauche). Sinon on retourne l’indice de la case moins un (puisque les cases sont numérotées ligne par ligne).

Pour le voisin droit : on retourne l’indice de la case demandée plus un, sauf si cette case est située au début de la ligne suivante (les indices des cases au situées au début des lignes sont connus).

Pour le voisin « haut gauche » : On commence par traîter les cas où ce voisin n’existe pas. C’est à dire si on se trouve à la frontière entre le bloc 0 et le bloc 1 ou sur toute première d’une ligne d’un bloc :

     / \        
(**)/   \_(**)
\           /
 \         /
 (*)       \
(*)         \
————\   /———— 
     \ /

« (*) » → cases qui n’ont pas de voisin haut gauche.

Si la case ne satisfait pas ces critères (donc elle a un voisin « haut gauche », on récupère l’indice de la case à la ligne précédente ayant de même offset. On enlève 1 selon si le bloc est impair ou non (car les offsets sont décalés de 1 selon si la taille des lignes est croissant ou décroissante)

Enfin on peut ajouter 4 ou retirer 5 pour faire le « collage » entre les blocs 0 et 1 et les blocs 2 et 3.

Pour le voisin « haut droit », on fonctionne de la même façon. On commence par traîter les cases qui n’ont pas ce voisin :

      / \        
(**)_/   \(**)
\           /
 \         /
 /         (*)
/           (*)
————\   /———— 
     \ /

Ensuite la formule est la même que précédemment sauf qu’au lieu d’enlever 1 selon le bloc, on ajoute 1.

Pour les voisins situés en bas, on applique encore ce principe.

La fonction « enum bool search(const size_t *tab, const size_t length, const size_t value) » est une simple recherche dichotomique qui permet de vérifier si la valeur « value » se trouve dans le tableau trié « tab » de taille « length ».

fonction « nb_possible_move »

La fonction « int nb_possible_move(const struct game_state_t game, const struct player_t player) » retourne le nombre de mouvements qu’un joueur peut faire. Cela correspond au nombre de voisins atteignables par 0 ou 1 saut. Pour cela on regarde pour chaque voisin si la case est atteignable ou non. Cette fonction sera utilisée dans la fonction « valid_move » afin d’autoriser un joueur à passer son tour s’il tous ses pions sont bloqués.

fonction « pawn_on_branch »

La fonction « enum bool pawn_on_branch(const enum hole_t player_branch, const size_t index, const size_t start_position[6][10]) » permet de vérifier si le pion à la case « index » se trouve sur une branche de l’étoile qui n’est ni la branche de départ, ni la branche d’arrivée du joueur. Cette fonction permet notamment de vérifier la règle « Un pion n’a le droit de séjourner que dans son triangle de départ, dans l’hexagone central ou dans son triangle d’arrivée. Au cours d’un parcours, on a le droit de passer par un triangle d’aile, mais pas d’y stationner. »

fonction « distance_to_branch »

La fonction « int distance_to_branch(const enum hole_t branch, const size_t index) » permet d’évaluer la distance entre deux cases du plateau. La distance est calculée comme le nombre de lignes séparant les deux cases plus la différence d’indice sur la ligne . Cependant comme les lignes n’ont pas toutes la même longueur, on accentue le décalage sur les lignes ayant peu d’éléments.

fonction « must_move »

La fonction « enum bool must_move(const struct player_t player, const struct game_state_t game, const size_t start_position[6][10], size_t *res) » détermine si le joueur bloque le joueur situé en face de lui. Pour cela on regarde toutes les cases que peut atteindre le joueur d’en face en un coup (composé de plusieurs mouvement). Si il n’y a aucun coup qui ne le fait progresser (un coup qui progresse est un coup qui diminue la distance avec la branche d’arrivée) et que il y en aurait eu si le joueur n’était pas resté dans sa branche de départ, alors le joueur bloque le joueur en face de lui. Il doit donc sortir de sa branche de départ. must_move retourne vrai si le joueur doit sortir de sa branche avec res qui correspond à la liste des pions qui gênent, sinon must_move retourne faux. Cette fonction est utilisée dans la fonction « valid_move » afin de vérifier la règle « Un pion doit obligatoirement quitter son triangle de départ quand le joueur situé vis-à-vis n’a plus, comme seul coup lui permettant de progresser, que la rentrée : on doit alors laisser la place libre, même si on a un meilleur autre coup. (Il est interdit de bloquer un autre joueur.) »

fonction « valid_move »

La fonction « enum validation_movement_t valid_move(const int start_pos_first_move, const struct move_t move, const struct move_t previous_move, const struct player_t player, const struct game_state_t game, const size_t start_position[6][10], const enum bool last_move) » permet de vérifier si le coup « move » est un coup valide ou non.

Le paramètre « first_move » est le point de départ du premier mouvement d’un coup. Le paramètre « move » est le mouvement dont on veut vérifier la validité Le paramètre « previous_move » est le mouvement précédent. Cela permet ainsi de vérifier que pendant un même tour, on ne déplace qu’un seul pion. Si c’est le premier mouvement d’un tour, ce mouvement doit comporter (-1,-1). Le paramètre « player » est le joueur qui effectue le déplacement. Cela permet de vérifier que le joueur déplace un pion qui lui appartient. Le paramètre « game » est le plateau du jeu. Le paramètre « start_position » est un tableau qui contient les indices des cases formant les branches de l’étoile. Le paramètre « last_move » permet d’indiquer qu’il s’agit du dernier mouvement d’un tour. Ces deux derniers paramètres permettent de vérifier que le joueur ne s’arrête pas sur une branche de l’étoile.

La fonction retourne « jump », « jump_valid », « neighbour_valid » ou « invalid ».

La fonction va d’abord vérifier si le joueur n’est pas bloqué. Si le joueur est bloqué alors si il a mis n’importe quoi dans « move » et que « next_move » a retourné 0, on considère qu’il passe son tour. Ensuite on vérifie que le joueur ne bloque pas le joueur en face de lui. Si il peut le bloquer, on regarde que le pion qu’il déplace est bien un pion qui gêne (retourné par must_move). Si ce n’est pas le cas, le mouvement est invalide. Ensuite, si c’est le dernier mouvement alors on vérifie que le joueur n’est pas stationné sur une branche. Si c’est bon, on retourne « jump_valid ». Ce retour permet à la fonction main de ne pas tenir compte du coup.

Ensuite on vérifie que la case du début du mouvement comporte bien un pion appartenant au joueur. Si ce n’est pas le premier mouvement d’un tour, on vérifie que la case au départ du déplacement est la même que celle qui a terminé le mouvement précédent.

Ensuite, si il s’agit du dernier mouvement d’un tour, on vérifie que la case de la fin du mouvement ne corresponde pas à la branche d’un joueur.

Ensuite on regarde chaque case située autour de la case au départ du mouvement. On a trois cas : Si la case est vide et que c’est là qu’on veut aller, le coup est autorisé. Si la case est occupée et que c’est là qu’on veut aller, le coup est invalide. Si la case est occupée et que ce n’est pas la qu’on veut aller, on refait ce test (uniquement les deux conditions précédentes) avec la case située après le pion voisin.

Une fois qu’on a regardé tous les voisins, si on n’a satisfait aucune condition, on considère naturellement que le coup est invalide.

fonction « winner »

La fonction « enum bool winner(struct player_t player, const size_t start_position[6][10], const struct game_state_t game) » permet de vérifier que tous les pions du joueur « player » sont situés sur la branche opposée à sa branche de départ.

fonction « save »

La fonction « void save(const char *filename, const struct player_t *player_state, const int nb_game, const int nb_game_end, const int nb_player, const time_t start_match, const int *winner, const time_t *duration, const char *const couleurs[6]) » permet de sauvegarder le résultat du match dans le fichier « filename ». Les paramètres de la fonctions correspondent : Le paramètre « filename » est le nom du fichier Le paramètre « *player_state » est le tableau des joueurs Le paramètre « nb_game » est le nombre de parties Le paramètre « nb_game_end » est le nombre de parties terminées (on peut avoir quitté le jeu avant la fin du match) Le paramètre « nb_player » est le nombre de joueurs Le paramètre « start_match » est le timestamp correspondant au moment du début du match Le paramètre « winner » est le tableau donnant le numéro du gagnant pour chaque partie Le paramètre « duration » est le tableau donnant la durée (en secondes) de chaque partie Le paramètre « couleurs » est le tableau donnant la couleur de chaque branche

fonction « main »

La fonction main gère le déroulement de la partie. La partie s’arrête si un seul joueur gagne ou si tous les joueurs sauf un ont perdu (un joueur ne va pas jouer tout seul).