Commit 340183e2 authored by Yves Combe's avatar Yves Combe

awele board by frederic Mazzarol

parent aa51028e
2005-12-18 Yves Combe <yves@ycombe.net>
awele board by Frederic Mazzarol. First version. Great!
Not yet in POTFILES
xml.in not finished
* boards/Makefile.am:
* boards/awele.xml.in:
* boards/awele/awele_frame.jpg:
* boards/awele/awele_frame.png:
* boards/awele/awele_frame_avec_messages.png:
* boards/awele/bouton1.png:
* boards/awele/bouton1_clic.png:
* boards/awele/bouton1_notify.png:
* boards/awele/bouton2.png:
* boards/awele/bouton2_clic.png:
* boards/awele/bouton2_notify.png:
* boards/awele/bouton3.png:
* boards/awele/bouton3_clic.png:
* boards/awele/bouton3_notify.png:
* boards/awele/bouton4.png:
* boards/awele/bouton4_clic.png:
* boards/awele/bouton4_notify.png:
* boards/awele/bouton5.png:
* boards/awele/bouton5_clic.png:
* boards/awele/bouton5_notify.png:
* boards/awele/bouton6.png:
* boards/awele/bouton6_clic.png:
* boards/awele/bouton6_notify.png:
* boards/awele/graine1.png:
* boards/awele/graine2.png:
* boards/awele/graine3.png:
* boards/awele/graine4.png:
* boards/awele/graine4bis.png:
* boards/awele/nv_partie.png:
* boards/awele/nv_partie_clic.png:
* boards/awele/nv_partie_notify.png:
* boards/boardicons/awele.png:
* src/boards/Makefile.am:
* src/boards/awele.c: (pause_board), (start_board), (end_board),
(is_our_board), (awele_next_level), (awele_destroy_all_items),
(awele_create_item), (game_won), (initBoardGraphics),
(buttonClick), (updateNbBeans), (updateCapturedBeans),
(buttonNewGameClick):
* src/boards/awele.h:
* src/boards/awele_alphaBeta.c: (eval), (think), (alphabeta),
(threatenDelta), (moveDelta):
* src/boards/awele_utils.c: (isOpponentHungry), (isValidMove),
(move), (switch_player), (randplay), (isEndOfGame), (testMove),
(create_awale), (awale_equal), (create_tree), (destroy_tree),
(destroy_awale):
* src/boards/awele_utils.h:
2005-12-14 Bruno coudoin <bruno.coudoin@free.fr>
- Minor fixes for windows version
......
......@@ -13,6 +13,7 @@ xml_in_files = \
algebramenu.xml.in \
algorithm.xml.in \
anim.xml.in \
awele.xml.in \
babymatch.xml.in \
babyshapes.xml.in \
bargame.xml.in \
......
<?xml version="1.0" encoding="UTF-8"?>
<GCompris>
<Board
name="awele"
type="awele"
section="/boards"
difficulty="0"
icon="boardicons/awele.png"
author="Frédéric Mazzarol"
boarddir="awele">
<_title>awele</_title>
<_description> TODO </_description>
<_prerequisite> TODO </_prerequisite>
<_goal> TODO </_goal>
<_manual> TODO
</_manual>
</Board>
</GCompris>
......@@ -29,6 +29,7 @@ SUBDIRS= $(python_subdir)
lib_LTLIBRARIES = \
libalgebra.la \
libalgebra_guesscount.la \
libawele.la \
libcanal_lock.la \
libchess.la \
libclick_on_letter.la \
......@@ -82,6 +83,10 @@ libdraw_la_LDFLAGS = -module -avoid-version
libdraw_la_LIBADD = $(top_builddir)/src/gcompris/libgcompris-1.la
libdraw_la_SOURCES = draw.c
libawele_la_LDFLAGS = -module -avoid-version
libawele_la_LIBADD = $(top_builddir)/src/gcompris/libgcompris-1.la
libawele_la_SOURCES = awele.c awele_alphaBeta.c awele_utils.c
libmenu_la_LDFLAGS = -module -avoid-version
libmenu_la_LIBADD = $(top_builddir)/src/gcompris/libgcompris-1.la
libmenu_la_SOURCES = menu.c
......
This diff is collapsed.
#define BOUTON "awele/bouton1.png" //Chemin relatif vers fichiers boutons
#define BOUTON_NOTIFY "awele/bouton1_notify.png" //Chemin relatif vers fichiers boutons cliqus
#define BOUTON_CLIC "awele/bouton1_clic.png" //Chemin relatif vers fichiers boutons cliqus
#define NEWGAME "awele/nv_partie.png"
#define NEWGAME_NOTIFY "awele/nv_partie_notify.png"
#define NEWGAME_CLIC "awele/nv_partie_clic.png"
#define BEAN "awele/graine1.png" //Chemin relatif vers fichiers graines
#define Y_BOUTONS 412 //Abcisse des boutons
#define WIDTH 800 // Largeur Fenetre
#define HEIGHT 600 // Hauteur Fenetre
/**
* Tableau de graines
*/
typedef struct {
GnomeCanvasItem *beanPixbuf;
char hole;
}BEANHOLE_LINK;
typedef struct {
GnomeCanvasItem *msg; //Item message pour dialogue avec utilisateur
GnomeCanvasItem *nbBeansHole[12]; //Tableau d'item affichant le nbre de graine par trou.
BEANHOLE_LINK *ptBeansHoleLink; //pointeur sur structures stockant les item graines et la case dans laquelle elles se trouvent.
GnomeCanvasItem *button[6]; //Item des boutons (affichs avec pixbufButton)
GdkPixbuf *pixbufButtonNotify[6]; //pixbux des boutons notifis
GdkPixbuf *pixbufButton[6]; //pixbuf des boutons(selection de la case a jouer)
GdkPixbuf *pixbufButtonClicked[6]; //pixbuf des boutons cliqus
GnomeCanvasItem *Captures[2]; //Tableau d'item affichage nbre graine captures.
GdkPixbuf *pixbufBeans[4]; //pixbufs des graines
GdkPixbuf *pixbufButtonNewGame;
GdkPixbuf *pixbufButtonNewGameNotify;
GdkPixbuf *pixbufButtonNewGameClicked;
GnomeCanvasItem *ButtonNewGame;
} GRAPHICS_ELT;
typedef struct {
short int numeroCase;
GRAPHICS_ELT *graphsElt;
} CALLBACK_ARGS;
/*
* Fonctions de traitement des vnements, signaux et rappels
*/
gint eventDelete (GtkWidget * widget, GdkEvent *event, gpointer data);
gint eventDestroy (GtkWidget * widget, GdkEvent *event, gpointer data);
gint buttonClick (GtkWidget *item, GdkEvent *event, gpointer data);
gint buttonNewGameClick (GtkWidget *item, GdkEvent *event, gpointer data);
/**
* Fonctions Mise a jour de l'affichage
*/
BEANHOLE_LINK * updateNbBeans (GnomeCanvasItem *nbBeansHole[NBHOLE], GnomeCanvasGroup *rootGroup, BEANHOLE_LINK *ptLink, int alpha);
void updateCapturedBeans (GnomeCanvasItem *Captures[2]);
void initBoardGraphics (GRAPHICS_ELT *graphsElt);
#include "awele_utils.h"
#include <string.h>
#include <stdlib.h>
int maxprof;
static void alphabeta( TREE * t , short int alpha , short int beta );
/**
* Fonction d'evaluation d'un plateau
* La fonction d'evaluation va evaluer la difference du nombre de graines capturées (Facteur preponderant),\n
* la difference de la mobilité des deux joueurs, la difference des cases menaçantes,\n
* et la difference du nombre de graine active de chaque joueur.\n
* @param AWALE *aw Pointeur sur la structure AWALE a évaluer
* @return Une note d'evaluation du plateau.
*/
int eval (AWALE *aw){
int score;//, attacDelta, mobilityDelta;
score = aw->CapturedBeans[COMPUTER] - aw->CapturedBeans[HUMAN];
//attacDelta = threatenDelta(aw)*33;
/*if ((aw->CapturedBeans[COMPUTER] + aw->CapturedBeans[HUMAN]) > 30)
mobilityDelta = 0;
else
mobilityDelta = moveDelta(aw)*22;*/
if (aw->player == HUMAN){
score = -score;
//attacDelta = -attacDelta;
//mobilityDelta = -mobilityDelta;
}
//score = score*100 + mobilityDelta + attacDelta;
return score;
}
/**
* Fonction de jeu de la machine
* Cette Fonction est appelée pour faire jouer l'ordinateur, \n
* la racine de l'arbre est crée, puis passé en argument à la fonction AlphaBeta\n
* La profondeur augmente au fur et mesure de la partie quand le nombre de graines diminue.\n
* @param aw Un pointeur sur le plateau à partir duquel reflechir
* @return Le meilleur coup calculé par la machine
*/
short int think( AWALE *a, short int level){
TREE * t ;
int npris ;
short int best;
maxprof = level ;
/*augmente la profondeur quand le nombre de pieces diminue */
npris = a->CapturedBeans[HUMAN] + a->CapturedBeans[COMPUTER] ;
if ( npris > 20 ) maxprof ++ ;
if ( npris > 25 ) maxprof ++ ;
if ( npris > 30 ) maxprof ++ ;
if ( npris > 35 ) maxprof ++ ;
if ( npris > 40 ) maxprof ++ ;
/* initialisation de l'arbre */
t = create_tree( 0 , a->player , a ) ;
/* recherche meilleur coup */
alphabeta( t , -INFINI , INFINI ) ;
best = t->best;
destroy_tree( &t ) ;
return (best);
}
/**
* Algorithme MiniMax (amelioration AlphaBeta)
* Cette fonction va etudier les meilleurs coups possibles a jouer \n
* de facon recursive pour tous les coups possibles\n
* @param t La racine de l'arbre
* @param alpha optimisation alphaBeta coupure de type Max
* @param beta optimisation alphaBeta coupure de type Min
*/
static void alphabeta( TREE * t , short int alpha , short int beta ){
short int i,j, n,is,ie,isOtherPlayer,ieOtherPlayer;
short int m,note;
char prune=FALSE ;
char FinPartie = TRUE;
is = (t->aw->player == HUMAN)?START_HUMAN:START_COMPUTER;
ie = (t->aw->player == HUMAN)?END_HUMAN:END_COMPUTER;
isOtherPlayer = (t->aw->player == HUMAN)?START_COMPUTER:START_HUMAN;
ieOtherPlayer = (t->aw->player == HUMAN)?END_COMPUTER:END_HUMAN;
/**
* Test si fin de partie par famine
*/
for (n =0, i=is; i<=ie; i++)
n += t->aw->board[i];
if (!n)
for (j=isOtherPlayer; j<=ieOtherPlayer; j++){
if (t->aw->board[j] <= ieOtherPlayer -j)
FinPartie = FinPartie & TRUE;
else {
FinPartie = FinPartie & FALSE;
break;
}
}
/* si noeud terminal calcul note */
if ( t->prof == DEF_DEPTH || t->aw->CapturedBeans[HUMAN]+t->aw->CapturedBeans[COMPUTER] >= NBTOTALBEAN -2 || FinPartie != TRUE) {
t->note = eval (t->aw);
return ;
}
/* remontee note des fils */
m = alpha ;
for ( n=0,i=is ; i<=ie && !prune ; n++,i++ )
if (testMove(i, t->aw)) {
if (t->prof >0)
t->son[n] = create_tree( t->prof+1, switch_player(t->aw->player), t->aw ) ;
else
t->son[n] = create_tree( t->prof+1, t->aw->player, t->aw ) ;
if (testMove(i, t->son[n]->aw)){
move ( i , t->son[n]->aw);
t->son[n]->aw->player = switch_player (t->son[n]->aw->player);
alphabeta( t->son[n] , -beta , -m ) ;
note = -t->son[n]->note ;
if ( note > m ) {
m = note ;
t->best = i ;
}
if ( m >= beta ) prune=TRUE ;
}
destroy_tree( &(t->son[n]) ) ;
}
t->note = m ;
}
/**
* Fonction de calcul de la difference du nombre de cases menaçcantes
* Cette fonction va calculer le nombre de cases menaçantes pour les deux joueurs\n
* puis faire la diffrence des deux.
* @param aw Un pointeur sur le plateau a evaluer
* @return un entier egal a la difference des cases menaçantes
*/
short int threatenDelta (AWALE *aw){
short int i, tempo;
short int threatenHuman = 0, threatenComputer = 0;
AWALE tmpAw[13];
memcpy(&tmpAw[12], aw, sizeof(AWALE));
for (i=START_HUMAN; i<=END_COMPUTER; i++){
if (i<=END_HUMAN){
if (testMove (i, &tmpAw[12])){
memcpy(&tmpAw[i], aw, sizeof(AWALE));
tempo = tmpAw[i].CapturedBeans[HUMAN];
move(i, tmpAw);
if (tmpAw[i].CapturedBeans[HUMAN] > tempo)
threatenHuman++;
}
}
else {
tmpAw[i].player = switch_player(aw->player);
tmpAw[12].player = COMPUTER;
if (testMove (i, &tmpAw[12])){
memcpy(&tmpAw[i], aw, sizeof(AWALE));
tempo = tmpAw[i].CapturedBeans[COMPUTER];
move(i, tmpAw);
if (tmpAw[i].CapturedBeans[COMPUTER] > tempo)
threatenComputer++;
}
}
}
return (threatenComputer - threatenHuman);
}
/**
* Fonction de calcul de la difference de la mobilité des deux joueurs
* Cette fonction va calculer le nombre de cases non vides pour les deux joueurs\n
* puis faire la diffrence des deux.
* @param aw Un pointeur sur le plateau a evaluer
* @return un entier egal a la difference des cases non vides des deux joueurs
*/
short int moveDelta (AWALE *aw){
short int i;
short int moveHuman = 0, moveComputer = 0;
AWALE tmpAw;
memcpy (&tmpAw, aw, sizeof (AWALE));
for (i=START_HUMAN; i<=END_COMPUTER; i++)
if (i == START_COMPUTER)
tmpAw.player = COMPUTER;
if (testMove (i, &tmpAw)){
if (i<=END_HUMAN)
moveHuman++;
else
moveComputer++;
}
return (moveComputer - moveHuman);
}
#include <stdlib.h>
#include <string.h>
#include <awele_utils.h>
/**
* Fonction test si famine
* Test si le mouvement demandé provoque une \n
* famine dans le camp opposé. Met a jour la variable string errorMsg\n
* pour affichage sur le plateau de jeu.
* @param aw un pointeur sur la structure awalé sur laquelle faire le test
* @param start un entier donnant la premiere case de l'opposant
* @param end un entier donnant la derniere case de l'opposant
* @return TRUE si ce mouvement ne declenche pas une famine, FALSE sinon
*/
short int isOpponentHungry(AWALE * aw, short int start, short int end)
{
short int i, total;
extern char errorMsg[30];
for (total = 0, i = start; i <= end; i++) {
total += aw->board[i];
}
if (total)
return TRUE;
strcpy(errorMsg, MSG_FAMINE);
return FALSE;
}
/**
* Fonction de test si case non vide
* Test si la case choisie n'est pas vide
* @param hole entier désignant la case du plateau choisie
* @param aw pointeur sur la structure AWALE courante.
*/
short int isValidMove(short int hole, AWALE * aw)
{
extern char errorMsg[30];
if (aw->board[hole])
return TRUE;
strcpy(errorMsg, MSG_EMPTYHOLE);
return FALSE;
}
/**
* Fonction deplacement des graines
* Cette fonction est appelé a chaque déplacement de graines
* @param hole la case à partir de laquelle commencer le déplacement
* @param un pointeur sur la structure AWALE courante, pour laquelle efetuer le déplacement
* @return TRUE si mouvement valide, le test de famine ne peut etre fait qu'apres deplacement\n
* FALSE sinon.
*/
short int move(short int hole, AWALE * aw)
{
AWALE tempAw;
short int i, j, nbBeans, start, end;
/**
* Sauvegarde de l'awale courant dans un awale temporaire
* pour effectuer le mouvement et tester s'il est valide.
*/
memcpy(&tempAw, aw, sizeof(AWALE));
start = (tempAw.player == HUMAN) ? START_COMPUTER : START_HUMAN;
end = (tempAw.player == HUMAN) ? END_COMPUTER : END_HUMAN;
nbBeans = tempAw.board[hole];
tempAw.board[hole] = 0;
// Déplacement des graines
for (i = nbBeans, j = hole + 1; i > 0; i--, j++) {
if (j == NBHOLE)
j = 0;
if (j != hole)
tempAw.board[j] += 1;
else
i++;
}
j--;
// Comptage des points et mise a zéro ou il y a capture
if ((j >= start && j <= end)
&& (tempAw.board[j] == 2 || tempAw.board[j] == 3)) {
for (i = j; i >= start; i--) {
if (tempAw.board[i] == 2 || tempAw.board[i] == 3) {
tempAw.CapturedBeans[tempAw.player] += tempAw.board[i];
tempAw.board[i] = 0;
} else
break;
}
}
if (isOpponentHungry(&tempAw, start, end) != TRUE)
return FALSE;
else
memcpy(aw, &tempAw, sizeof(AWALE));
if (isEndOfGame(aw) == GAMEOVER)
return GAMEOVER;
return TRUE;
}
/**
* Fonction de chgt de joueur
* Cette fonction permet de renvoyer la valeur de l'opposant
* @param player un entier représentant le joueur courant
* @return un entier représentant l'opposant
*/
short int switch_player(short int player)
{
return (player == HUMAN) ? COMPUTER : HUMAN;
}
/**
* Fonction coup Aléatoire
* Cette fonction permet de générer un coup aléatoire
* @param a pointeur sur la structure AWALE courante
* @return un entier représentant le coup a jouer
*/
short int randplay(AWALE * a)
{
short int i;
do {
i = 6 + rand() % 6;
} while (a->board[i] == 0 && !testMove(i, a));
return (i);
}
/**
* Fonction de test si Fin de partie
* Cette fonction est appelée après chaque mvt\
* pour tester si c'est la fin de la partie.
* @param aw pointeur sur la structure AWALE a evaluer
* @return GAMEOVER si c'est la fin de la partie, NOT_GAMEOVER sinon
*/
short int isEndOfGame(AWALE * aw)
{
short int start, end, i, iCpt, otherPlayer, oppICpt;
start = (aw->player == HUMAN) ? START_HUMAN : START_COMPUTER;
end = (aw->player == HUMAN) ? END_HUMAN : END_COMPUTER;
otherPlayer = (aw->player == HUMAN) ? COMPUTER : HUMAN;
// Comptage du nbre de graine dans le camp du joueur courant.
iCpt = 0;
for (i = start; i <= end; i++) {
iCpt += aw->board[i];
}
start = (aw->player == HUMAN) ? START_COMPUTER : START_HUMAN;
end = (aw->player == HUMAN) ? END_COMPUTER : END_HUMAN;
switch (iCpt) {
case 0: //si le joueur courant n'a plus de graine dans son camp
for (i = start; i <= end; i++)
if (aw->board[i] > (end - i))
// si l'adversaire peut rengrainer :
return NOT_GAMEOVER;
/* Sinon, l'adversaire ne peut pas rengrainer :
comptage des points, mise a zéro des cases */
aw->CapturedBeans[otherPlayer] +=
NBTOTALBEAN - (aw->CapturedBeans[HUMAN] +
aw->CapturedBeans[COMPUTER]);
for (i = START_HUMAN; i < NBHOLE; i++)
aw->board[i] = 0;
return GAMEOVER;
case 1: // S'il ne reste plus q'une graine dans chaque camp alors fin de partie.
oppICpt = 0;
for (i = start; i <= end; i++) {
oppICpt += aw->board[i];
}
if (oppICpt == 1 || oppICpt == 0)
return GAMEOVER;
}
return NOT_GAMEOVER;
}
/**
* Fonction de test mouvement pour les coups de la machine
* Cette fonction est appelée avt chaque mouvement de la machine pour connaitre\n
* tous les coups possibles.
* @param coup un entier représentant le coup a tester
* @param aw Un pointeur su la structure AWALE courante
* @return TRUE si le mouvement est possible, FALSE sinon
*/
short int testMove(short int coup, AWALE * aw)
{
AWALE tempAw;
short int returnMove = FALSE;
memcpy(&tempAw, aw, sizeof(AWALE));
if (isValidMove(coup, &tempAw))
returnMove = move(coup, &tempAw);
if (returnMove == TRUE || returnMove == GAMEOVER)
return TRUE;
else
return FALSE;
}
/**
* Fonction de creation d'un awale
* Cette fonction reserve en memoire l'espace d'un awale\n
* et renvoi un pointeur sur la zone réservée
* @param void
* @return un pointeur sur la zone nouvellement créee contenant la structure awalé
*/
AWALE *create_awale()
{
AWALE *a;
a = (AWALE *) malloc(sizeof(AWALE));
if (a == NULL) {
exit(0);
}