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.
 
 
 
 

429 lines
16 KiB

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <dlfcn.h>
#include <time.h>
#include <SDL/SDL.h>
#include "arbitre.h"
#include "gui.h"
#define score_filename "scores"
enum api_function_t {ia_lib_init, ia_start_match, ia_start_game, ia_end_game , ia_end_match, ia_next_move};
enum bool char_to_int(const char *const argv, int *nombre) {
char* error;
error = (char*) argv;
*nombre=strtol(argv,&error,10);
return (error!=argv);
}
unsigned int number_length(int n) {
int ret=0;
n=(n<0?ret++,-n:n);
while(ret++, (n/=10) > 0);
return ret;
}
enum bool ia_call_function(const struct player_t player, const enum api_function_t api_function, void *const result, ...) {
va_list ap;
char* error;
enum bool res;
char* function_name;
void (*ia_function_pf)();
unsigned int a; int b; enum hole_t c;
void *d, *e;
if(!player.ia_lib_p) {
#ifdef debug
fputs("le joueur n'est pas une stratégie.\n",stderr);
#endif
return false;
}
va_start(ap, result);
function_name = NULL;
switch(api_function) {
case ia_lib_init:
function_name = "InitLibrary";
break;
case ia_start_match:
function_name = "StartMatch";
break;
case ia_start_game:
function_name = "StartGame";
break;
case ia_end_game:
function_name = "EndGame";
break;
case ia_end_match:
function_name = "EndMatch";
break;
case ia_next_move:
function_name = "NextMove";
break;
}
*(void **) (&ia_function_pf) = dlsym(player.ia_lib_p, function_name);
if( (error = (char*) dlerror()) == NULL ) {
switch(api_function) {
case ia_lib_init:
(*ia_function_pf)(va_arg (ap, char*));
break;
case ia_start_match:
a = va_arg(ap, unsigned int);
c = va_arg(ap, enum hole_t);
(*ia_function_pf)(a, c);
break;
case ia_next_move:
d = va_arg(ap, struct game_state_t*);
b = va_arg(ap, int);
e = va_arg(ap, struct move_t*);
*((int*) result) = (*(int(*)())ia_function_pf)(d, b, e); /* berk */
break;
default: /* par défaut la fonction est de type void sans paramètres */
(*ia_function_pf)();
break;
}
res = true;
} else {
#ifdef debug
fprintf(stderr,"la fonction \"%s\" n'a pas été trouvée dans la stratégie. (%s)\n", function_name, error);
#endif
res = false;
}
va_end(ap);
return res;
}
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]) {
FILE* file;
char *buffer;
int i;
if( (file=fopen(filename, "at")) != NULL) {
/* remove '\n' from ctime */
buffer = ctime(&start_match);
buffer[strlen(buffer)-1] = '\0';
fprintf(file, "=== %s (%d partie%s - %d joueurs) ===\n", buffer, nb_game, (nb_game>1)?"s":"", nb_player);
for(i=0; i < nb_game_end ; i++) {
if(duration[i] > 3600)
fprintf(file, "partie %d (%ld heure%s, %ld minute%s et %ld seconde%s) : ", i+1, (long int)(duration[i]/3600), ((duration[i]/3600)>1?"s":""), (long int)((duration[i]%3600)/60), ((((duration[i]%3600)/60)>1)?"s":""), (long int)((duration[i]%3600)%60), (((duration[i]%3600)%60)>1?"s":""));
else if(duration[i] > 60)
fprintf(file, "partie %d (%ld minute%s et %ld seconde%s) : ", i+1, (long int)(duration[i]/60), (((duration[i]/60)>1)?"s":""), (long int)(duration[i]%60), ((duration[i]%60)>1?"s":""));
else
fprintf(file, "partie %d (%ld second%s) : ", i+1, (long int)(duration[i]), ((duration[i]%60)>1?"s":""));
if(winner[i] >= 0 && winner[i] < nb_player)
fprintf(file, "gagnée par le joueur %s (%s)\n", couleurs[(player_state[winner[i]].branch)-1], (player_state[winner[i]].ia_lib_p)?player_state[winner[i]].name:"joueur réel");
else
fputs("aucun joueur n'a gagné\n", file);
}
if(nb_game != nb_game_end)
fputs("match interrompu brutalement\n", file);
fputs("\n", file);
fclose(file);
}
#ifdef debug
else
fprintf(stderr, "impossible d'ouvrir le fichier \"%s\"\n", filename);
#endif
}
int main(int argc, char **argv) {
const size_t start_position[6][10] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{19, 20, 21, 22, 32, 33, 34, 44, 45, 55},
{74, 84, 85, 95, 96, 97, 107, 108, 109, 110},
{111, 112, 113, 114, 115, 116, 117, 118, 119, 120},
{65, 75, 76, 86, 87, 88, 98, 99, 100, 101},
{10, 11, 12, 13, 23, 24, 25, 35, 36, 46}};
const char *const couleurs[6] = {"jaune", "noir", "bleu", "rouge", "vert", "violet"};
struct player_t player_state[6];
struct game_state_t game_state, game_state_copy, game_state_ia_copy;
struct move_t movement, previous_movement;
int first_move, next_move;
int nb_game, nb_player, nb_game_end, nb_player_end, start_pos_first_move;
int i, j, k;
char *buffer;
enum validation_movement_t validation_movement;
/* heure de début du match et tableaux pour stocker les gagnants et la durée des parties */
time_t time_start_match, *duration_games;
int *winner_games;
if (argc < 3 || argc > 9) {
fprintf(stderr, "%s nb_game nb_player [[ia]…].\n", argv[0]);
return 1;
}
/* on lit le nombre de parties */
if(!char_to_int(argv[1],&nb_game) || nb_game <= 0) {
fprintf(stderr, "nb_game (%s) doit être un nombre positif.\n", argv[1]);
return 2;
}
/* on lit le nombre de joueurs */
if(!char_to_int(argv[2],&nb_player) || nb_player < 2 || nb_player > 6 || nb_player == 5) {
fprintf(stderr, "nb_player (%s) doit être 2, 3, 4 ou 6.\n", argv[2]);
return 3;
}
/* on vérifie que le nombre de joueurs est supérieur au nombse d'ia passées en paramètrest */
if(nb_player < argc-3) {
fprintf(stderr, "%d IA précisées mais seulement %d joueurs.\n", argc-3, nb_player);
return 4;
}
srand(time(NULL));
/* on charge les IA et on initialise les joueurs « réels » */
j = 3;
for( i = 0; i < nb_player ; i++ ) {
/* on associe une branche à chaque joueur */
player_state[i].branch = star_branch(nb_player, i);
/* on prépare pour le nom */
snprintf(player_state[i].name,50,"Joueur %s", couleurs[(player_state[i].branch)-1]);
player_state[i].name[49] = '\0';
/* on choisit de placer un joueur réel ou une stratégie */
if( (rand()%(nb_player-i)) < (argc-j) ) {
/* on ajoute une stratégie */
#ifdef debug
fprintf(stderr,"strategie %s (%d)\n", argv[j], i);
#endif
player_state[i].ia_lib_p = dlopen(argv[j], RTLD_LAZY);
if( (buffer = (char*) dlerror()) != NULL ) {
#ifdef debug
fprintf(stderr,"erreur pendant le chargement de %s : %s (%d)\n", argv[j], buffer, i);
#endif
/* échec du chargement, on décharge toutes les stratégies précédement chargées */
for( j=0 ; j<i ; j++ )
if( player_state[j].ia_lib_p )
dlclose(player_state[j].ia_lib_p);
return 5;
} else
/* on initialise la bibliothèque */
ia_call_function(player_state[i], ia_lib_init, NULL, player_state[i].name); /* x = &x puisque c'est un tableau */
j++;
} else {
/* on ajoute un joueur réel */
#ifdef debug
fprintf(stderr,"joueur réel (%d)\n", i);
#endif
player_state[i].ia_lib_p = NULL;
}
/* petite vérification sur le nom */
#ifdef debug
if(player_state[i].name[49] != '\0')
fprintf(stderr,"attention : la longueur du nom du joueur %s doit être inférieur à 49 caractères.\n", couleurs[(player_state[i].branch)-1]);
#endif
player_state[i].name[49] = '\0';
}
/* on initialise les tableaux pour stocker les gagnants et la durée de chaque partie */
if((winner_games = (int*) malloc(nb_game*sizeof(int))) == NULL) {
#ifdef debug
fputs("malloc error\n", stderr);
#endif
return 6;
}
if((duration_games = (time_t*) malloc(nb_game*sizeof(time_t))) == NULL) {
#ifdef debug
fputs("malloc error\n", stderr);
#endif
return 6;
}
/* ouverture de la fenêtre graphique */
puts("(GUI) Opening window");
struct gui_resource_t gui_res = display_start(&gui_res);
/* dlsym inutile, sert à contourner le bug dlsym sdl sur openbsd */
dlsym(NULL,"");
dlerror();
for( i=0 ; i < nb_player ; i++ )
/* appel de start_match pour les joueurs présents */
if( player_state[i].ia_lib_p )
ia_call_function(player_state[i], ia_start_match, NULL, nb_player, player_state[i].branch);
/* on note l'heure du début du jeu */
time_start_match = time(NULL);
int quit = 0;
validation_movement = invalid;
for( nb_game_end = 0 ; nb_game_end < nb_game && !quit ; nb_game_end++ ) {
#ifdef debug
fprintf(stderr,"%d partie%s restante%s\n", nb_game, nb_game>1?"s":"", nb_game>1?"s":"");
#endif
duration_games[nb_game_end] = time(NULL);
winner_games[nb_game_end] = -1;
/* appel de start_game */
for( i=0 ; i < nb_player ; i++ )
if( player_state[i].ia_lib_p )
ia_call_function(player_state[i], ia_start_game, NULL);
/* initialisation du plateau */
for(i=0; i < 121; game_state.board[i++] = none);
/* initialisation : on place les pions */
for( i=0 ; i < nb_player ; i++ ) {
player_state[i].error = 0;
for( j=0 ; j < 10 ; j++ )
game_state.board[start_position[player_state[i].branch-1][j]] = player_state[i].branch;
}
/* rafraîchissement du plateau à l'écran */
printf("(GUI) Rendering board, branch %d\n", player_state[0].branch);
display_render_board(&gui_res, &game_state, player_state[0].branch, player_state[0].name);
SDL_Flip(gui_res.screen);
if( (buffer = (char*) malloc( (23+number_length(nb_game_end+1)+number_length(nb_game)) *sizeof(char) ) ) == NULL)
display_animsg(&gui_res, &game_state, player_state[0].branch, player_state[0].name, "Début de la manche", 2000);
else {
snprintf(buffer, 22+number_length(nb_game_end+1)+number_length(nb_game), "Début de la manche %d/%d", nb_game_end+1, nb_game);
display_animsg(&gui_res, &game_state, player_state[0].branch, player_state[0].name, buffer, 2000);
free(buffer);
}
/* chaque joueur joue */
i = 0;
nb_player_end = 0;
while(nb_player_end < nb_player-1 && !quit) {
#ifdef debug
fprintf(stderr, "joueur %s (%d) (%s)\n", couleurs[(player_state[i].branch)-1], i, player_state[i].name);
#endif
game_state_copy = game_state;
previous_movement.start_pos=-1; previous_movement.end_pos=-1;
start_pos_first_move = -1;
first_move = 1;
do { /* on demande la suite de coup */
/* si on a une stratégie */
if( player_state[i].ia_lib_p ) {
movement.start_pos=-1; movement.end_pos=-1;
game_state_ia_copy = game_state_copy;
ia_call_function(player_state[i], ia_next_move, &next_move, &game_state_ia_copy, first_move, &movement);
if(!quit) {
printf("(GUI) Moving pawn %d → hole %d\n", movement.start_pos, movement.end_pos);
if(first_move)
start_pos_first_move = movement.start_pos; /* on retient le point de départ pour éviter les coups qui reviendraient là où on est parti */
/* on cherche à valider le coup */
validation_movement = valid_move(start_pos_first_move, movement, previous_movement, player_state[i], game_state_copy, start_position, !next_move);
#ifdef debug
switch(validation_movement) {
case invalid: fputs("validation → invalide\n", stderr); break;
case jump: fputs("validation → jump\n", stderr); break;
case neighbour_valid: fputs("validation → voisin\n", stderr); break;
case jump_valid: fputs("validation → commit jump\n", stderr); break;
}
#endif
/* on effectue le mouvement à l'écran */
if(validation_movement != jump_valid && movement.start_pos != movement.end_pos && movement.start_pos >= 0 && movement.end_pos >= 0 && movement.start_pos < 121 && movement.end_pos < 121 && player_state[i].ia_lib_p)
quit += display_animove_pawn(&gui_res, game_state_copy, player_state[i].branch, player_state[i].name, movement.start_pos, movement.end_pos);
/* on regarde le résultat de la validation */
if(validation_movement == invalid) {
#ifdef debug
fprintf(stderr, "mouvement (%d,%d) invalide\n",movement.start_pos, movement.end_pos);
#endif
if( (buffer = (char*) malloc( 28 *sizeof(char) ) ) == NULL)
display_animsg(&gui_res, &game_state_copy, player_state[i].branch, player_state[i].name, "Coup non valide", 1000);
else {
snprintf(buffer, 28, "Coup non valide (%d erreur%s)", player_state[i].error+1, (player_state[i].error+1>1)?"s":"");
display_animsg(&gui_res, &game_state_copy, player_state[i].branch, player_state[i].name, buffer, 1500);
free(buffer);
}
if(++(player_state[i].error)==3) {
nb_player_end++;
puts("perdu");
/* on enlève les pions du joueur */
j=0; k=0;
do {
if(game_state.board[j] == player_state[i].branch) {
game_state.board[j] = none;
k++;
}
} while(++j < 121 && k < 10);
if( player_state[i].ia_lib_p )
ia_call_function(player_state[i], ia_end_game, NULL);
}
}
}
} else {
/* on a un joueur réel, on demande un coup tant qu'il n'est pas valide */
do {
movement.start_pos=-1; movement.end_pos=-1;
game_state_ia_copy = game_state_copy;
quit += display_usermove_pawn(&gui_res, &game_state_ia_copy, player_state[i].branch, player_state[i].name, &movement, &next_move);
if(first_move)
start_pos_first_move = movement.start_pos; /* on retient le point de départ pour éviter les coups qui reviendraient là où on est parti */
validation_movement = valid_move(start_pos_first_move, movement, previous_movement, player_state[i], game_state_copy, start_position, !next_move);
if(validation_movement == invalid && !quit)
display_animsg(&gui_res, &game_state_copy, player_state[i].branch, player_state[i].name, "Coup non valide, réessayer", 1000);
} while(validation_movement == invalid && !quit);
}
if(validation_movement == jump || validation_movement == neighbour_valid) {
/* on effectue le mouvement */
#ifdef debug
fprintf(stderr, "mouvement (%d,%d) valide\n",movement.start_pos, movement.end_pos);
#endif
game_state_copy.board[movement.start_pos]=none;
game_state_copy.board[movement.end_pos]=player_state[i].branch;
previous_movement=movement;
}
if(validation_movement == jump_valid || validation_movement == neighbour_valid) {
/* commit */
#ifdef debug
fputs("commit\n",stderr);
#endif
game_state=game_state_copy; /* commit */
if(winner(player_state[i], start_position, game_state)) {
nb_player_end=nb_player /* la partie se termine */;
puts("le joueur a gagné");
winner_games[nb_game_end] = i;
if( player_state[i].ia_lib_p )
ia_call_function(player_state[i], ia_end_game, NULL);
}
}
first_move=0;
} while(validation_movement == jump && nb_player_end < nb_player-1 && !quit);
/* on sélectionne le joueur suivant */
j=i;
do {
++i;
i%=nb_player;
} while(player_state[i].error >= 3 && !quit);
/* le joueur a joué, rotation du plateau à l'écran vers le suivant */
if( (nb_player_end < nb_player-1) && !quit) { // TODO condition identique au while, c'est pour quand on arrive à la fin de la partie, ne pas tourner le plateau
printf("(GUI) Rotating board, branch %d → %d\n", player_state[j].branch, player_state[i].branch);
quit += display_anirotate_board(&gui_res, &game_state, player_state[j].branch, player_state[j].name, player_state[i].branch + (j+1!=i)*6 /*places des joueurs actuel et suivant, ∈[1-6]*/, player_state[i].name);
}
}
puts("fin de la partie");
if(!quit)
display_animsg(&gui_res, &game_state, player_state[i].branch, player_state[i].name, "Manche terminée", 2000);
/* pour les joueurs qui n'ont pas perdu */
for (i=0; i < nb_player ; i++)
if( player_state[i].error < 3 && player_state[i].ia_lib_p )
ia_call_function(player_state[i], ia_end_game, NULL);
duration_games[nb_game_end] = time(NULL) - duration_games[nb_game_end];
}
/* appel de end_match */
for( i=0 ; i < nb_player ; i++ ) {
if( player_state[i].ia_lib_p ) {
ia_call_function(player_state[i], ia_end_match, NULL);
/* on décharge toutes les stratégies */
dlclose(player_state[i].ia_lib_p);
}
}
/* on enregistre le résultat du jeu dans le fichier */
save(score_filename, player_state, nb_game, nb_game_end-(quit?1:0), nb_player, time_start_match, winner_games, duration_games, couleurs);
free(winner_games);
free(duration_games);
/* fermeture de la fenêtre graphique */
display_close(&gui_res);
return 0;
}