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.
 
 
 
 

430 lines
16 KiB

  1. #include <sys/types.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <stdarg.h>
  5. #include <dlfcn.h>
  6. #include <time.h>
  7. #include <SDL/SDL.h>
  8. #include "arbitre.h"
  9. #include "gui.h"
  10. #define score_filename "scores"
  11. enum api_function_t {ia_lib_init, ia_start_match, ia_start_game, ia_end_game , ia_end_match, ia_next_move};
  12. enum bool char_to_int(const char *const argv, int *nombre) {
  13. char* error;
  14. error = (char*) argv;
  15. *nombre=strtol(argv,&error,10);
  16. return (error!=argv);
  17. }
  18. unsigned int number_length(int n) {
  19. int ret=0;
  20. n=(n<0?ret++,-n:n);
  21. while(ret++, (n/=10) > 0);
  22. return ret;
  23. }
  24. enum bool ia_call_function(const struct player_t player, const enum api_function_t api_function, void *const result, ...) {
  25. va_list ap;
  26. char* error;
  27. enum bool res;
  28. char* function_name;
  29. void (*ia_function_pf)();
  30. unsigned int a; int b; enum hole_t c;
  31. void *d, *e;
  32. if(!player.ia_lib_p) {
  33. #ifdef debug
  34. fputs("le joueur n'est pas une stratégie.\n",stderr);
  35. #endif
  36. return false;
  37. }
  38. va_start(ap, result);
  39. function_name = NULL;
  40. switch(api_function) {
  41. case ia_lib_init:
  42. function_name = "InitLibrary";
  43. break;
  44. case ia_start_match:
  45. function_name = "StartMatch";
  46. break;
  47. case ia_start_game:
  48. function_name = "StartGame";
  49. break;
  50. case ia_end_game:
  51. function_name = "EndGame";
  52. break;
  53. case ia_end_match:
  54. function_name = "EndMatch";
  55. break;
  56. case ia_next_move:
  57. function_name = "NextMove";
  58. break;
  59. }
  60. *(void **) (&ia_function_pf) = dlsym(player.ia_lib_p, function_name);
  61. if( (error = (char*) dlerror()) == NULL ) {
  62. switch(api_function) {
  63. case ia_lib_init:
  64. (*ia_function_pf)(va_arg (ap, char*));
  65. break;
  66. case ia_start_match:
  67. a = va_arg(ap, unsigned int);
  68. c = va_arg(ap, enum hole_t);
  69. (*ia_function_pf)(a, c);
  70. break;
  71. case ia_next_move:
  72. d = va_arg(ap, struct game_state_t*);
  73. b = va_arg(ap, int);
  74. e = va_arg(ap, struct move_t*);
  75. *((int*) result) = (*(int(*)())ia_function_pf)(d, b, e); /* berk */
  76. break;
  77. default: /* par défaut la fonction est de type void sans paramètres */
  78. (*ia_function_pf)();
  79. break;
  80. }
  81. res = true;
  82. } else {
  83. #ifdef debug
  84. fprintf(stderr,"la fonction \"%s\" n'a pas été trouvée dans la stratégie. (%s)\n", function_name, error);
  85. #endif
  86. res = false;
  87. }
  88. va_end(ap);
  89. return res;
  90. }
  91. 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]) {
  92. FILE* file;
  93. char *buffer;
  94. int i;
  95. if( (file=fopen(filename, "at")) != NULL) {
  96. /* remove '\n' from ctime */
  97. buffer = ctime(&start_match);
  98. buffer[strlen(buffer)-1] = '\0';
  99. fprintf(file, "=== %s (%d partie%s - %d joueurs) ===\n", buffer, nb_game, (nb_game>1)?"s":"", nb_player);
  100. for(i=0; i < nb_game_end ; i++) {
  101. if(duration[i] > 3600)
  102. 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":""));
  103. else if(duration[i] > 60)
  104. 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":""));
  105. else
  106. fprintf(file, "partie %d (%ld second%s) : ", i+1, (long int)(duration[i]), ((duration[i]%60)>1?"s":""));
  107. if(winner[i] >= 0 && winner[i] < nb_player)
  108. 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");
  109. else
  110. fputs("aucun joueur n'a gagné\n", file);
  111. }
  112. if(nb_game != nb_game_end)
  113. fputs("match interrompu brutalement\n", file);
  114. fputs("\n", file);
  115. fclose(file);
  116. }
  117. #ifdef debug
  118. else
  119. fprintf(stderr, "impossible d'ouvrir le fichier \"%s\"\n", filename);
  120. #endif
  121. }
  122. int main(int argc, char **argv) {
  123. const size_t start_position[6][10] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
  124. {19, 20, 21, 22, 32, 33, 34, 44, 45, 55},
  125. {74, 84, 85, 95, 96, 97, 107, 108, 109, 110},
  126. {111, 112, 113, 114, 115, 116, 117, 118, 119, 120},
  127. {65, 75, 76, 86, 87, 88, 98, 99, 100, 101},
  128. {10, 11, 12, 13, 23, 24, 25, 35, 36, 46}};
  129. const char *const couleurs[6] = {"jaune", "noir", "bleu", "rouge", "vert", "violet"};
  130. struct player_t player_state[6];
  131. struct game_state_t game_state, game_state_copy, game_state_ia_copy;
  132. struct move_t movement, previous_movement;
  133. int first_move, next_move;
  134. int nb_game, nb_player, nb_game_end, nb_player_end, start_pos_first_move;
  135. int i, j, k;
  136. char *buffer;
  137. enum validation_movement_t validation_movement;
  138. /* heure de début du match et tableaux pour stocker les gagnants et la durée des parties */
  139. time_t time_start_match, *duration_games;
  140. int *winner_games;
  141. if (argc < 3 || argc > 9) {
  142. fprintf(stderr, "%s nb_game nb_player [[ia]…].\n", argv[0]);
  143. return 1;
  144. }
  145. /* on lit le nombre de parties */
  146. if(!char_to_int(argv[1],&nb_game) || nb_game <= 0) {
  147. fprintf(stderr, "nb_game (%s) doit être un nombre positif.\n", argv[1]);
  148. return 2;
  149. }
  150. /* on lit le nombre de joueurs */
  151. if(!char_to_int(argv[2],&nb_player) || nb_player < 2 || nb_player > 6 || nb_player == 5) {
  152. fprintf(stderr, "nb_player (%s) doit être 2, 3, 4 ou 6.\n", argv[2]);
  153. return 3;
  154. }
  155. /* on vérifie que le nombre de joueurs est supérieur au nombse d'ia passées en paramètrest */
  156. if(nb_player < argc-3) {
  157. fprintf(stderr, "%d IA précisées mais seulement %d joueurs.\n", argc-3, nb_player);
  158. return 4;
  159. }
  160. srand(time(NULL));
  161. /* on charge les IA et on initialise les joueurs « réels » */
  162. j = 3;
  163. for( i = 0; i < nb_player ; i++ ) {
  164. /* on associe une branche à chaque joueur */
  165. player_state[i].branch = star_branch(nb_player, i);
  166. /* on prépare pour le nom */
  167. snprintf(player_state[i].name,50,"Joueur %s", couleurs[(player_state[i].branch)-1]);
  168. player_state[i].name[49] = '\0';
  169. /* on choisit de placer un joueur réel ou une stratégie */
  170. if( (rand()%(nb_player-i)) < (argc-j) ) {
  171. /* on ajoute une stratégie */
  172. #ifdef debug
  173. fprintf(stderr,"strategie %s (%d)\n", argv[j], i);
  174. #endif
  175. player_state[i].ia_lib_p = dlopen(argv[j], RTLD_LAZY);
  176. if( (buffer = (char*) dlerror()) != NULL ) {
  177. #ifdef debug
  178. fprintf(stderr,"erreur pendant le chargement de %s : %s (%d)\n", argv[j], buffer, i);
  179. #endif
  180. /* échec du chargement, on décharge toutes les stratégies précédement chargées */
  181. for( j=0 ; j<i ; j++ )
  182. if( player_state[j].ia_lib_p )
  183. dlclose(player_state[j].ia_lib_p);
  184. return 5;
  185. } else
  186. /* on initialise la bibliothèque */
  187. ia_call_function(player_state[i], ia_lib_init, NULL, player_state[i].name); /* x = &x puisque c'est un tableau */
  188. j++;
  189. } else {
  190. /* on ajoute un joueur réel */
  191. #ifdef debug
  192. fprintf(stderr,"joueur réel (%d)\n", i);
  193. #endif
  194. player_state[i].ia_lib_p = NULL;
  195. }
  196. /* petite vérification sur le nom */
  197. #ifdef debug
  198. if(player_state[i].name[49] != '\0')
  199. fprintf(stderr,"attention : la longueur du nom du joueur %s doit être inférieur à 49 caractères.\n", couleurs[(player_state[i].branch)-1]);
  200. #endif
  201. player_state[i].name[49] = '\0';
  202. }
  203. /* on initialise les tableaux pour stocker les gagnants et la durée de chaque partie */
  204. if((winner_games = (int*) malloc(nb_game*sizeof(int))) == NULL) {
  205. #ifdef debug
  206. fputs("malloc error\n", stderr);
  207. #endif
  208. return 6;
  209. }
  210. if((duration_games = (time_t*) malloc(nb_game*sizeof(time_t))) == NULL) {
  211. #ifdef debug
  212. fputs("malloc error\n", stderr);
  213. #endif
  214. return 6;
  215. }
  216. /* ouverture de la fenêtre graphique */
  217. puts("(GUI) Opening window");
  218. struct gui_resource_t gui_res = display_start(&gui_res);
  219. /* dlsym inutile, sert à contourner le bug dlsym sdl sur openbsd */
  220. dlsym(NULL,"");
  221. dlerror();
  222. for( i=0 ; i < nb_player ; i++ )
  223. /* appel de start_match pour les joueurs présents */
  224. if( player_state[i].ia_lib_p )
  225. ia_call_function(player_state[i], ia_start_match, NULL, nb_player, player_state[i].branch);
  226. /* on note l'heure du début du jeu */
  227. time_start_match = time(NULL);
  228. int quit = 0;
  229. validation_movement = invalid;
  230. for( nb_game_end = 0 ; nb_game_end < nb_game && !quit ; nb_game_end++ ) {
  231. #ifdef debug
  232. fprintf(stderr,"%d partie%s restante%s\n", nb_game, nb_game>1?"s":"", nb_game>1?"s":"");
  233. #endif
  234. duration_games[nb_game_end] = time(NULL);
  235. winner_games[nb_game_end] = -1;
  236. /* appel de start_game */
  237. for( i=0 ; i < nb_player ; i++ )
  238. if( player_state[i].ia_lib_p )
  239. ia_call_function(player_state[i], ia_start_game, NULL);
  240. /* initialisation du plateau */
  241. for(i=0; i < 121; game_state.board[i++] = none);
  242. /* initialisation : on place les pions */
  243. for( i=0 ; i < nb_player ; i++ ) {
  244. player_state[i].error = 0;
  245. for( j=0 ; j < 10 ; j++ )
  246. game_state.board[start_position[player_state[i].branch-1][j]] = player_state[i].branch;
  247. }
  248. /* rafraîchissement du plateau à l'écran */
  249. printf("(GUI) Rendering board, branch %d\n", player_state[0].branch);
  250. display_render_board(&gui_res, &game_state, player_state[0].branch, player_state[0].name);
  251. SDL_Flip(gui_res.screen);
  252. if( (buffer = (char*) malloc( (23+number_length(nb_game_end+1)+number_length(nb_game)) *sizeof(char) ) ) == NULL)
  253. display_animsg(&gui_res, &game_state, player_state[0].branch, player_state[0].name, "Début de la manche", 2000);
  254. else {
  255. 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);
  256. display_animsg(&gui_res, &game_state, player_state[0].branch, player_state[0].name, buffer, 2000);
  257. free(buffer);
  258. }
  259. /* chaque joueur joue */
  260. i = 0;
  261. nb_player_end = 0;
  262. while(nb_player_end < nb_player-1 && !quit) {
  263. #ifdef debug
  264. fprintf(stderr, "joueur %s (%d) (%s)\n", couleurs[(player_state[i].branch)-1], i, player_state[i].name);
  265. #endif
  266. game_state_copy = game_state;
  267. previous_movement.start_pos=-1; previous_movement.end_pos=-1;
  268. start_pos_first_move = -1;
  269. first_move = 1;
  270. do { /* on demande la suite de coup */
  271. /* si on a une stratégie */
  272. if( player_state[i].ia_lib_p ) {
  273. movement.start_pos=-1; movement.end_pos=-1;
  274. game_state_ia_copy = game_state_copy;
  275. ia_call_function(player_state[i], ia_next_move, &next_move, &game_state_ia_copy, first_move, &movement);
  276. if(!quit) {
  277. printf("(GUI) Moving pawn %d → hole %d\n", movement.start_pos, movement.end_pos);
  278. if(first_move)
  279. 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 */
  280. /* on cherche à valider le coup */
  281. validation_movement = valid_move(start_pos_first_move, movement, previous_movement, player_state[i], game_state_copy, start_position, !next_move);
  282. #ifdef debug
  283. switch(validation_movement) {
  284. case invalid: fputs("validation → invalide\n", stderr); break;
  285. case jump: fputs("validation → jump\n", stderr); break;
  286. case neighbour_valid: fputs("validation → voisin\n", stderr); break;
  287. case jump_valid: fputs("validation → commit jump\n", stderr); break;
  288. }
  289. #endif
  290. /* on effectue le mouvement à l'écran */
  291. 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)
  292. quit += display_animove_pawn(&gui_res, game_state_copy, player_state[i].branch, player_state[i].name, movement.start_pos, movement.end_pos);
  293. /* on regarde le résultat de la validation */
  294. if(validation_movement == invalid) {
  295. #ifdef debug
  296. fprintf(stderr, "mouvement (%d,%d) invalide\n",movement.start_pos, movement.end_pos);
  297. #endif
  298. if( (buffer = (char*) malloc( 28 *sizeof(char) ) ) == NULL)
  299. display_animsg(&gui_res, &game_state_copy, player_state[i].branch, player_state[i].name, "Coup non valide", 1000);
  300. else {
  301. snprintf(buffer, 28, "Coup non valide (%d erreur%s)", player_state[i].error+1, (player_state[i].error+1>1)?"s":"");
  302. display_animsg(&gui_res, &game_state_copy, player_state[i].branch, player_state[i].name, buffer, 1500);
  303. free(buffer);
  304. }
  305. if(++(player_state[i].error)==3) {
  306. nb_player_end++;
  307. puts("perdu");
  308. /* on enlève les pions du joueur */
  309. j=0; k=0;
  310. do {
  311. if(game_state.board[j] == player_state[i].branch) {
  312. game_state.board[j] = none;
  313. k++;
  314. }
  315. } while(++j < 121 && k < 10);
  316. if( player_state[i].ia_lib_p )
  317. ia_call_function(player_state[i], ia_end_game, NULL);
  318. }
  319. }
  320. }
  321. } else {
  322. /* on a un joueur réel, on demande un coup tant qu'il n'est pas valide */
  323. do {
  324. movement.start_pos=-1; movement.end_pos=-1;
  325. game_state_ia_copy = game_state_copy;
  326. quit += display_usermove_pawn(&gui_res, &game_state_ia_copy, player_state[i].branch, player_state[i].name, &movement, &next_move);
  327. if(first_move)
  328. 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 */
  329. validation_movement = valid_move(start_pos_first_move, movement, previous_movement, player_state[i], game_state_copy, start_position, !next_move);
  330. if(validation_movement == invalid && !quit)
  331. display_animsg(&gui_res, &game_state_copy, player_state[i].branch, player_state[i].name, "Coup non valide, réessayer", 1000);
  332. } while(validation_movement == invalid && !quit);
  333. }
  334. if(validation_movement == jump || validation_movement == neighbour_valid) {
  335. /* on effectue le mouvement */
  336. #ifdef debug
  337. fprintf(stderr, "mouvement (%d,%d) valide\n",movement.start_pos, movement.end_pos);
  338. #endif
  339. game_state_copy.board[movement.start_pos]=none;
  340. game_state_copy.board[movement.end_pos]=player_state[i].branch;
  341. previous_movement=movement;
  342. }
  343. if(validation_movement == jump_valid || validation_movement == neighbour_valid) {
  344. /* commit */
  345. #ifdef debug
  346. fputs("commit\n",stderr);
  347. #endif
  348. game_state=game_state_copy; /* commit */
  349. if(winner(player_state[i], start_position, game_state)) {
  350. nb_player_end=nb_player /* la partie se termine */;
  351. puts("le joueur a gagné");
  352. winner_games[nb_game_end] = i;
  353. if( player_state[i].ia_lib_p )
  354. ia_call_function(player_state[i], ia_end_game, NULL);
  355. }
  356. }
  357. first_move=0;
  358. } while(validation_movement == jump && nb_player_end < nb_player-1 && !quit);
  359. /* on sélectionne le joueur suivant */
  360. j=i;
  361. do {
  362. ++i;
  363. i%=nb_player;
  364. } while(player_state[i].error >= 3 && !quit);
  365. /* le joueur a joué, rotation du plateau à l'écran vers le suivant */
  366. 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
  367. printf("(GUI) Rotating board, branch %d → %d\n", player_state[j].branch, player_state[i].branch);
  368. 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);
  369. }
  370. }
  371. puts("fin de la partie");
  372. if(!quit)
  373. display_animsg(&gui_res, &game_state, player_state[i].branch, player_state[i].name, "Manche terminée", 2000);
  374. /* pour les joueurs qui n'ont pas perdu */
  375. for (i=0; i < nb_player ; i++)
  376. if( player_state[i].error < 3 && player_state[i].ia_lib_p )
  377. ia_call_function(player_state[i], ia_end_game, NULL);
  378. duration_games[nb_game_end] = time(NULL) - duration_games[nb_game_end];
  379. }
  380. /* appel de end_match */
  381. for( i=0 ; i < nb_player ; i++ ) {
  382. if( player_state[i].ia_lib_p ) {
  383. ia_call_function(player_state[i], ia_end_match, NULL);
  384. /* on décharge toutes les stratégies */
  385. dlclose(player_state[i].ia_lib_p);
  386. }
  387. }
  388. /* on enregistre le résultat du jeu dans le fichier */
  389. save(score_filename, player_state, nb_game, nb_game_end-(quit?1:0), nb_player, time_start_match, winner_games, duration_games, couleurs);
  390. free(winner_games);
  391. free(duration_games);
  392. /* fermeture de la fenêtre graphique */
  393. display_close(&gui_res);
  394. return 0;
  395. }