Browse Source

Merge branch 'master' of ssh://ssh.confais.org/git/dames_chinoises

master
Nikos 8 years ago
parent
commit
727c342bbf
9 changed files with 691 additions and 457 deletions
  1. +6
    -3
      Makefile
  2. +3
    -0
      ai/Makefile
  3. +1
    -1
      ai/ai.c
  4. +38
    -10
      ai/strategy.c
  5. +2
    -2
      ai/strategy.h
  6. +170
    -0
      ai/test.c
  7. +437
    -0
      arbitre.c
  8. +32
    -0
      arbitre.h
  9. +2
    -441
      main.c

+ 6
- 3
Makefile View File

@ -8,15 +8,15 @@ LDFLAGS=$(shell sdl-config --libs) -dynamic
CFLAGS=$(shell sdl-config --cflags) -O
endif
WARNINGS=-Wall -Wextra -Wformat
WARNINGS=-Wall -Wextra -Wformat -Wpointer-sign -Wsign-compare
all : main dummy ai guidemo
api.h : dameschinoises.h dameschinoises.patch
patch -o api.h dameschinoises.h dameschinoises.patch
main : main.o stack.o gui
gcc main.o stack.o gui.o -o main -pedantic $(LDFLAGS) -lSDL -lSDL_image -lSDL_gfx -lSDL_ttf -lm $(WARNINGS)
main : main.o stack.o arbitre.o gui
gcc main.o stack.o arbitre.o gui.o -o main -pedantic $(LDFLAGS) -lSDL -lSDL_image -lSDL_gfx -lSDL_ttf -lm $(WARNINGS)
main.o : main.c api.h
gcc -c main.c $(CFLAGS) $(WARNINGS)
@ -24,6 +24,9 @@ main.o : main.c api.h
stack.o : stack.c
gcc -c stack.c $(CFLAGS) $(WARNINGS)
arbitre.o : arbitre.c
gcc -c arbitre.c $(CFLAGS) $(WARNINGS)
dummy : dummy.c
gcc dummy.c -o dummy -fPIC -shared -nostartfiles $(WARNINGS)


+ 3
- 0
ai/Makefile View File

@ -7,6 +7,9 @@ EXECUTABLE=ai.so
CFLAGS=-fPIC -g -std=gnu99 -Wall
LDFLAGS=-shared
test: $(OBJECTS) $(EXECUTABLE)
$(CC) -o $@ $(OBJECTS)
all: $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)


+ 1
- 1
ai/ai.c View File

@ -124,7 +124,7 @@ int NextMove(const SGameState* const gameState, int firstMove, SMove* move)
return 0;
}
g_move = getBestMove(piecesMoves);
g_move = getBestMove(piecesMoves, gameState);
if (g_move.score == INT_MIN)
{


+ 38
- 10
ai/strategy.c View File

@ -92,7 +92,7 @@ int findAllPiecesValidMoves(const SGameState* state, const EPlayer color, Tree**
return movables;
}
Choice treeToMoveChoice(Tree* root)
Choice treeToMoveChoice(Tree* root, const SGameState* const state)
{
int* array;
int size = getParents(root, &array);
@ -115,7 +115,7 @@ Choice treeToMoveChoice(Tree* root)
Choice c;
c.move = array;
c.size = size;
c.score = moveScore(c);
c.score = moveScore(c, state);
return c;
}
@ -157,7 +157,34 @@ int distance(int a, int b)
return distance;
}
int moveScore(Choice move)
int nearestEmptyDestinationHole(const int hole, const SGameState* const state)
{
int destination[10];
for (int i=0, j=0; i<121; i++)
{
if (isStarBranch(i) == ((g_color+2) % 6) + 1)
destination[j++] = i;
}
int min = -1;
int minDist = INT_MAX;
for (int i=0; i<10; i++)
{
if (state->board[destination[i]] != none)
continue;
int dist = distance(hole, destination[i]);
if (dist < minDist)
{
min = i;
minDist = dist;
}
}
return destination[min];
}
int moveScore(Choice move, const SGameState* const state)
{
if (move.move == NULL)
return INT_MIN;
@ -170,17 +197,18 @@ int moveScore(Choice move)
}
return (distance(move.move[0], g_destination) - distance(move.move[move.size - 1], g_destination)) * 5 // +5 pour chaque case parcourue vers l'avant, -5 pour celles parcourues vers l'arrière
+ (distance(move.move[0], g_destination)) // +1 point pour chaque case séparant le pion de l'arrivée (pour ne pas trop laisser les pions derrière)
return (distance(move.move[0], g_destination) - distance(move.move[move.size - 1], g_destination)) * 8 // +8 pour chaque case parcourue vers l'avant, -8 pour celles parcourues vers l'arrière
+ (distance(move.move[0], g_destination)) * 2 // +2 points pour chaque case séparant le pion de l'arrivée (pour ne pas trop laisser les pions derrière)
+ (distance(move.move[0], nearestEmptyDestinationHole(move.move[0], state)) - (distance(move.move[move.size - 1], nearestEmptyDestinationHole(move.move[0], state)))) // On tente de se rapprocher de la case d'arrivée vide la plus proche
;
}
List getPieceBestMove(Tree* root, List bestMoves)
List getPieceBestMove(Tree* root, List bestMoves, const SGameState* const state)
{
if (root == NULL)
return bestMoves;
Choice tmp = treeToMoveChoice(root);
Choice tmp = treeToMoveChoice(root, state);
if (tmp.move != NULL)
{
@ -204,19 +232,19 @@ List getPieceBestMove(Tree* root, List bestMoves)
}
for (int i=0; i<6; i++)
bestMoves = getPieceBestMove(root->child[i], bestMoves);
bestMoves = getPieceBestMove(root->child[i], bestMoves, state);
return bestMoves;
}
Choice getBestMove(Tree* moves[])
Choice getBestMove(Tree* moves[], const SGameState* const state)
{
List bestMoves = NULL;
for (int i=0; i<10; i++)
{
List tmp = getPieceBestMove(moves[i], NULL);
List tmp = getPieceBestMove(moves[i], NULL, state);
if (tmp == NULL)
continue;


+ 2
- 2
ai/strategy.h View File

@ -16,7 +16,7 @@ typedef struct Choice Choice;
Tree* findPieceValidMoves(const SGameState* state, const int hole);
int findAllPiecesValidMoves(const SGameState* state, const EPlayer color, Tree** moves);
int moveScore(Choice move);
Choice getBestMove(Tree* moves[]);
int moveScore(Choice move, const SGameState* const state);
Choice getBestMove(Tree* moves[], const SGameState* const state);
#endif

+ 170
- 0
ai/test.c View File

@ -0,0 +1,170 @@
#include "ai.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "gamestate.h"
#include "strategy.h"
#include "list.h"
#include <limits.h>
void casesJoueur (SGameState g, EPlayer p, int * tab)
{
int i=0,j=0;
for(i=0; i<121; i++)
{
if( g.board[i] == p )
{
tab[j] = i; //tab[j] recupere lindice i de la case qui contient le pion du joueur p
j++;
}
}
}
SGameState * initGameState ( int n )
{
int i = 0;
SGameState * gameState = ( SGameState * ) malloc ( sizeof( SGameState ) );
for (i = 0; i < 121; i++)
{
gameState->board[i] = 0;
}
for (i = 0; i < 121; i++)
{
if( i < 10 )
gameState->board[i] = 1;
else if( (10 <= i && i <= 13) || (23 <= i && i <= 25) || (35 <= i && i <= 36) || (i == 46) )
gameState->board[i] = 2;
else if( (19 <= i && i <= 22) || (32 <= i && i <= 34) || (44 <= i && i <= 45) || (i == 55) )
gameState->board[i] = 3;
else if( (98 <= i && i <= 101) || (86 <= i && i <= 88) || (75 <= i && i <= 76) || (i == 65) )
gameState->board[i] = 4;
else if( (107 <= i && i <= 110) || (95 <= i && i <= 97) || (84 <= i && i <= 85) || (i == 74) )
gameState->board[i] = 5;
else if( 111 <= i )
gameState->board[i] = 6;
}
return( gameState );
}
void afficherGameState (SGameState gameState)
{
int i, j, offset = 12, dir = 1, pawn = 0;
for(i = 0; i < 17; i++) { /* 17 lignes */
if(i%4 == 0) /* inversion de la pente toutes les 4 lignes */
dir = -dir;
if(i == 4) /* base du 2nd triangle */
offset = 0;
else if(i == 13) /* base du 1er triangle */
offset = 9;
putchar('\t');
for(j = 0; j < 13; j++) { /* 13 colonnes */
if(j < offset)
putchar(' ');
else
printf("%1d ", gameState.board[pawn++]);
}
putchar('\n');
offset += dir; /* continuer sur la pente */
}
putchar('\n');
}
void afficherDestination (const SGameState* const state)
{
int i, j, offset = 12, dir = 1, pawn = 0;
for(i = 0; i < 17; i++) { /* 17 lignes */
if(i%4 == 0) /* inversion de la pente toutes les 4 lignes */
dir = -dir;
if(i == 4) /* base du 2nd triangle */
offset = 0;
else if(i == 13) /* base du 1er triangle */
offset = 9;
putchar('\t');
for(j = 0; j < 13; j++) { /* 13 colonnes */
if(j < offset)
putchar(' ');
else
printf("%1d ", nearestEmptyDestinationHole(pawn++, state));
}
putchar('\n');
offset += dir; /* continuer sur la pente */
}
putchar('\n');
}
int main()
{
char name[50];
InitLibrary(name);
SGameState state;
bzero(state.board, 121*sizeof(int));
for (int i=0; i<9; i++)
state.board[i] = color4;
state.board[15] = color4;
StartMatch(6, color4);
StartGame();
afficherDestination(&state);
afficherGameState(state);
SMove m;
while (1)
{
int firstMove = 1;
while (NextMove(&state, firstMove, &m))
{
state.board[m.endPos] = state.board[m.startPos];
state.board[m.startPos] = none;
printf("Test : %d\t%d\n", m.startPos, m.endPos);
afficherGameState(state);
firstMove = 0;
if (distance(m.startPos, m.endPos) == 1)
break;
}
printf("\n\nNextMove\n\n");
getchar();
}
//printf("%lu\n", (long unsigned int)sizeof(LCell));
EndGame();
EndMatch();
return 0;
}
/*
#include "queue.h"
int main()
{
Queue* q = NULL;
push(&q, (Tree*) 1);
debugQueue(q);
printf("%p\n", pop(&q));
debugQueue(q);
return 0;
}
*/

+ 437
- 0
arbitre.c View File

@ -0,0 +1,437 @@
#include <stdlib.h>
#include <stdio.h>
#include "arbitre.h"
#include "api.h"
#define STACK_TYPE size_t
#include "stack.h"
/* recherche dichotomique */
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;
}
/* 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;
}
/* trouver l'indice d'une case voisine */
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};
ssize_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];
switch(move) {
case left:
return offset==0?-1:((ssize_t)index)-1;
case right:
return index+1==index_lines[line+1]?-1:((ssize_t)index)+1;
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);
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);
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);
case down_right:
if(line == 16 || (block%2==1 && index+1 == index_lines[line+1] && index != 64) || (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);
default:
return index;
break;
}
}
/* nombre de mouvements possibles */
int nb_possible_move(const struct game_state_t game, const struct player_t player) {
int res, i,j,k;
ssize_t dest, dest2;
res = 0; i = 0; j = 0;
do {
if(game.board[i] == player.branch) {
j++;
/* on regarde si on peut faire un déplacement */
k = 0;
do {
if( (dest = move_calculation( (size_t) i, k)) != -1 ) {
if(game.board[dest] == none)
res++;
else
if(((dest2 = move_calculation(dest,k)) != -1) && (game.board[dest2] == none))
res++;
}
} while(++k < 6);
}
} while(++i < 121 && j < 10);
#ifdef debug
fprintf(stderr, "%d cases voisines atteignables pour le joueurs\n", res);
#endif
return res;
}
/* vérifie si la case à l'index est sur la branche d'un autre joueur */
enum bool pawn_on_branch(const enum hole_t player_branch, const size_t index, const size_t start_position[6][10]) {
size_t j;
/* 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,index)) {
#ifdef debug
fputs("arret sur la branche d'un autre joueur\n", stderr);
#endif
return true;
}
return false;
}
#if (allow_to_block_opposite_player == 0)
/* calcule la distance jusqu'à une branche */
int distance_to_branch(const enum hole_t branch, const size_t index) {
const size_t index_lines[] = {0, 1, 3, 6, 10, 23, 35, 46, 56, 65, 75, 86, 98, 111, 115, 118, 120};
const size_t coord_branch[6][2] = {{0, 6}, {4, 12}, {12, 12}, {16, 0}, {12, 0}, {4, 6}};
const size_t offset_add[9] = {6, 5, 5, 4, 0, 0, 1, 1, 2};
size_t line, offset;
int res;
if(/* index < 0 || */ index > 120 || branch < 1 || branch > 6)
return -1;
line = 0;
while( line < 18 && index_lines[line++] <= index );
line -= 2;
offset = index - index_lines[line] + (line>8?offset_add[16-line]:offset_add[line]);
res = 0;
if(offset < coord_branch[branch-1][1])
res += (coord_branch[branch-1][1]-offset);
else
res += (offset-coord_branch[branch-1][1]);
if(line < coord_branch[branch-1][0])
res += (coord_branch[branch-1][0]-line);
else
res += (line-coord_branch[branch-1][0]);
return res;
}
/* vérifie si le joueur doit quitter sa branche car il gène le joueur d'en face */
enum bool must_move(const struct player_t player, const struct game_state_t game, const size_t start_position[6][10], size_t *res) {
ssize_t dest, dest2;
int distance, distance1;
size_t i, j, start_hole, player_pawn, hole;
enum bool tried[121], pawn_to_move[121];
struct stack_cell_t *stack;
enum bool must_move, no_pawn_in_start_zone;
for(i=0 ; i<121 ; pawn_to_move[i++]=false);
for(i=0 ; i<10 ; res[i++]=121);
must_move = true;
stack = NULL;
/* on vérifie que le joueur a au moins un pion dans sa zone de départ */
no_pawn_in_start_zone = true;
for(hole=0; hole < 10 && no_pawn_in_start_zone ; hole++)
if(game.board[start_position[player.branch-1][hole]] == player.branch)
no_pawn_in_start_zone = false;
if(no_pawn_in_start_zone) {
#ifdef debug
fputs("pas de pion dans la zone de départ\n", stderr);
#endif
return false;
}
/* on regarde sur le plateau les pions du joueur d'en face */
hole = 0;
player_pawn = 0;
do {
if(game.board[hole] == ((player.branch+2)%6)+1) {
player_pawn++;
distance = distance_to_branch(player.branch, hole);
for(i=0 ; i<121 ; tried[i++]=false);
/* on regarde là ou on peut aller en un mouvement */
j=0;
do {
if( (dest = move_calculation( (size_t) hole, j)) != -1) {
distance1 = distance_to_branch(player.branch, dest);
if(game.board[dest] == none && distance1 < distance && !pawn_on_branch(((player.branch+2)%6)+1,dest,start_position)) {
printf("coup qui progresse : %lu (%d) %lu (%d)\n", hole, distance, dest, distance1);
must_move = false; /* on a un coup qui progresse */
}
if(game.board[dest] == player.branch && search(start_position[player.branch-1],10,dest) && distance1 < distance) {
pawn_to_move[dest] = true; /* on aurait un coup qui progresse si on déplace le pion sur dest */
}
if(game.board[dest] != none) {
if((dest2 = move_calculation(dest,j)) != -1) {
distance1 = distance_to_branch(player.branch, dest2);
if(game.board[dest2] == none && distance1 < distance && !pawn_on_branch(((player.branch+2)%6)+1,dest2,start_position)) {
printf("coup qui progresse : %lu (%d) %lu (%d)\n", hole, distance, dest2, distance1);
must_move = false; /* on a un coup qui progresse */
}
else if(game.board[dest2] == player.branch && search(start_position[player.branch-1],10,dest2) && distance1 < distance)
pawn_to_move[dest] = true; /* on aurait un coup qui progresse si on déplace le pion sur dest2 */
if(game.board[dest2] == none)
stack_push(&stack, (size_t) dest2);
}
}
}
} while(++j < 6 && must_move);
tried[hole] = true;
/* pour tous les sauts, peut on en faire d'autres ? */
while(stack && must_move) {
start_hole = *(stack_pop(&stack));
if(!tried[start_hole]) {
j = 0;
do {
if( (dest = move_calculation(start_hole, j)) != -1 && game.board[dest] != none && (dest2 = move_calculation(dest,j)) != -1) {
distance1 = distance_to_branch(player.branch, dest2);
if(game.board[dest2] == none && distance1 < distance && !pawn_on_branch(((player.branch+2)%6)+1,dest2,start_position)) {
printf("coup qui progresse : %lu (%d) %lu (%d)\n", hole, distance, dest2, distance1);
must_move = false; /* on a un coup qui progresse */
}
else if(game.board[dest2] == player.branch && search(start_position[player.branch-1],10,dest2) && distance1 < distance)
pawn_to_move[dest] = true; /* on aurait un coup qui progresse si on déplace le pion sur dest2 */
if(game.board[dest2] == none)
stack_push(&stack, (size_t) dest2);
}
} while(++j < 6 && must_move);
tried[start_hole] = true;
}
}
/* on vide la pile */
if(!must_move) {
while(stack)
stack_pop(&stack);
}
}
}while(++hole < 121 && player_pawn < 10 && must_move);
/* s'il n'y a pas de joueur en face */
if(player_pawn < 10)
return false;
if(must_move) {
/* aucun coup qui progresse donc on doit sortir de sa branche */
#ifdef debug
fputs("le joueur peut déplacer les pions ", stderr);
#endif
for(hole=0, player_pawn=0;hole<121 && player_pawn < 10;hole++)
if(pawn_to_move[hole]) {
#ifdef debug
fprintf(stderr, "%lu ", hole);
#endif
res[player_pawn++]=hole; /* le tableau est trié donc on pourra faire une recherche dichotomique */
}
#ifdef debug
fputs("\n",stderr);
#endif
}
/* si aucun coup ne bloque */
if(player_pawn == 0) {
#ifdef debug
fputs("n'importe quel coup est autorisé\n", stderr);
#endif
return false;
}
#ifdef debug
else
fputs("n'importe quel coup est autorisé\n", stderr);
#endif
return must_move;
}
#endif
/* vérifie que le mouvement est valide */
enum validation_movement_t valid_move(const int start_pos_first_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, pawn[10];
ssize_t dest, dest2;
#ifdef debug
fprintf(stderr, "=== validation de %d → %d ===\n", move.start_pos, move.end_pos);
#endif
/* si c'est le dernier mouvement et le premier, on vérifie que le joueur est bloqué */
if(last_move && previous_move.start_pos == -1 && previous_move.end_pos == -1) {
#ifdef debug
fputs("vérification si le joueur a retourné 0 pour next_move. On vérifie si il est bloqué\n", stderr);
#endif
if(nb_possible_move(game, player)==0) {
#ifdef debug
fputs("le joueur est effectivement bloqué\n", stderr);
#endif
return jump_valid;
} else {
#ifdef debug
fputs("le joueur pouvait faire un mouvement\n", stderr);
#endif
return invalid;
}
// return (nb_possible_move(game, player)==0)?jump_valid:invalid;
}
#if (allow_to_block_opposite_player == 0)
/* si c'est le premier mouvement, on vérifie qu'on ne va pas bloquer le joueur d'en face */
if(previous_move.start_pos == -1 && previous_move.end_pos == -1 && must_move(player,game, start_position, pawn)) {
#ifdef debug
fputs("le joueur peut bloquer son adversaire.\n", stderr);
#endif
for(j=0;j<10 && pawn[j]!=121;j++);
if(!search(pawn,--j, move.start_pos)) {
#ifdef debug
fputs("le joueur bloque son adversaire. Il doit déplacer un autre pion\n", stderr);
#endif
return invalid;
}
}
#endif
/* si c'est le dernier mouvement, on ne stationne pas sur une branche */
if(last_move && previous_move.start_pos != -1 && previous_move.end_pos != -1) {
#ifdef debug
fputs("vérification de ne pas stationner sur une branche\n", stderr);
#endif
return (pawn_on_branch(player.branch, previous_move.end_pos, start_position) || (start_pos_first_move == previous_move.end_pos && ! allow_to_returning_to_the_starting_point_in_a_round ))?invalid:jump_valid;
}
if(move.start_pos < 0 || move.end_pos < 0 || move.start_pos > 120 || move.end_pos > 120) {
#ifdef debug
fputs("l'indice de la case n'est pas compris entre 0 et 120\n", stderr);
#endif
return invalid;
}
/* on vérifie que la case appartient au joueur */
if(game.board[move.start_pos] != player.branch ) {
#ifdef debug
fprintf(stderr, "la case %d n'appartient pas au joueur\n",move.start_pos);
#endif
return invalid;
}
/* si ce n'est pas le premier mouvement, on peut rester sur place */
if(previous_move.start_pos != -1 && previous_move.end_pos != -1 && move.start_pos == move.end_pos)
return jump;
/* on vérifie que la case de destination est libre (testé quand même dans le switch plus bas) */
/* if(game.board[move.end_pos] != none) {
#ifdef debug
fprintf(stderr, "la case %d de destination n'est pas libre\n", move.end_pos);
#endif
return invalid;
}
*/
/* 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) {
#ifdef debug
fputs("le pion déplacé n'est pas le même qu'au mouvement précédent\n", stderr);
#endif
return invalid;
}
/* on regarde autour */
#ifdef debug
fputs("on regarde les voisins\n", stderr);
#endif
j = 0;
do {
if( (dest = move_calculation( (size_t) move.start_pos, j)) != -1 ) {
#ifdef debug
fprintf(stderr, "%d → %zu (j=%zu) ?\n", move.start_pos, dest, j);
#endif
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) {
#ifdef debug
fprintf(stderr, "%zu → %zu (j=%zu) ?\n", dest, dest2, j);
#endif
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 (déjà traité plus haut) */ return invalid;
case 3: /* on veut y aller et la place est libre */ return jump;
}
}
break;
case 1: /*on veut y aller mais la place est occupée (déjà traité plus haut) */
return invalid;
case 3: /*on veut y aller et la place est libre*/
if(previous_move.start_pos == -1 && previous_move.end_pos == -1) {
if(!pawn_on_branch(player.branch, move.end_pos, start_position)) {
if(!last_move) {
return neighbour_valid;
} else {
#ifdef debug
fputs("next_move doit etre à 1\n", stderr);
#endif
return invalid;
}
} else {
#ifdef debug
fputs("la case voisine est sur une branche de l'étoile\n", stderr);
#endif
return invalid;
}
} else {
#ifdef debug
fputs("on ne peut atteindre un voisin que sur le premier mouvement\n", stderr);
#endif
return invalid;
}
// return (previous_move.start_pos == -1 && previous_move.end_pos == -1 && !pawn_on_branch(player.branch, move.end_pos, start_position) && !last_move)?neighbour_valid:invalid;
}
}
} while(++j < 6);
#ifdef debug
fprintf(stderr, "%d ne fait pas partie des voisins\n", move.end_pos);
#endif
return invalid;
}
/* vérifie si le joueur a gagné */
enum bool winner(const 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;
}

+ 32
- 0
arbitre.h View File

@ -0,0 +1,32 @@
#define start_pos startPos
#define end_pos endPos
#define debug
#define allow_to_returning_to_the_starting_point_in_a_round 1
#define allow_to_block_opposite_player 0
#ifndef arbitre_h
#define arbitre_h
#include "api.h"
enum bool {false, true};
enum validation_movement_t {neighbour_valid, jump, jump_valid, invalid};
enum direction_t { left, right, up_left, up_right, down_left, down_right };
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 */
};
/* pour associer une branche de l'étoile à un joueur */
enum hole_t star_branch(const unsigned int nb_player, const size_t index);
/* vérifie que le mouvement est valide */
enum validation_movement_t valid_move(const int start_pos_first_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);
/* vérifie si le joueur a gagné */
enum bool winner(const struct player_t player, const size_t start_position[6][10], const struct game_state_t game);
#endif

+ 2
- 441
main.c View File

@ -9,31 +9,12 @@
#include <SDL/SDL.h>
#include "gui.h"
#define STACK_TYPE size_t
#include "stack.h"
#include "arbitre.h"
#define start_pos startPos
#define end_pos endPos
#define debug
#define score_filename "scores"
#define allow_to_returning_to_the_starting_point_in_a_round 1
#define allow_to_block_opposite_player 0
enum bool {false, true};
enum validation_movement_t {neighbour_valid, jump, jump_valid, invalid};
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(const char *const argv, int *nombre) {
char* error;
error = (char*) argv;
@ -116,426 +97,6 @@ enum bool ia_call_function(const struct player_t player, const enum api_function
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};
ssize_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];
switch(move) {
case left:
return offset==0?-1:((ssize_t)index)-1;
case right:
return index+1==index_lines[line+1]?-1:((ssize_t)index)+1;
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);
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);
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);
case down_right:
if(line == 16 || (block%2==1 && index+1 == index_lines[line+1] && index != 64) || (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);
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;
}
int nb_possible_move(const struct game_state_t game, const struct player_t player) {
int res, i,j,k;
ssize_t dest, dest2;
res = 0; i = 0; j = 0;
do {
if(game.board[i] == player.branch) {
j++;
/* on regarde si on peut faire un déplacement */
k = 0;
do {
if( (dest = move_calculation( (size_t) i, k)) != -1 ) {
if(game.board[dest] == none)
res++;
else
if(((dest2 = move_calculation(dest,k)) != -1) && (game.board[dest2] == none))
res++;
}
} while(++k < 6);
}
} while(++i < 121 && j < 10);
#ifdef debug
fprintf(stderr, "%d cases voisines atteignables pour le joueurs\n", res);
#endif
return res;
}
enum bool pawn_on_branch(const enum hole_t player_branch, const size_t index, const size_t start_position[6][10]) {
size_t j;
/* 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,index)) {
#ifdef debug
fputs("arret sur la branche d'un autre joueur\n", stderr);
#endif
return true;
}
return false;
}
#if (allow_to_block_opposite_player == 0)
int distance_to_branch(const enum hole_t branch, const size_t index) {
const size_t index_lines[] = {0, 1, 3, 6, 10, 23, 35, 46, 56, 65, 75, 86, 98, 111, 115, 118, 120};
const size_t coord_branch[6][2] = {{0, 6}, {4, 12}, {12, 12}, {16, 0}, {12, 0}, {4, 6}};
const size_t offset_add[9] = {6, 5, 5, 4, 0, 0, 1, 1, 2};
size_t line, offset;
int res;
if(/* index < 0 || */ index > 120 || branch < 1 || branch > 6)
return -1;
line = 0;
while( line < 18 && index_lines[line++] <= index );
line -= 2;
offset = index - index_lines[line] + (line>8?offset_add[16-line]:offset_add[line]);
res = 0;
if(offset < coord_branch[branch-1][1])
res += (coord_branch[branch-1][1]-offset);
else
res += (offset-coord_branch[branch-1][1]);
if(line < coord_branch[branch-1][0])
res += (coord_branch[branch-1][0]-line);
else
res += (line-coord_branch[branch-1][0]);
return res;
}
enum bool must_move(const struct player_t player, const struct game_state_t game, const size_t start_position[6][10], size_t *res) {
ssize_t dest, dest2;
int distance, distance1;
size_t i, j, start_hole, player_pawn, hole;
enum bool tried[121], pawn_to_move[121];
struct stack_cell_t *stack;
enum bool must_move, no_pawn_in_start_zone;
for(i=0 ; i<121 ; pawn_to_move[i++]=false);
for(i=0 ; i<10 ; res[i++]=121);
must_move = true;
stack = NULL;
/* on vérifie que le joueur a au moins un pion dans sa zone de départ */
no_pawn_in_start_zone = true;
for(hole=0; hole < 10 && no_pawn_in_start_zone ; hole++)
if(game.board[start_position[player.branch-1][hole]] == player.branch)
no_pawn_in_start_zone = false;
if(no_pawn_in_start_zone) {
#ifdef debug
fputs("pas de pion dans la zone de départ\n", stderr);
#endif
return false;
}
/* on regarde sur le plateau les pions du joueur d'en face */
hole = 0;
player_pawn = 0;
do {
if(game.board[hole] == ((player.branch+2)%6)+1) {
player_pawn++;
distance = distance_to_branch(player.branch, hole);
for(i=0 ; i<121 ; tried[i++]=false);
/* on regarde là ou on peut aller en un mouvement */
j=0;
do {
if( (dest = move_calculation( (size_t) hole, j)) != -1) {
distance1 = distance_to_branch(player.branch, dest);
if(game.board[dest] == none && distance1 < distance && !pawn_on_branch(((player.branch+2)%6)+1,dest,start_position)) {
printf("coup qui progresse : %lu (%d) %lu (%d)\n", hole, distance, dest, distance1);
must_move = false; /* on a un coup qui progresse */
}
if(game.board[dest] == player.branch && search(start_position[player.branch-1],10,dest) && distance1 < distance) {
pawn_to_move[dest] = true; /* on aurait un coup qui progresse si on déplace le pion sur dest */
}
if(game.board[dest] != none) {
if((dest2 = move_calculation(dest,j)) != -1) {
distance1 = distance_to_branch(player.branch, dest2);
if(game.board[dest2] == none && distance1 < distance && !pawn_on_branch(((player.branch+2)%6)+1,dest2,start_position)) {
printf("coup qui progresse : %lu (%d) %lu (%d)\n", hole, distance, dest2, distance1);
must_move = false; /* on a un coup qui progresse */
}
else if(game.board[dest2] == player.branch && search(start_position[player.branch-1],10,dest2) && distance1 < distance)
pawn_to_move[dest] = true; /* on aurait un coup qui progresse si on déplace le pion sur dest2 */
if(game.board[dest2] == none)
stack_push(&stack, (size_t) dest2);
}
}
}
} while(++j < 6 && must_move);
tried[hole] = true;
/* pour tous les sauts, peut on en faire d'autres ? */
while(stack && must_move) {
start_hole = *(stack_pop(&stack));
if(!tried[start_hole]) {
j = 0;
do {
if( (dest = move_calculation(start_hole, j)) != -1 && game.board[dest] != none && (dest2 = move_calculation(dest,j)) != -1) {
distance1 = distance_to_branch(player.branch, dest2);
if(game.board[dest2] == none && distance1 < distance && !pawn_on_branch(((player.branch+2)%6)+1,dest2,start_position)) {
printf("coup qui progresse : %lu (%d) %lu (%d)\n", hole, distance, dest2, distance1);
must_move = false; /* on a un coup qui progresse */
}
else if(game.board[dest2] == player.branch && search(start_position[player.branch-1],10,dest2) && distance1 < distance)
pawn_to_move[dest] = true; /* on aurait un coup qui progresse si on déplace le pion sur dest2 */
if(game.board[dest2] == none)
stack_push(&stack, (size_t) dest2);
}
} while(++j < 6 && must_move);
tried[start_hole] = true;
}
}
/* on vide la pile */
if(!must_move) {
while(stack)
stack_pop(&stack);
}
}
}while(++hole < 121 && player_pawn < 10 && must_move);
/* s'il n'y a pas de joueur en face */
if(player_pawn < 10)
return false;
if(must_move) {
/* aucun coup qui progresse donc on doit sortir de sa branche */
#ifdef debug
fputs("le joueur peut déplacer les pions ", stderr);
#endif
for(hole=0, player_pawn=0;hole<121 && player_pawn < 10;hole++)
if(pawn_to_move[hole]) {
#ifdef debug
fprintf(stderr, "%lu ", hole);
#endif
res[player_pawn++]=hole; /* le tableau est trié donc on pourra faire une recherche dichotomique */
}
#ifdef debug
fputs("\n",stderr);
#endif
}
/* si aucun coup ne bloque */
if(player_pawn == 0) {
#ifdef debug
fputs("n'importe quel coup est autorisé\n", stderr);
#endif
return false;
}
#ifdef debug
else
fputs("n'importe quel coup est autorisé\n", stderr);
#endif
return must_move;
}
#endif
enum validation_movement_t valid_move(const int start_pos_first_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, pawn[10];
ssize_t dest, dest2;
#ifdef debug
fprintf(stderr, "=== validation de %d → %d ===\n", move.start_pos, move.end_pos);
#endif
/* si c'est le dernier mouvement et le premier, on vérifie que le joueur est bloqué */
if(last_move && previous_move.start_pos == -1 && previous_move.end_pos == -1) {
#ifdef debug
fputs("vérification si le joueur a retourné 0 pour next_move. On vérifie si il est bloqué\n", stderr);
#endif
if(nb_possible_move(game, player)==0) {
#ifdef debug
fputs("le joueur est effectivement bloqué\n", stderr);
#endif
return jump_valid;
} else {
#ifdef debug
fputs("le joueur pouvait faire un mouvement\n", stderr);
#endif
return invalid;
}
// return (nb_possible_move(game, player)==0)?jump_valid:invalid;
}
#if (allow_to_block_opposite_player == 0)
/* si c'est le premier mouvement, on vérifie qu'on ne va pas bloquer le joueur d'en face */
if(previous_move.start_pos == -1 && previous_move.end_pos == -1 && must_move(player,game, start_position, pawn)) {
#ifdef debug
fputs("le joueur peut bloquer son adversaire.\n", stderr);
#endif
for(j=0;j<10 && pawn[j]!=121;j++);
if(!search(pawn,--j, move.start_pos)) {
#ifdef debug
fputs("le joueur bloque son adversaire. Il doit déplacer un autre pion\n", stderr);
#endif
return invalid;
}
}
#endif
/* si c'est le dernier mouvement, on ne stationne pas sur une branche */
if(last_move && previous_move.start_pos != -1 && previous_move.end_pos != -1) {
#ifdef debug
fputs("vérification de ne pas stationner sur une branche\n", stderr);
#endif
return (pawn_on_branch(player.branch, previous_move.end_pos, start_position) || (start_pos_first_move == previous_move.end_pos && ! allow_to_returning_to_the_starting_point_in_a_round ))?invalid:jump_valid;
}
if(move.start_pos < 0 || move.end_pos < 0 || move.start_pos > 120 || move.end_pos > 120) {
#ifdef debug
fputs("l'indice de la case n'est pas compris entre 0 et 120\n", stderr);
#endif
return invalid;
}
/* on vérifie que la case appartient au joueur */
if(game.board[move.start_pos] != player.branch ) {
#ifdef debug
fprintf(stderr, "la case %d n'appartient pas au joueur\n",move.start_pos);
#endif
return invalid;
}
/* si ce n'est pas le premier mouvement, on peut rester sur place */
if(previous_move.start_pos != -1 && previous_move.end_pos != -1 && move.start_pos == move.end_pos)
return jump;
/* on vérifie que la case de destination est libre */
/* if(game.board[move.end_pos] != none) {
#ifdef debug
fprintf(stderr, "la case %d de destination n'est pas libre\n", move.end_pos);
#endif
return invalid;
}
*/
/* 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) {
#ifdef debug
fputs("le pion déplacé n'est pas le même qu'au mouvement précédent\n", stderr);
#endif
return invalid;
}
/* on regarde autour */
#ifdef debug
fputs("on regarde les voisins\n", stderr);
#endif
j = 0;
do {
if( (dest = move_calculation( (size_t) move.start_pos, j)) != -1 ) {
#ifdef debug
fprintf(stderr, "%d → %zu (j=%zu) ?\n", move.start_pos, dest, j);
#endif
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) {
#ifdef debug
fprintf(stderr, "%zu → %zu (j=%zu) ?\n", dest, dest2, j);
#endif
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 (déjà traité plus haut) */ return invalid;
case 3: /* on veut y aller et la place est libre */ return jump;
}
}
break;
case 1: /*on veut y aller mais la place est occupée (déjà traité plus haut) */
return invalid;
case 3: /*on veut y aller et la place est libre*/
if(previous_move.start_pos == -1 && previous_move.end_pos == -1) {
if(!pawn_on_branch(player.branch, move.end_pos, start_position)) {
if(!last_move) {
return neighbour_valid;
} else {
#ifdef debug
fputs("next_move doit etre à 1\n", stderr);
#endif
return invalid;
}
} else {
#ifdef debug
fputs("la case voisine est sur une branche de l'étoile\n", stderr);
#endif
return invalid;
}
} else {
#ifdef debug
fputs("on ne peut atteindre un voisin que sur le premier mouvement\n", stderr);
#endif
return invalid;
}
// return (previous_move.start_pos == -1 && previous_move.end_pos == -1 && !pawn_on_branch(player.branch, move.end_pos, start_position) && !last_move)?neighbour_valid:invalid;
}
}
} while(++j < 6);
#ifdef debug
fprintf(stderr, "%d ne fait pas partie des voisins\n", move.end_pos);
#endif
return invalid;
}
enum bool winner(const 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, 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;
@ -553,7 +114,7 @@ void save(const char *filename, const struct player_t *player_state, const int n
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]].name);
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);
}


Loading…
Cancel
Save