#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
|
|
|
|
|
|
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 {
|
|
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);
|
|
}
|
|
|
|
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)();
|
|
int a; enum hole_t b;
|
|
void *c, *d;
|
|
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, int);
|
|
b = va_arg(ap, enum hole_t);
|
|
(*ia_function_pf)(a, b);
|
|
break;
|
|
case ia_next_move:
|
|
c = va_arg(ap, struct game_state_t*);
|
|
a = va_arg(ap, int);
|
|
d = va_arg(ap, struct move_t*);
|
|
*((int*) result) = (*(int(*)())ia_function_pf)(c, a, d); /* 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.\n", function_name);
|
|
#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) {
|
|
size_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;
|
|
if(move.start_pos < 0 || move.end_pos < 0 || move.start_pos > 120 || move.end_pos > 120)
|
|
return false;
|
|
|
|
/* on considère que c'est le premier saut (pas de vérification du collage) si le previous_move = (-1,-1) */
|
|
if(previous_move.start_pos == -1 && previous_move.end_pos == -1) {
|
|
if(game.board[move.start_pos] != player.branch ) /* on vérifie que la case appartient au joueur */
|
|
return false;
|
|
} else {
|
|
if(move.start_pos != previous_move.end_pos) /* on vérifie le collage */
|
|
return false;
|
|
}
|
|
|
|
if(last_move) /* si c'est le dernier mouvement, on ne stationne pas sur une branche */
|
|
for(j = (((player.branch-1)%3)+1)%6; j!=((player.branch-1)%3) ; j=(j+(j==((player.branch-1)%3)+2?2:1))%6 ) /* ne pas faire les branches de départ et d'arrivée */
|
|
if(search(start_position[j],10,move.end_pos))
|
|
return false;
|
|
|
|
/* on regarde autour */
|
|
j = 0;
|
|
do {
|
|
if((dest = move_calculation(move.start_pos, j)) != -1) {
|
|
switch(((dest == move.end_pos)?1:0)+((game.board[dest] == none)?2:0)) {
|
|
case 0:
|
|
/* saut */
|
|
if((dest2 = move_calculation(dest,j)) != -1) {
|
|
switch(((dest2 == move.end_pos)?1:0)+((game.board[dest2] == none)?2:0)) {
|
|
case 1:
|
|
return false;
|
|
case 3:
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
return false;
|
|
case 3:
|
|
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;
|
|
}
|
|
|
|
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_player_end;
|
|
int i, j;
|
|
|
|
|
|
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 */
|
|
j = 3;
|
|
for( i = 0; i < nb_player ; i++ ) {
|
|
/* 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
|
|
if( (player_state[i].ia_lib_p = dlopen(argv[j], RTLD_LAZY)) == NULL ) {
|
|
#ifdef debug
|
|
fprintf(stderr,"error while loading %s : %s (%d)\n", argv[j], dlerror(), 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, "toto");
|
|
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;
|
|
}
|
|
}
|
|
|
|
#ifdef debug
|
|
for( i=0 ; i<nb_player ; printf("%d\n", player_state[i++].ia_lib_p) );
|
|
puts("");
|
|
#endif
|
|
|
|
/* ouverture de la fenêtre graphique */
|
|
puts("(GUI) Opening window");
|
|
struct gui_resource_t gui_res = display_start(gui_res);
|
|
|
|
/* appel de start_match */
|
|
for( i=0 ; i < nb_player ; i++ )
|
|
if( player_state[i].ia_lib_p )
|
|
ia_call_function(player_state[i], ia_start_match, NULL, nb_player, i+1);
|
|
|
|
while( nb_game-- > 0 ) {
|
|
#ifdef debug
|
|
fprintf(stderr,"%d %s left\n", nb_game, nb_game>1?"games":"game");
|
|
#endif
|
|
|
|
/* on associe une branche à chaque joueur */
|
|
for( i=0 ; i < nb_player ; i++ )
|
|
player_state[i].branch = star_branch(nb_player, i);
|
|
|
|
/* 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);
|
|
/* display_text(gui_res, game_state, 1, "Début de la manche"); TODO */
|
|
|
|
#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) {
|
|
|
|
#ifdef debug
|
|
fprintf(stderr, "joueur %d\n", i);
|
|
#endif
|
|
game_state_copy = game_state;
|
|
first_move = 1;
|
|
do { /* on demande la suite de coup */
|
|
movement.start_pos=-1; movement.end_pos=-1;
|
|
previous_movement.start_pos=-1; previous_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 */
|
|
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 */
|
|
printf("(GUI) Moving pawn %d → hole %d\n", movement.start_pos, movement.end_pos);
|
|
display_animove_pawn(gui_res, game_state, player_state[i].branch, movement.start_pos, movement.end_pos);
|
|
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-1 /* la partie se termine */;
|
|
puts("le joueur a gagné");
|
|
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
|
|
/* display_text(gui_res, game_state, player_state[i].branch, "Coup non valide"); TODO*/
|
|
if(++(player_state[i].error)==3) {
|
|
nb_player_end++;
|
|
puts("perdu");
|
|
ia_call_function(player_state[i], ia_end_game, NULL);
|
|
}
|
|
next_move=0;
|
|
}
|
|
first_move=0;
|
|
} while(next_move);
|
|
/* 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 */
|
|
printf("(GUI) Rotating board, branch %d → %d\n", player_state[j].branch, player_state[i].branch);
|
|
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_text(gui_res, game_state, player_state[i].branch, "Manche terminée"); TODO*/
|
|
/* 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)%nb_player)], ia_end_game, NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* fermeture de la fenêtre graphique */
|
|
display_close(gui_res);
|
|
|
|
return 0;
|
|
}
|
|
|