/*
* gcompris - awele.c
*
* Copyright (C) 2005, 2008 Frederic Mazzarol
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include "gcompris/gcompris.h"
#include
#include
#include
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
/* This is a global Y offset that can be use to move the
* whole activity drawing up or down
*/
#define OFFSET_Y 35
#define Y_BOUTONS 322+OFFSET_Y
char errorMsg[30];
AWALE *staticAwale;
int caseCoord[12] =
{ 102, 206, 309, 413, 522, 628, 626, 520, 411, 307, 201, 100 };
static GRAPHICS_ELT *graphsElt = NULL;
static void start_board (GcomprisBoard * agcomprisBoard);
static void pause_board (gboolean pause);
static void end_board (void);
static void set_level (guint level);
static int gamewon;
static void game_won (void);
static void repeat(void);
static GooCanvasItem *boardRootItem = NULL;
static gboolean is_our_board (GcomprisBoard * gcomprisBoard);
static GooCanvasItem *awele_create_item (GooCanvasItem * parent);
static void awele_destroy_all_items (void);
static void awele_next_level (void);
static gboolean to_computer(gpointer data);
static gint timeout = 0;
static gboolean computer_turn = FALSE;
static gboolean sublevel_finished = FALSE;
/*=============================================*/
static GcomprisAnimation *animation;
static GcomprisAnimCanvasItem *anim_item;
/*
* Description of this plugin
*/
static BoardPlugin menu_bp = {
NULL,
NULL,
"The awele game", /* The name that describe this board */
"African strategy board game", /* The description that describes
* this board */
"Frederic Mazzarol ", /* The author of this
* board */
NULL, /* Called when the plugin is loaded */
NULL, /* Called when gcompris exit */
NULL, /* Show the about box */
NULL, /* Show the configuration dialog */
start_board, /* Callback to start_board implementation */
pause_board,
end_board,
is_our_board, /* Return 1 if the plugin can handle the board file */
NULL,
NULL,
set_level,
NULL,
repeat,
NULL,
NULL
};
/*
* Main entry point mandatory for each Gcompris's game
* ---------------------------------------------------
*
*/
GET_BPLUGIN_INFO (awele)
/*
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
*
*/
static void pause_board (gboolean pause)
{
if (gcomprisBoard == NULL)
return;
board_paused = pause;
if (pause == FALSE) {
if (gamewon == TRUE)
game_won ();
else
if (computer_turn){
timeout = g_timeout_add (2000,
(GSourceFunc) to_computer,
NULL);
anim_item = gc_anim_activate( boardRootItem,
animation );
g_object_set (anim_item->canvas,
"visibility", GOO_CANVAS_ITEM_VISIBLE,
NULL);
}
}
else{
if (computer_turn){
gc_anim_deactivate(anim_item);
if (timeout){
g_source_remove(timeout);
timeout = 0;
}
}
}
}
/*
*/
static void
start_board (GcomprisBoard * agcomprisBoard)
{
if (agcomprisBoard != NULL)
{
gcomprisBoard = agcomprisBoard;
gcomprisBoard->level = 1;
gcomprisBoard->maxlevel = 9;
gcomprisBoard->sublevel = 1;
gcomprisBoard->number_of_sublevel = 1; /* Go to next level after
* this number of 'play' */
gc_bar_set(GC_BAR_LEVEL|GC_BAR_REPEAT);
gchar *anim_file = "awele/sablier.txt";
animation = gc_anim_load( anim_file );
if(!animation)
{
gchar *text = g_strdup_printf(_("File '%s' is not found.\n"
"You cannot play this activity."),
anim_file);
gc_dialog(text, gc_board_stop);
return;
}
awele_next_level ();
gamewon = FALSE;
pause_board (FALSE);
}
}
/*
* =======================================
*/
static void
end_board ()
{
if (gcomprisBoard != NULL)
{
pause_board (TRUE);
gc_anim_free(animation);
awele_destroy_all_items ();
}
gcomprisBoard = NULL;
}
static gboolean
is_our_board (GcomprisBoard * gcomprisBoard)
{
if (gcomprisBoard)
{
if (g_ascii_strcasecmp (gcomprisBoard->type, "awele") == 0)
{
/*
* Set the plugin entry
*/
gcomprisBoard->plugin = &menu_bp;
return TRUE;
}
}
return FALSE;
}
/*
* Repeat let the user restart the current level
*
*/
static void repeat (){
if (computer_turn){
gc_anim_deactivate(anim_item);
if (timeout){
g_source_remove(timeout);
timeout = 0;
}
}
awele_next_level();
}
static void
set_level (guint level)
{
if(gcomprisBoard!=NULL)
{
gcomprisBoard->level=level;
gcomprisBoard->sublevel = 1;
if (computer_turn){
gc_anim_deactivate(anim_item);
if (timeout){
g_source_remove(timeout);
timeout = 0;
}
}
awele_next_level();
}
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*
* set initial values for the next level
*/
static void
awele_next_level ()
{
gc_set_default_background (goo_canvas_get_root_item (gcomprisBoard->canvas));
gc_bar_set_level (gcomprisBoard);
awele_destroy_all_items ();
gamewon = FALSE;
computer_turn = FALSE;
/*
* Create the level
*/
awele_create_item (goo_canvas_get_root_item (gcomprisBoard->canvas));
if ((gcomprisBoard->level % 2) ==0){
computer_turn = TRUE;
staticAwale->player = HUMAN;
timeout = g_timeout_add (2000,
(GSourceFunc) to_computer,
NULL);
anim_item = gc_anim_activate( boardRootItem,
animation );
g_object_set (anim_item->canvas, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
} else {
computer_turn = FALSE;
}
}
/*
* ====================================
*/
/*
* Destroy all the items
*/
static void
awele_destroy_all_items ()
{
int i;
if (boardRootItem != NULL)
goo_canvas_item_remove(boardRootItem);
boardRootItem = NULL;
if(graphsElt)
{
for (i = 0; i < NBHOLE / 2; i++)
{
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(graphsElt->pixbufButton[i]);
gdk_pixbuf_unref(graphsElt->pixbufButtonNotify[i]);
gdk_pixbuf_unref(graphsElt->pixbufButtonClicked[i]);
#else
g_object_unref(graphsElt->pixbufButton[i]);
g_object_unref(graphsElt->pixbufButtonNotify[i]);
g_object_unref(graphsElt->pixbufButtonClicked[i]);
#endif
}
g_free(graphsElt);
graphsElt = NULL;
}
}
/*
* ====================================
*/
static GooCanvasItem *
awele_create_item (GooCanvasItem * parent)
{
GdkPixbuf *pixmap = NULL;
gint i = 0, x1 = 0;
gchar buffer[2];
gchar xpmFile[35] = BOUTON;
gchar xpmFileNotify[35] = BOUTON_NOTIFY;
gchar xpmFileClic[35] = BOUTON_CLIC;
boardRootItem = goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
NULL);
/*
* Load the cute frame
*/
pixmap = gc_pixmap_load ("awele/awele_frame.png");
goo_canvas_image_new (boardRootItem,
pixmap,
0,
OFFSET_Y,
NULL);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(pixmap);
#else
g_object_unref(pixmap);
#endif
/*
* Display text
*/
{
int x, y;
x = 35;
y = 100 + OFFSET_Y;
goo_canvas_text_new (boardRootItem,
_("NORTH"),
(double) x,
(double) y,
-1,
GTK_ANCHOR_CENTER,
"font", gc_skin_font_board_medium,
"fill_color", "black",
NULL);
x = 765;
y = 205 + OFFSET_Y;
goo_canvas_text_new (boardRootItem,
_("SOUTH"),
(double) x,
(double) y,
-1,
GTK_ANCHOR_CENTER,
"font", gc_skin_font_board_medium,
"fill_color", "black",
NULL);
}
staticAwale = (AWALE *) g_malloc (sizeof (AWALE));
if (!staticAwale)
exit (1);
for (i = 0; i < NBHOLE; i++)
{
staticAwale->board[i] = NBBEANSPERHOLE;
}
/* ->player is last player */
/* next is human */
staticAwale->player = COMPUTER;
for (i = 0; i < NBPLAYER; i++)
{
staticAwale->CapturedBeans[i] = 0;
}
graphsElt = (GRAPHICS_ELT *) g_malloc (sizeof (GRAPHICS_ELT));
/*
* Boucle pour creer et positionner les boutons qui serviront
* a selectionner la case a jouer
*/
for (i = 0; i < NBHOLE / 2; i++)
{
sprintf (buffer, "%d", i + 1);
xpmFile[12] = buffer[0];
graphsElt->pixbufButton[i] = gc_pixmap_load (xpmFile);
xpmFileNotify[12] = buffer[0];
graphsElt->pixbufButtonNotify[i] =
gc_pixmap_load (xpmFileNotify);
xpmFileClic[12] = buffer[0];
graphsElt->pixbufButtonClicked[i] =
gc_pixmap_load (xpmFileClic);
/*
* Ajustement de l'ordonnee x, pour positionner le bouton sur la barre de boutons.
*/
switch (i)
{
case 0:
x1 = 120;
break;
case 1:
x1 = 220;
break;
case 2:
x1 = 325;
break;
case 3:
x1 = 432;
break;
case 4:
x1 = 539;
break;
case 5:
x1 = 643;
break;
}
/*
* Ajout des boutons comme items sur le rootGroup du canevas.
* et sauvegarde dans tableau button de type Gnome Canvas Item
* pour attacher les pointeurs de la fonction de rappel buttonClick
* qui servira a detecter quel est l'evenement souris, et en fonction
* declencher la procedure associee. Passage en argument a cette fonction
* du numero de case selectionne par tableau chaine
*/
graphsElt->button[i] = goo_canvas_image_new (boardRootItem,
graphsElt->pixbufButton[i],
x1,
Y_BOUTONS,
NULL);
g_signal_connect (graphsElt->button[i],
"button_press_event",
(GCallback) buttonClick,
GINT_TO_POINTER(i));
gc_item_focus_init(graphsElt->button[i], NULL);
}
/**
* Affichage initial du nombre de graine courant dans le trou i.
* Sauvegarde des items d'affichage dans nbBeansHole, pour mise a jour
* pdt la partie.
*/
for (i = 11; i >= 0; i--)
{
sprintf (buffer, "%d", staticAwale->board[i]);
graphsElt->nbBeansHole[i] =
goo_canvas_text_new (boardRootItem,
buffer,
(caseCoord[i] + 45),
((i < 6) ? 288 : 4) + OFFSET_Y,
-1,
GTK_ANCHOR_CENTER,
"font", "sans 20",
"fill-color", "black",
NULL);
}
/**
* Affichage initial du nombre de graine capturees par chaque joueur.
* Sauvegarde des items d'affichage dans Captures[i], pour mise a jour
* pdt la partie.
*/
for (i = 0; i < 2; i++)
{
x1 = (i == 1) ? 32 : 762;
sprintf (buffer, "%d", staticAwale->CapturedBeans[i]);
graphsElt->Captures[i] = \
goo_canvas_text_new (boardRootItem,
buffer,
x1,
156 + OFFSET_Y,
-1,
GTK_ANCHOR_CENTER,
"font", "sans 24",
"fill-color", "white",
NULL);
}
/**
* Initialisation du buffer xpmFile avec le chemin relatif des fichiers graine
* Creation des pixbuf et sauvegarde dans variable pixbufBeans
*/
strcpy (xpmFile, BEAN);
for (i = 0; i < 4; i++)
{
sprintf (buffer, "%d", i + 1);
xpmFile[12] = buffer[0];
graphsElt->pixbufBeans[i] = gc_pixmap_load (xpmFile);
}
/**
* Reservation d'un espace memoire egal a NBTOTALBEAN x taille struct BEANHOLE_LINK
* pour y stocker chaque item de graine ainsi que la case dans laquelle se trouve la graine.
* Puis creation de toutes les graines et affichage sur le plateau.
*/
initBoardGraphics (graphsElt);
graphsElt->msg = goo_canvas_text_new (boardRootItem,
_("Choose a house"),
(double) 400,
(double) 410 + OFFSET_Y,
-1,
GTK_ANCHOR_CENTER,
"font", "sans 20",
"fill-color", "red",
NULL);
return NULL;
}
/*
* ====================================
*/
static void
game_won ()
{
if (sublevel_finished){
gcomprisBoard->sublevel++;
if (gcomprisBoard->sublevel > gcomprisBoard->number_of_sublevel)
{
/*
* Try the next level
*/
gcomprisBoard->sublevel = 1;
gcomprisBoard->level++;
if (gcomprisBoard->level > gcomprisBoard->maxlevel)
gcomprisBoard->level = gcomprisBoard->maxlevel;
}
}
sublevel_finished = FALSE;
awele_next_level ();
}
/**
* Fonction effectuant l'initialisation des graines sur le plateau
* Cette fonction est appelee a chaque debut de partie
* @param data un pointeur de type void, pour passer en argument a la fonction\n
* les elements graphiques a modifier.
* @return void
*/
static void
initBoardGraphics (GRAPHICS_ELT * graphsElt)
{
int i, j, k, idxTabBeans = 0;
//if (graphsElt->ptBeansHoleLink != NULL)
// free(graphsElt->ptBeansHoleLink);
graphsElt->ptBeansHoleLink =
(BEANHOLE_LINK *) malloc (NBTOTALBEAN *
sizeof (BEANHOLE_LINK));
for (i = NBHOLE - 1; i >= 0; i--)
{
for (j = 0;
j < staticAwale->board[i] && idxTabBeans < NBTOTALBEAN;
j++, idxTabBeans++)
{
k = 0 + g_random_int() % 4;
graphsElt->ptBeansHoleLink[idxTabBeans].beanPixbuf =
goo_canvas_image_new (boardRootItem,
graphsElt->pixbufBeans[k],
caseCoord[i] + g_random_int() % 50,
(((i <
6) ? 170 :
40) +
g_random_int() %
60) + OFFSET_Y,
NULL);
graphsElt->ptBeansHoleLink[idxTabBeans].hole = i;
}
}
}
static gboolean to_computer(gpointer data)
{
short int coup;
if (!computer_turn){
g_warning ("to_computer called but not compter_turn");
return FALSE;
}
if (board_paused){
g_warning ("to_computer called but board paused");
timeout = 0;
return TRUE;
}
coup = think (staticAwale, gcomprisBoard->level);
gc_anim_deactivate(anim_item);
computer_turn = FALSE;
if (coup >= 0){
AWALE *tmpAw = staticAwale;
staticAwale = moveAwale (coup, tmpAw);
if (!staticAwale){
g_error("le coup devrait être bon !");
}
gboolean IAmHungry = diedOfHunger(staticAwale);
if (!IAmHungry){
g_free(tmpAw);
updateNbBeans (0);
updateCapturedBeans ();
g_object_set (graphsElt->msg, "text",
_("Your turn to play ..."), NULL);
} else {
/* computer hungry but human can't give it beans */
/* Human player win by catching all the beans left. */
gamewon = TRUE;
sublevel_finished = TRUE;
gc_bonus_display(TRUE, GC_BONUS_FLOWER);
}
} else {
/* computer can't play. Why? human is hungry and i cannot give it
to eat */
/* if human has 24 beans, it's draw (human win in gcompris) */
/* if not, all staying are captured by computer and computer win */
gamewon = TRUE;
sublevel_finished = (staticAwale->CapturedBeans[HUMAN] == 24);
gc_bonus_display(sublevel_finished, GC_BONUS_FLOWER);
}
timeout = 0;
return FALSE;
}
/**
* Fonction effectuant la procedure associe a un clic sur pixmap
* Cette fonction est appelee quand un clic sur un bouton est effectue.\n
*/
static gboolean buttonClick (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventButton *event,
gchar *data)
{
gint numeroCase = GPOINTER_TO_INT(data);
if (computer_turn)
return TRUE;
g_object_set (graphsElt->msg, "text", "", NULL);
AWALE *tmpaw = moveAwale (numeroCase, staticAwale);
if (!tmpaw)
{
g_object_set (graphsElt->msg, "text", _("Not allowed! Try again !"),
NULL);
}
else
{
g_free(staticAwale);
staticAwale = tmpaw;
updateNbBeans (0);
updateCapturedBeans ();
if (!gamewon){
computer_turn = TRUE;
timeout = g_timeout_add (2000,
(GSourceFunc) to_computer,
NULL);
anim_item = gc_anim_activate( boardRootItem,
animation );
}
}
return FALSE;
}
/**
* Fonction de gestion des graines dessinees sur le plateau
* Cette fonction est appelee apres chaque mouvement, \n
* pour remettre a jour le nombre de graines dessinees sur le plateau, \n
* et diminuer la zone d'allocation ou les pixmap des graines sont stockees.
* @param nbBeansHole[NBHOLE] Tableau de pointeur sur les gnomeCanvasItem\n
* affichant le nombre de graine par case
* @param rootGroup Pointeur sur le groupe contenant tous les items inseres dans le canevas
* @param ptLink pointeur sur zone memoire ou sont stockees toutes les images des graines du plateau.
* @param alpha entier pour differencier une mise a jour du plateau ou le lancement d'une nouvelle partie.
* @return Renvoi du pointeur sur la zone memoire apres redimension (n'a probablement pas changé d'adresse).
*/
static BEANHOLE_LINK *
updateNbBeans (int alpha)
{
char buffer[3]; //Manipulation chaines de caracteres
int i, j, idxTabBeans = 0; //Compteur Boucle Manipulation Elements graphiques
static short int nbActiveBean = NBTOTALBEAN; //nbre graine restant sur plateau
static short int nbOldActiveBean; //nbre graine restant sur plateau au tour precedent
BEANHOLE_LINK *ptBeansHoleLink = NULL; //pointeur sur structures stockant les item graines et la case dans laquelle elles se trouvent.
/**
* Sauvegarde du nombre de graines restantes sur le plateau de jeu
* pour le prochain appel a la fonction.
* Mise a jour de nbActiveBean avec nouvelle configuration du plateau de jeu.
*/
if (alpha)
{
nbOldActiveBean = 48;
}
else
{
nbOldActiveBean = nbActiveBean;
}
nbActiveBean =
NBTOTALBEAN - (staticAwale->CapturedBeans[HUMAN] +
staticAwale->CapturedBeans[COMPUTER]);
/**
* Destruction d'autant d'elements graphiques graines
* qu'il y a eu de captures pdt ce tour de jeu
*/
for (ptBeansHoleLink = &(graphsElt->ptBeansHoleLink)[nbActiveBean], i = 0;
i < nbOldActiveBean - nbActiveBean; i++, ptBeansHoleLink++)
{
goo_canvas_item_remove(ptBeansHoleLink->beanPixbuf);
}
/**
* Allocation d'un nouvel espace memoire stockant les item graines
* et la case dans laquelle elles se trouvent. Puis liberation de la fin de
* l'ancien espace memoire.
*/
ptBeansHoleLink =
(BEANHOLE_LINK *) realloc (graphsElt->ptBeansHoleLink,
nbActiveBean *
sizeof (BEANHOLE_LINK));
/**
* Pour chaque case du plateau, mise a jour du nbre de graines qu'elle contient.
* Et pour chaque graine de cette case, deplacement d'un element graphique type graine
* dans cette case. Et mise a jour de l'information hole dans la structure BEANHOLE_LINK.
*/
for (i = NBHOLE - 1; i >= 0; i--)
{
sprintf (buffer, "%d", staticAwale->board[i]);
g_object_set (graphsElt->nbBeansHole[i], "text", buffer, NULL);
for (j = 0;
j < staticAwale->board[i] && idxTabBeans < nbActiveBean;
j++, idxTabBeans++)
{
g_object_set (ptBeansHoleLink[idxTabBeans].beanPixbuf,
"x", (double) caseCoord[i] +
g_random_int() % 50,
"y", (double) (((i <
6) ? 170 : 40) +
g_random_int() % 60 + OFFSET_Y),
NULL);
ptBeansHoleLink[idxTabBeans].hole = i;
}
}
/**
* Renvoi du pointeur sur la zone memoire retaillee (n'a probablement pas change d'adresse).
*/
graphsElt->ptBeansHoleLink = ptBeansHoleLink;
return ptBeansHoleLink;
}
/**
* Fonction de gestion de l'affichage des scores
* Cette fonction est appelee apres chaque mouvement, \n
* pour remettre a jour le score des joueurs
* @param Captures[2] pointeur sur les gnomeCanvasItem d'affichage des scores
*/
static void
updateCapturedBeans ()
{
short int i;
char buffer[3];
for (i = 0; i < 2; i++)
{
sprintf (buffer, "%d", staticAwale->CapturedBeans[i]);
g_object_set (graphsElt->Captures[i], "text", buffer, NULL);
if (staticAwale->CapturedBeans[i] > 24)
{
gamewon = TRUE;
sublevel_finished = (i==0);
gc_bonus_display(sublevel_finished, GC_BONUS_FLOWER);
}
}
}