Back to index

gcompris  8.2.2
hanoi_real.c
Go to the documentation of this file.
00001 /* gcompris - hanoi_real.c
00002  *
00003  * Copyright (C) 2005 Bruno Coudoin
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU General Public License as published by
00007  *   the Free Software Foundation; either version 2 of the License, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details.
00014  *
00015  *   You should have received a copy of the GNU General Public License
00016  *   along with this program; if not, write to the Free Software
00017  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #include "gcompris/gcompris.h"
00021 
00022 #define SOUNDLISTFILE PACKAGE
00023 
00024 static GcomprisBoard *gcomprisBoard = NULL;
00025 static gboolean board_paused = TRUE;
00026 
00027 static void    start_board (GcomprisBoard *agcomprisBoard);
00028 static void    pause_board (gboolean pause);
00029 static void    end_board (void);
00030 static gboolean       is_our_board (GcomprisBoard *gcomprisBoard);
00031 static void    set_level (guint level);
00032 static int     gamewon;
00033 static void    game_won(void);
00034 
00035 static GnomeCanvasGroup *boardRootItem = NULL;
00036 
00037 static GnomeCanvasItem      *hanoi_create_item(GnomeCanvasGroup *parent);
00038 static void           hanoi_destroy_all_items(void);
00039 static void           hanoi_next_level(void);
00040 
00041 /*
00042  * Contains the piece information
00043  */
00044 typedef struct {
00045   GnomeCanvasItem *item;
00046   gint i;
00047   gint j;
00048   double x;
00049   double y;
00050   gboolean on_top;
00051   gint width;
00052 } PieceItem;
00053 
00054 static gint           item_event(GnomeCanvasItem *item, GdkEvent *event, PieceItem *data);
00055 
00056 /* This contains the layout of the pieces */
00057 #define MAX_NUMBER_X 3
00058 #define MAX_NUMBER_Y 10
00059 static PieceItem *position[MAX_NUMBER_X][MAX_NUMBER_Y];
00060 
00061 static int number_of_item = 0;
00062 static int number_of_item_x = MAX_NUMBER_X;
00063 static int number_of_item_y = 0;
00064 static int item_width;
00065 static int item_height;
00066 
00067 /* Description of this plugin */
00068 static BoardPlugin menu_bp =
00069   {
00070     NULL,
00071     NULL,
00072     N_("Tower of Hanoi"),
00073     "",
00074     "Bruno Coudoin <bruno.coudoin@free.fr>",
00075     NULL,
00076     NULL,
00077     NULL,
00078     NULL,
00079     start_board,
00080     pause_board,
00081     end_board,
00082     is_our_board,
00083     NULL,
00084     NULL,
00085     set_level,
00086     NULL,
00087     NULL,
00088     NULL,
00089     NULL
00090   };
00091 
00092 /*
00093  * Main entry point mandatory for each Gcompris's game
00094  * ---------------------------------------------------
00095  *
00096  */
00097 
00098 GET_BPLUGIN_INFO(hanoi_real)
00099 
00100 /*
00101  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
00102  *
00103  */
00104 static void pause_board (gboolean pause)
00105 {
00106   if(gcomprisBoard==NULL)
00107     return;
00108 
00109   if(gamewon == TRUE && pause == FALSE) /* the game is won */
00110     {
00111       game_won();
00112     }
00113 
00114   board_paused = pause;
00115 }
00116 
00117 /*
00118  */
00119 static void start_board (GcomprisBoard *agcomprisBoard)
00120 {
00121 
00122   if(agcomprisBoard!=NULL)
00123     {
00124       gcomprisBoard=agcomprisBoard;
00125       gcomprisBoard->level=1;
00126       gcomprisBoard->maxlevel=2;
00127       gcomprisBoard->sublevel=1;
00128       gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
00129       gc_bar_set(GC_BAR_LEVEL);
00130 
00131       gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
00132                            gc_skin_image_get("gcompris-bg.jpg"));
00133 
00134       boardRootItem = NULL;
00135 
00136       hanoi_next_level();
00137 
00138       gamewon = FALSE;
00139       pause_board(FALSE);
00140     }
00141 }
00142 /* ======================================= */
00143 static void end_board ()
00144 {
00145   if(gcomprisBoard!=NULL)
00146     {
00147       pause_board(TRUE);
00148       hanoi_destroy_all_items();
00149     }
00150   gcomprisBoard = NULL;
00151 }
00152 
00153 /* ======================================= */
00154 static void set_level (guint level)
00155 {
00156 
00157   if(gcomprisBoard!=NULL)
00158     {
00159       gcomprisBoard->level=level;
00160       gcomprisBoard->sublevel=1;
00161       hanoi_next_level();
00162     }
00163 }
00164 /* ======================================= */
00165 static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
00166 {
00167   if (gcomprisBoard)
00168     {
00169       if(g_strcasecmp(gcomprisBoard->type, "hanoi_real")==0)
00170        {
00171          /* Set the plugin entry */
00172          gcomprisBoard->plugin=&menu_bp;
00173 
00174          return TRUE;
00175        }
00176     }
00177   return FALSE;
00178 }
00179 
00180 /*-------------------------------------------------------------------------------*/
00181 /*-------------------------------------------------------------------------------*/
00182 /* set initial values for the next level */
00183 static void hanoi_next_level()
00184 {
00185 
00186   gc_bar_set_level(gcomprisBoard);
00187 
00188   hanoi_destroy_all_items();
00189   gamewon = FALSE;
00190 
00191   /* Select level difficulty */
00192   number_of_item_y = gcomprisBoard->level + 2;
00193 
00194   /* Try the next level */
00195   hanoi_create_item(gnome_canvas_root(gcomprisBoard->canvas));
00196 }
00197 /* ==================================== */
00198 /* Destroy all the items */
00199 static void hanoi_destroy_all_items()
00200 {
00201   guint i,j;
00202 
00203   if(boardRootItem!=NULL)
00204     {
00205       gtk_object_destroy (GTK_OBJECT(boardRootItem));
00206 
00207       /* Cleanup our memory structure */
00208       for(i=0; i<number_of_item_x; i++)
00209        {
00210          for(j=0; j<number_of_item_y; j++)
00211            {
00212              g_free(position[i][j]);
00213            }
00214        }
00215     }
00216   boardRootItem = NULL;
00217 
00218 }
00219 
00220 #if 0
00221 static void dump_solution()
00222 {
00223   guint i, j;
00224 
00225   printf("Dumping solution\n");
00226   for(i=0; i<number_of_item_x; i++)
00227     {
00228       for(j=0; j<number_of_item_y; j++)
00229        {
00230          printf("(%d,%d= width=%d top=%d) ",  position[i][j]->i, position[i][j]->j,
00231                   position[i][j]->width,
00232                   position[i][j]->on_top);
00233        }
00234       printf("\n");
00235     }
00236 
00237 }
00238 #endif
00239 
00240 /* ==================================== */
00241 static GnomeCanvasItem *hanoi_create_item(GnomeCanvasGroup *parent)
00242 {
00243   int i,j;
00244   double gap_x, gap_y;
00245   double baseline;
00246   GnomeCanvasItem *item = NULL;
00247   GdkPixbuf *pixmap = NULL;
00248   gchar *filename;
00249 
00250   boardRootItem = GNOME_CANVAS_GROUP(
00251                                  gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
00252                                                      gnome_canvas_group_get_type (),
00253                                                      "x", (double) 0,
00254                                                      "y", (double) 0,
00255                                                      NULL));
00256 
00257   pixmap = gc_skin_pixmap_load("gcompris-shapelabel.png");
00258   if(pixmap) {
00259     gnome_canvas_item_new (boardRootItem,
00260                         gnome_canvas_pixbuf_get_type (),
00261                         "pixbuf", pixmap,
00262                         "x",       (double)BOARDWIDTH/2,
00263                         "y",       (double)BOARDHEIGHT - 35,
00264                         "width", (double) BOARDWIDTH - 20,
00265                         "width_set", TRUE,
00266                         "anchor", GTK_ANCHOR_CENTER,
00267                         NULL);
00268     gdk_pixbuf_unref(pixmap);
00269   }
00270 
00271   gnome_canvas_item_new (boardRootItem,
00272                       gnome_canvas_text_get_type (),
00273                       "text", _("Move the entire stack to the right peg, one disc at a time"),
00274                       "font", gc_skin_font_board_medium,
00275                       "x", (double) BOARDWIDTH/2 +1,
00276                       "y", (double) BOARDHEIGHT - 50 +1,
00277                       "anchor", GTK_ANCHOR_NORTH,
00278                       "fill_color_rgba", gc_skin_color_shadow,
00279                       "justification", GTK_JUSTIFY_CENTER,
00280                       NULL);
00281 
00282   gnome_canvas_item_new (boardRootItem,
00283                       gnome_canvas_text_get_type (),
00284                       "text", _("Move the entire stack to the right peg, one disc at a time"),
00285                       "font", gc_skin_font_board_medium,
00286                       "x", (double) BOARDWIDTH/2,
00287                       "y", (double) BOARDHEIGHT - 50,
00288                       "anchor", GTK_ANCHOR_NORTH,
00289                       "fill_color_rgba", gc_skin_color_text_button,
00290                       "justification", GTK_JUSTIFY_CENTER,
00291                       NULL);
00292 
00293 
00294   /*----------------------------------------*/
00295   /* Empty the solution */
00296   for(i=0; i<number_of_item_x; i++)
00297     {
00298       for(j=0; j<number_of_item_y; j++)
00299        {
00300          position[i][j] = g_malloc(sizeof(PieceItem));
00301          position[i][j]->width  = -1;
00302          position[i][j]->i      = i;
00303          position[i][j]->j      = j;
00304          position[i][j]->on_top = FALSE;
00305        }
00306     }
00307 
00308   /* Initialize the left positions */
00309   for(j=0; j<number_of_item_y; j++)
00310     {
00311       position[0][j]->width = number_of_item_y - j;
00312     }
00313 
00314   /* Mark the top piece */
00315   position[0][number_of_item_y-1]->on_top = TRUE;
00316 
00317   /*dump_solution();*/
00318 
00319   /*----------------------------------------*/
00320   /* Display it now */
00321 
00322   item_width  = BOARDWIDTH / (number_of_item_x);
00323   item_height = 30;
00324 
00325   gap_x = item_width  * 0.1;
00326   gap_y = item_height * 0.25;
00327 
00328   baseline = BOARDHEIGHT/2 + item_height * number_of_item_y/2;
00329 
00330   number_of_item = 0;
00331 
00332   for(i=0; i<number_of_item_x; i++)
00333     {
00334       if(i==number_of_item_x-1)
00335        {
00336          /* Create the backgound for the target */
00337          gnome_canvas_item_new (boardRootItem,
00338                              gnome_canvas_rect_get_type (),
00339                              "x1", (double) item_width * i + gap_x/2,
00340                              "y1", (double) baseline - item_height * number_of_item_y - gap_y - 50,
00341                              "x2", (double) item_width * (i+1) - gap_x/2,
00342                              "y2", (double) baseline + 50,
00343                              "fill_color_rgba", 0x036ED8FF,
00344                              "outline_color", "black",
00345                              "width_units", (double)1,
00346                              NULL);
00347        }
00348 
00349       /* The disc support */
00350       pixmap = gc_pixmap_load ("images/disc_support.png");
00351 
00352       item = gnome_canvas_item_new (boardRootItem,
00353                                 gnome_canvas_pixbuf_get_type (),
00354                                 "x", (double) item_width * i + item_width/2,
00355                                 "y", (double) baseline - gdk_pixbuf_get_height(pixmap)/2 + item_height/2,
00356                                 "pixbuf", pixmap,
00357                                 "anchor", GTK_ANCHOR_CENTER,
00358                                 NULL);
00359 
00360       gdk_pixbuf_unref(pixmap);
00361 
00362 
00363       for(j=0; j<number_of_item_y; j++)
00364        {
00365 
00366          position[i][j]->x = item_width * i + item_width/2;
00367          position[i][j]->y = baseline - item_height * j - item_height + gap_y;
00368 
00369          if(position[i][j]->width != -1)
00370            {
00371              filename = g_strdup_printf("%s%d.png", "images/disc", j+1);
00372              pixmap = gc_pixmap_load (filename);
00373 
00374              item = gnome_canvas_item_new (boardRootItem,
00375                                        gnome_canvas_pixbuf_get_type (),
00376                                        "pixbuf", pixmap,
00377                                        "x", position[i][j]->x,
00378                                        "y", position[i][j]->y,
00379                                        "anchor", GTK_ANCHOR_CENTER,
00380                                        NULL);
00381              g_free(filename);
00382              gdk_pixbuf_unref(pixmap);
00383 
00384              position[i][j]->item = item;
00385 
00386              gtk_signal_connect(GTK_OBJECT(item), "event", (GtkSignalFunc) item_event,  position[i][j]);
00387 
00388            }
00389        }
00390     }
00391 
00392   return NULL;
00393 }
00394 /* ==================================== */
00395 static void game_won()
00396 {
00397   gcomprisBoard->sublevel++;
00398 
00399   if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
00400     /* Try the next level */
00401     gcomprisBoard->sublevel=1;
00402     gcomprisBoard->level++;
00403     if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
00404       gc_bonus_end_display(BOARD_FINISHED_RANDOM);
00405       return;
00406     }
00407     gc_sound_play_ogg ("sounds/bonus.ogg", NULL);
00408   }
00409   hanoi_next_level();
00410 }
00411 
00412 /*
00413  * Returns TRUE is the goal is reached
00414  */
00415 static gboolean is_completed()
00416 {
00417   gint j;
00418   gboolean done = TRUE;
00419 
00420   for(j=0; j<number_of_item_y; j++)
00421     {
00422       if(position[number_of_item_x-1][j]->width != number_of_item_y - j)
00423        done = FALSE;
00424     }
00425 
00426   return done;
00427 }
00428 
00429 /* ==================================== */
00430 static gint
00431 item_event(GnomeCanvasItem *item, GdkEvent *event, PieceItem *data)
00432 {
00433    static double x, y;
00434    double new_x, new_y;
00435    GdkCursor *fleur;
00436    static int dragging;
00437    double item_x, item_y;
00438 
00439    if(!gcomprisBoard)
00440      return FALSE;
00441 
00442   if(board_paused)
00443     return FALSE;
00444 
00445   if(!data->on_top)
00446     return FALSE;
00447 
00448   item_x = event->button.x;
00449   item_y = event->button.y;
00450   gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
00451 
00452   switch (event->type)
00453     {
00454     case GDK_ENTER_NOTIFY:
00455       gc_item_focus_set(item, TRUE);
00456       break;
00457     case GDK_LEAVE_NOTIFY:
00458       gc_item_focus_set(item, FALSE);
00459       break;
00460     case GDK_BUTTON_PRESS:
00461       switch(event->button.button)
00462        {
00463        case 1:
00464 
00465          x = item_x;
00466          y = item_y;
00467 
00468          gnome_canvas_item_raise_to_top(data->item);
00469 
00470          fleur = gdk_cursor_new(GDK_FLEUR);
00471          gc_canvas_item_grab(data->item,
00472                              GDK_POINTER_MOTION_MASK |
00473                              GDK_BUTTON_RELEASE_MASK,
00474                              fleur,
00475                              event->button.time);
00476          gdk_cursor_destroy(fleur);
00477          dragging = TRUE;
00478          break;
00479        }
00480       break;
00481 
00482     case GDK_MOTION_NOTIFY:
00483       if (dragging && (event->motion.state & GDK_BUTTON1_MASK))
00484        {
00485          new_x = item_x;
00486          new_y = item_y;
00487 
00488          gnome_canvas_item_move(data->item, new_x - x, new_y - y);
00489          x = new_x;
00490          y = new_y;
00491        }
00492       break;
00493 
00494     case GDK_BUTTON_RELEASE:
00495       if(dragging)
00496        {
00497          gint i;
00498          gint tmpi, tmpj;
00499          double tmpx, tmpy;
00500          PieceItem *piece_src;
00501          PieceItem *piece_dst;
00502          gint line;
00503          gint col=-1;
00504          double disc_w, disc_h;
00505          GdkPixbuf *pixmap;
00506 
00507          g_object_get(G_OBJECT(data->item), "pixbuf", &pixmap, NULL);
00508          disc_w = gdk_pixbuf_get_width(pixmap)/2;
00509          disc_h = gdk_pixbuf_get_height(pixmap)/2;
00510          gdk_pixbuf_unref(pixmap);
00511 
00512          gc_canvas_item_ungrab(data->item, event->button.time);
00513          dragging = FALSE;
00514 
00515          /* Search the column (x) where this item is ungrabbed */
00516          if(item_x > (position[number_of_item_x-1][0]->x
00517                      - (position[number_of_item_x-1][0]->x - position[number_of_item_x-2][0]->x) / 2))
00518             col = number_of_item_x-1;
00519          else if(item_x < position[0][0]->x)
00520            col = 0;
00521          else {
00522            for(i=0; i<number_of_item_x-1; i++) {
00523              int distance = (position[i+1][0]->x - position[i][0]->x) / 2;
00524              if(position[i][0]->x -  distance < item_x &&
00525                position[i+1][0]->x - distance > item_x)
00526               col = i;
00527            }
00528          }
00529 
00530          /* Bad drop / Outside of column area */
00531          /* Bad drop / On the same column */
00532          if(col<0 || col > number_of_item_x || col == data->i)
00533            {
00534              /* Return to the original position */
00535              gc_item_absolute_move (data->item , data->x - disc_w, data->y - disc_h);
00536 
00537              /* FIXME : Workaround for bugged canvas */
00538              gnome_canvas_update_now(gcomprisBoard->canvas);
00539 
00540              return FALSE;
00541            }
00542 
00543 
00544          /* Now search the free line (y) */
00545          line = number_of_item_y;
00546          for(i=number_of_item_y-1; i>=0; i--)
00547            if(position[col][i]->width == -1)
00548              line = i;
00549 
00550          /* Bad drop / Too many pieces here or larger disc is above */
00551          if(line > number_of_item_y ||
00552             (line > 0 && position[col][line-1]->width != -1 && position[col][line-1]->width < data->width))
00553            {
00554              /* Return to the original position */
00555              gc_item_absolute_move (data->item , data->x - disc_w, data->y - disc_h);
00556 
00557              /* FIXME : Workaround for bugged canvas */
00558              gnome_canvas_update_now(gcomprisBoard->canvas);
00559 
00560              return FALSE;
00561            }
00562 
00563          /* Update ontop values for the piece under the grabbed one */
00564          if(data->j>0)
00565            position[data->i][data->j-1]->on_top = TRUE;
00566 
00567          /* Update ontop values for the piece under the ungrabbed one */
00568          if(line>0)
00569            position[col][line-1]->on_top = FALSE;
00570 
00571          /* Move the piece */
00572          piece_dst = position[col][line];
00573          piece_src = data;
00574 
00575          gc_item_absolute_move (data->item,
00576                            piece_dst->x - disc_w,
00577                            piece_dst->y - disc_h);
00578 
00579          /* FIXME : Workaround for bugged canvas */
00580          gnome_canvas_update_now(gcomprisBoard->canvas);
00581 
00582          /* Swap values in the pieces */
00583          tmpx    = data->x;
00584          tmpy    = data->y;
00585          piece_src->x = piece_dst->x;
00586          piece_src->y = piece_dst->y;
00587          piece_dst->x = tmpx;
00588          piece_dst->y = tmpy;
00589 
00590          tmpi    = data->i;
00591          tmpj    = data->j;
00592          position[tmpi][tmpj]->i = piece_dst->i;
00593          position[tmpi][tmpj]->j = piece_dst->j;
00594          piece_dst->i  = tmpi;
00595          piece_dst->j  = tmpj;
00596 
00597          position[piece_src->i][piece_src->j] = piece_src;
00598          position[piece_dst->i][piece_dst->j] = piece_dst;
00599 
00600          /*dump_solution();*/
00601          if(is_completed())
00602            {
00603              gamewon = TRUE;
00604              hanoi_destroy_all_items();
00605              gc_bonus_display(gamewon, BONUS_SMILEY);
00606            }
00607        }
00608       break;
00609 
00610     default:
00611       break;
00612     }
00613 
00614 
00615   return FALSE;
00616 }
00617