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.
 
 
 
 

441 lines
14 KiB

#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;
}