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.
 
 
 
 

576 lines
19 KiB

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <dlfcn.h>
#include <time.h>
#include "api.h"
#include <SDL/SDL.h>
#include "gui.h"
#define start_pos startPos
#define end_pos endPos
#define debug
#define score_filename "scores"
enum bool {false, true};
enum direction_t { left, right, up_left, up_right, down_left, down_right };
enum api_function_t {ia_lib_init, ia_start_match, ia_start_game, ia_end_game , ia_end_match, ia_next_move};
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 */
};
enum bool char_to_int(char *argv, int *nombre) {
char* error;
error=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 *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("the player is not a strategy.\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=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,"function \"%s\" not found in strategy. (%s)\n", function_name, error);
#endif
res = false;
}
va_end(ap);
return res;
}
/* pour associer une branche de l'étoile à un joueur */
enum hole_t star_branch(const unsigned int nb_player, const size_t index) {
return ((nb_player==4)?index+(index>=2?1:0):(6/nb_player)*index)+1;
}
ssize_t move_calculation(const size_t index, const enum direction_t move) {
size_t index_lines[] = {0, 1, 3, 6, 10, 23, 35, 46, 56, 65, 75, 86, 98, 111, 115, 118, 120};
size_t index_lines_block[] = {0, 10, 65, 111};
size_t line, offset, block;
if(index < 0 || index > 120)
return -1;
line = 0, block = 0;
while( line < 18 && index_lines[line++] <= index );
while( block < 5 && index_lines_block[block++] <= index );
line -= 2;
block -= 2;
offset = index - index_lines[line];
/* printf("line %d, offset %d, block %d\n", line, offset,block); */
switch(move) {
case left:
return offset==0?-1:index-1;
break;
case right:
return index+1==index_lines[line+1]?-1:index+1;
break;
case up_left:
if(line==0 || ( block%2==0 && offset==0 ) || ( line == 4 && ( offset < 5 || offset > 8 ) ) )
return -1;
else
return index_lines[line-1]+offset+((block%2)-1)+(line==4?-5:0)+(line==13?4:0);
break;
case up_right:
if(line==0 || ( block%2==0 && index+1 == index_lines[line+1]) || ( line == 4 && ( offset < 4 || offset > 7) ) )
return -1;
else
return index_lines[line-1]+offset+(block%2)+(line==4?-5:0)+(line==13?4:0);
break;
case down_left:
if(line == 16 || (line != 8 && block%2==1 && offset==0) || (line == 12 && ( offset < 5 || offset > 8 ) ) )
return -1;
else
return index_lines[line+1]+offset-(block%2)+(line==3?4:0)+(line==8?1:0)+(line==12?-5:0);
break;
case down_right:
if(line == 16 || (block%2==1 && index+1 == index_lines[line+1]) || (line == 12 && ( offset < 4 || offset > 7 ) ) )
return -1;
else
return index_lines[line+1]+offset-((block%2)-1)+(line==3?4:0)+(line==8?1:0)+(line==12?-5:0);
break;
default:
return index;
break;
}
}
enum bool search(const size_t *tab, const size_t length, const size_t value) {
ssize_t first, last, middle;
first = 0;
last = length-1;
while(first <= last) {
middle = (last+first)/2;
if(tab[middle] == value)
return true;
if(tab[middle] < value)
first = middle+1;
else
last = middle-1;
}
return false;
}
enum bool valid_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) {
size_t j;
ssize_t dest, dest2;
#ifdef debug
printf("=== validation de %d → %d===\n", move.start_pos, move.end_pos);
#endif
if(move.start_pos < 0 || move.end_pos < 0 || move.start_pos > 120 || move.end_pos > 120)
return false;
/* on vérifie que la case appartient au joueur */
if(game.board[move.start_pos] != player.branch )
{
printf("la case %d n'appartient pas au joueur\n",move.start_pos);
printf("%d %d\n", game.board[move.start_pos], player.branch);
return false;
}
/* on vérifie que la case de destination est libre */
if(game.board[move.end_pos] != none) {
printf("la case %d de destination n'est pas libre\n", move.end_pos);
return false;
}
/* on vérifie le collage si ce n'est pas le premier mouvement */
if(previous_move.start_pos != -1 && previous_move.end_pos != -1 && move.start_pos != previous_move.end_pos) {
puts("collage not ok");
return false;
}
/* si c'est le dernier mouvement, on ne stationne pas sur une branche */
if(last_move) {
puts("vérification de ne pas stationner sur une branche");
/* ne pas faire les branches de départ et d'arrivée */
for(j = (((player.branch-1)%3)+1)%6; j!=((player.branch-1)%3) ; j=(j+(j==((player.branch-1)%3)+2?2:1))%6 ) {
if(search(start_position[j],10,move.end_pos))
return false;
}
}
/* on regarde autour */
puts("on regarde autour");
j = 0;
do {
if((dest = move_calculation(move.start_pos, j)) != -1) {
printf("%d → %d (j=%d) ?\n", move.start_pos, dest, j);
switch(((dest == move.end_pos)?1:0)+((game.board[dest] == none)?2:0)) {
case 0: /*on veut pas y aller mais la place est occupée. peut etre qu'on veut faire le saut */
if((dest2 = move_calculation(dest,j)) != -1) {
printf("%d → %d (j=%d) ?\n", dest, dest2, j);
switch(((dest2 == move.end_pos)?1:0)+((game.board[dest2] == none)?2:0)) {
case 1: /* on veut y aller mais la place est occupée */ return false;
case 3: /* on veut y aller et la place est libre */ return true;
}
}
break;
case 1: /*on veut y aller mais la place est occupée*/ return false;
case 3: /*on veut y aller et la place est libre*/ return (previous_move.start_pos == -1 && previous_move.end_pos == -1 && last_move );
}
}
} while(++j < 6);
return false;
}
enum bool winner(struct player_t player, const size_t start_position[6][10], const struct game_state_t game) {
size_t final_branch, j;
final_branch = (player.branch+2)%6; /* décalée de 1 (pour éviter de faire +1 et ensuite -1 lors de l'accès au tableau). */
j = 0;
do
if(game.board[start_position[final_branch][j]] != player.branch )
return false;
while(++j < 10);
return true;
}
void save(const char *filename, 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) {
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 game%s - %d players) ===\n", buffer, nb_game, (nb_game>1)?"s":"", nb_player);
for(i=0; i < nb_game_end ; i++) {
if(duration[i] > 3600)
fprintf(file, "game %d (%d hour%s %d minute%s %d second%s) : ", i+1, duration[i]/3600, ((duration[i]/3600)>1?"s":""), (duration[i]%3600)/60, ((((duration[i]%3600)/60)>1)?"s":""), (duration[i]%3600)%60, (((duration[i]%3600)%60)>1?"s":""));
else if(duration[i] > 60)
fprintf(file, "game %d (%d minute%s %d second%s) : ", i+1, duration[i]/60, (((duration[i]/60)>1)?"s":""), duration[i]%60, ((duration[i]%60)>1?"s":""));
else
fprintf(file, "game %d (%d second%s) : ", i+1, duration[i], ((duration[i]%60)>1?"s":""));
if(winner[i] >= 0 && winner[i] < nb_player)
fprintf(file, "won by player %d (%s)\n", winner[i]+1, player_state[winner[i]].name);
else
fputs("no player has won\n", file);
}
if(nb_game != nb_game_end)
fputs("match stopped abruptly\n", file);
fputs("\n", file);
fclose(file);
}
#ifdef debug
else
fprintf(stderr, "cannot open file \"%s\"\n", filename);
#endif
printf("→ %d \n", nb_game_end);
}
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}};
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;
int i, j, k;
char *buffer;
/* 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) must be a positive number.\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) must be 2, 3, 4 or 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 specified but only %d player%s.\n", argc-3, nb_player, nb_player==1?"":"s");
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 prépare pour le nom */
snprintf(player_state[i].name,50,"player %d",i+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,"strategy %s (%d)\n", argv[j], i);
#endif
player_state[i].ia_lib_p = dlopen(argv[j], RTLD_LAZY);
if( (buffer = dlerror()) != NULL ) {
#ifdef debug
fprintf(stderr,"error while loading %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,"real player (%d)\n", i);
#endif
player_state[i].ia_lib_p = NULL;
}
/* on vérifie le nom */
if(player_state[i].name[49] != '\0') {
#ifdef debug
fprintf(stderr,"warning : the length of the player's name %d must be to 49 characters maximum.\n", i);
#endif
} else
player_state[i].name[49] = '\0';
}
#ifdef debug
for( i=0 ; i<nb_player ; printf("%d\n", player_state[i++].ia_lib_p) );
puts("");
#endif
/* 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 à cause du bug dlsym sdl (il faut trouver une meilleure solution) */
dlsym(NULL,"");
dlerror();
for( i=0 ; i < nb_player ; i++ ) {
/* on associe une branche à chaque joueur */
player_state[i].branch = star_branch(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;
for( nb_game_end = 0 ; nb_game_end < nb_game && !quit ; nb_game_end++ ) {
#ifdef debug
fprintf(stderr,"%d %s left\n", nb_game, nb_game>1?"games":"game");
#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);
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, "Début de la manche", 1200);
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, buffer, 1200);
free(buffer);
}
#ifdef debug
for(i=0;i<121; i++)
if(game_state.board[i])
printf("%d %d\n",i, game_state.board[i]);
#endif
/* chaque joueur joue */
i = 0;
nb_player_end = 0;
while(nb_player_end < nb_player-1 && !quit) {
#ifdef debug
fprintf(stderr, "joueur %d (%s)\n", i, player_state[i].name);
#endif
game_state_copy = game_state;
previous_movement.start_pos=-1; previous_movement.end_pos=-1;
first_move = 1;
do { /* on demande la suite de coup */
movement.start_pos=-1; movement.end_pos=-1;
game_state_ia_copy = game_state_copy;
/* on suppose qu'on a une stratégie */
ia_call_function(player_state[i], ia_next_move, &next_move, &game_state_ia_copy, first_move, &movement);
/* on cherche à valider le coup */
printf("(GUI) Moving pawn %d → hole %d\n", movement.start_pos, movement.end_pos);
quit += display_animove_pawn(gui_res, game_state_copy, player_state[i].branch, movement.start_pos, movement.end_pos);
if(valid_move(movement, previous_movement, player_state[i], game_state_copy, start_position, !next_move)) {
#ifdef debug
fprintf(stderr, "mouvement (%d,%d) valide\n", movement.start_pos, movement.end_pos);
#endif
/* on effectue le mouvement */
game_state_copy.board[movement.start_pos]=none;
game_state_copy.board[movement.end_pos]=player_state[i].branch;
if(next_move)
previous_movement=movement;
else {
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;
ia_call_function(player_state[i], ia_end_game, NULL);
}
}
} else {
#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, player_state[i].branch, "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, player_state[i].branch, buffer, 1200);
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);
ia_call_function(player_state[i], ia_end_game, NULL);
}
next_move=0;
}
first_move=0;
} while(next_move && !quit);
/* on sélectionne le joueur suivant */
j=i;
do {
++i;
i%=nb_player;
} while(player_state[i].error >= 3);
/* 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[i].branch + (j+1!=i)*6 /*places des joueurs actuel et suivant, ∈[1-6]*/);
}
}
puts("fin de la partie");
display_animsg(gui_res, game_state, player_state[i].branch, "Manche terminée", 1200);
/* pour les joueurs qui n'ont pas perdu */
for (i=0; i < nb_player ; i++)
if(player_state[i].error < 3)
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);
free(winner_games);
free(duration_games);
/* fermeture de la fenêtre graphique */
display_close(gui_res);
return 0;
}