3
* Copyright (C) 2003, 2008 Bruno Coudoin
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, see <http://www.gnu.org/licenses/>.
19
#include "gcompris/gcompris.h"
21
#define SOUNDLISTFILE PACKAGE
23
static GcomprisBoard *gcomprisBoard = NULL;
24
static gboolean board_paused = TRUE;
26
static void start_board (GcomprisBoard *agcomprisBoard);
27
static void pause_board (gboolean pause);
28
static void end_board (void);
29
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
30
static void set_level (guint level);
32
static void game_won(void);
34
static GooCanvasItem *boardRootItem = NULL;
36
static GooCanvasItem *hanoi_create_item(GooCanvasItem *parent);
37
static void hanoi_destroy_all_items(void);
38
static void hanoi_next_level(void);
41
* Contains the piece information
53
static gint item_event(GooCanvasItem *item,
54
GooCanvasItem *target,
55
GdkEvent *event, PieceItem *data);
57
/* This contains the layout of the pieces */
58
#define MAX_NUMBER_X 10
59
#define MAX_NUMBER_Y 10
60
static PieceItem *position[MAX_NUMBER_X][MAX_NUMBER_Y];
62
static int number_of_item = 0;
63
static int number_of_item_x = 0;
64
static int number_of_item_y = 0;
65
static int item_width;
66
static int item_height;
68
static guint colorlist [] =
86
#define NUMBER_OF_COLOR G_N_ELEMENTS(colorlist)
88
static char symbollist [NUMBER_OF_COLOR] =
107
/* Description of this plugin */
108
static BoardPlugin menu_bp =
112
N_("Simplified Tower of Hanoi"),
113
N_("Reproduce the given tower"),
114
"Bruno Coudoin <bruno.coudoin@free.fr>",
133
* Main entry point mandatory for each Gcompris's game
134
* ---------------------------------------------------
138
GET_BPLUGIN_INFO(hanoi)
141
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
145
pause_board (gboolean pause)
147
if(gcomprisBoard==NULL)
150
if(gamewon == TRUE && pause == FALSE) /* the game is won */
155
board_paused = pause;
161
start_board (GcomprisBoard *agcomprisBoard)
164
if(agcomprisBoard!=NULL)
166
gcomprisBoard=agcomprisBoard;
167
gcomprisBoard->level=1;
168
gcomprisBoard->maxlevel=6;
169
gcomprisBoard->sublevel=1;
170
gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
171
gc_bar_set(GC_BAR_LEVEL);
173
gc_set_default_background(goo_canvas_get_root_item(gcomprisBoard->canvas));
175
gc_drag_start(goo_canvas_get_root_item(gcomprisBoard->canvas),
176
(GcDragFunc)item_event,
177
GC_DRAG_MODE_DEFAULT);
185
/* ======================================= */
189
if(gcomprisBoard!=NULL)
191
gc_drag_stop(goo_canvas_get_root_item(gcomprisBoard->canvas));
193
hanoi_destroy_all_items();
195
gcomprisBoard = NULL;
198
/* ======================================= */
199
static void set_level (guint level)
202
if(gcomprisBoard!=NULL)
204
gcomprisBoard->level=level;
205
gcomprisBoard->sublevel=1;
209
/* ======================================= */
210
static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
214
if(g_strcasecmp(gcomprisBoard->type, "hanoi")==0)
216
/* Set the plugin entry */
217
gcomprisBoard->plugin=&menu_bp;
225
/*-------------------------------------------------------------------------------*/
226
/*-------------------------------------------------------------------------------*/
227
/* set initial values for the next level */
228
static void hanoi_next_level()
231
gc_bar_set_level(gcomprisBoard);
233
hanoi_destroy_all_items();
236
/* Select level difficulty */
237
switch(gcomprisBoard->level)
240
number_of_item_x = 3;
241
number_of_item_y = 5;
244
number_of_item_x = 4;
245
number_of_item_y = 5;
248
number_of_item_x = 5;
249
number_of_item_y = 6;
251
number_of_item_x = 6;
252
number_of_item_y = 7;
255
number_of_item_x = 6;
256
number_of_item_y = 8;
259
number_of_item_x = 5;
260
number_of_item_y = 9;
263
number_of_item_x = 5;
264
number_of_item_y = 7;
268
/* Try the next level */
269
hanoi_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
271
/* ==================================== */
272
/* Destroy all the items */
273
static void hanoi_destroy_all_items()
277
if(boardRootItem!=NULL)
279
goo_canvas_item_remove(boardRootItem);
281
/* Cleanup our memory structure */
282
for(i=0; i<(number_of_item_x+2); i++)
284
for(j=0; j<number_of_item_y; j++)
286
g_free(position[i][j]);
290
boardRootItem = NULL;
295
static void dump_solution()
299
g_warning("Dumping solution\n");
300
for(i=0; i<(number_of_item_x+2); i++)
302
for(j=0; j<number_of_item_y; j++)
304
g_warning("(%d,%d=%2d/%d) ", position[i][j]->i, position[i][j]->j, position[i][j]->color,
305
position[i][j]->on_top);
312
static void print_piece(PieceItem *piece)
314
g_warning("Piece: (%d,%d=%2d/%d)\n",
316
piece->color, piece->on_top);
320
/* ==================================== */
321
static GooCanvasItem *
322
hanoi_create_item(GooCanvasItem *parent)
327
GooCanvasItem *item = NULL;
328
guint color_to_place;
329
guint used_colors[NUMBER_OF_COLOR];
333
goo_canvas_group_new (parent,
337
if (gcomprisBoard->level == 1)
339
item = goo_canvas_svg_new (boardRootItem,
343
SET_ITEM_LOCATION_CENTER(item,
347
goo_canvas_text_new (boardRootItem,
348
_("Build the same tower in the empty area as the one you see on the right-hand side."),
353
"font", gc_skin_font_board_medium,
354
"fill_color_rgba", gc_skin_color_text_button,
358
/*----------------------------------------*/
359
/* Empty the solution */
360
for(i=0; i<(number_of_item_x+2); i++)
362
for(j=0; j<number_of_item_y; j++)
364
position[i][j] = g_malloc(sizeof(PieceItem));
365
position[i][j]->color = -1;
366
position[i][j]->i = i;
367
position[i][j]->j = j;
368
position[i][j]->on_top = FALSE;
372
/* Clear the used colors list */
373
for(i=0; i<NUMBER_OF_COLOR; i++)
374
used_colors[i] = FALSE;
376
/* Initialize a random goal and store the color index
377
in position[number_of_item_x] */
378
for(i=0; i<(number_of_item_y); i++)
380
guint color = (guint)g_random_int_range(0, NUMBER_OF_COLOR-1);
381
position[number_of_item_x+1][i]->color = color;
382
used_colors[color] = TRUE;
386
/* Randomly place the solution */
387
for (color_to_place=0; color_to_place<number_of_item_y; color_to_place++)
395
i = (guint)g_random_int_range(0, number_of_item_x);
397
/* Restrict the goal to lowest items */
398
j = (guint)g_random_int_range(0, 2);
400
if(position[i][j]->color == -1)
403
position[i][j]->color = position[number_of_item_x+1][color_to_place]->color;
409
/* Initialize the left open positions */
410
for(i=0; i<(number_of_item_x); i++)
412
for(j=0; j<number_of_item_y-1; j++)
414
if(position[i][j]->color == -1)
416
/* Take only a color that is not part of the goal */
417
guint color = (guint)g_random_int_range(0, NUMBER_OF_COLOR-1);
418
while(used_colors[color])
421
if(color >= NUMBER_OF_COLOR)
425
position[i][j]->color = color;
431
/* Mark the top pieces */
432
for(i=0; i<(number_of_item_x); i++)
434
position[i][number_of_item_y-2]->on_top = TRUE;
437
/*----------------------------------------*/
440
item_width = BOARDWIDTH / (number_of_item_x + 2);
443
gap_x = item_width * 0.1;
444
gap_y = item_height * 0.25;
446
baseline = BOARDHEIGHT/2 + item_height * number_of_item_y/2;
450
for(i=0; i<(number_of_item_x+2); i++)
452
if(i == number_of_item_x+1)
454
/* Create the backgound for the target */
455
goo_canvas_rect_new (boardRootItem,
456
item_width * i + gap_x/2,
457
baseline - item_height * number_of_item_y - gap_y - 50,
459
item_height * number_of_item_y + gap_y*2 + 100,
460
"fill_color_rgba", 0x036ED8FF,
461
"stroke-color", "black",
462
"line-width", (double)1,
465
else if (i == number_of_item_x)
467
/* Create the backgound for the empty area */
468
goo_canvas_rect_new (boardRootItem,
469
item_width * i + gap_x/2,
470
baseline - item_height * number_of_item_y - gap_y - 50,
472
item_height * number_of_item_y + gap_y*2 + 100,
473
"fill_color_rgba", 0x48AAF1FF,
474
"stroke-color", "black",
475
"line-width", (double)1,
479
/* Create the vertical line */
481
goo_canvas_rect_new (boardRootItem,
482
item_width * i + item_width/2 - w,
483
baseline - item_height * number_of_item_y - gap_y,
485
(item_height + gap_y/2 - 2) * number_of_item_y,
486
"fill_color_rgba", 0xFF1030FF,
487
"stroke-color", "black",
488
"line-width", (double)1,
491
/* And the base line */
492
item = goo_canvas_path_new (boardRootItem,
493
"M 43,19 A 22,20 0 1 1 -1,19 L 20,19 z",
494
"fill_color_rgba", 0xFF1030FF,
495
"stroke-color", "black",
498
goo_canvas_item_translate(item,
499
item_width * i + item_width/2 - 20,
502
for(j=0; j<number_of_item_y; j++)
505
position[i][j]->x = item_width * i + gap_x;
506
position[i][j]->y = baseline - item_height * j - item_height + gap_y;
508
if(position[i][j]->color != -1)
512
GooCanvasItem *group = goo_canvas_group_new(boardRootItem,
514
goo_canvas_item_translate(group,
518
position[i][j]->group = group;
520
item = goo_canvas_rect_new (group,
523
item_width - gap_x * 2,
526
colorlist[position[i][j]->color],
527
"stroke-color", "black",
528
"line-width", (double)1,
531
car[0] = symbollist[position[i][j]->color];
534
goo_canvas_text_new (group,
540
"font", "sans bold 14",
541
"fill-color", "white",
544
if(i != number_of_item_x+1)
546
g_signal_connect(item, "button_press_event",
547
(GtkSignalFunc)gc_drag_event,
549
g_signal_connect(item, "button_release_event",
550
(GtkSignalFunc)gc_drag_event,
553
g_signal_connect(item, "enter_notify_event",
554
(GtkSignalFunc) item_event,
556
g_signal_connect(item, "leave_notify_event",
557
(GtkSignalFunc) item_event,
567
/* ==================================== */
571
gcomprisBoard->sublevel++;
573
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
574
/* Try the next level */
575
gcomprisBoard->sublevel=1;
576
gcomprisBoard->level++;
577
if(gcomprisBoard->level>gcomprisBoard->maxlevel)
578
gcomprisBoard->level = gcomprisBoard->maxlevel;
580
gc_sound_play_ogg ("sounds/bonus.wav", NULL);
586
* Returns TRUE is the goal is reached
592
gboolean done = TRUE;
594
for(j=0; j<number_of_item_y; j++)
596
if(position[number_of_item_x+1][j]->color != position[number_of_item_x][j]->color)
603
/* ==================================== */
605
item_event(GooCanvasItem *item,
606
GooCanvasItem *target,
610
double item_x, item_y;
618
if(data && !data->on_top)
623
case GDK_ENTER_NOTIFY:
625
"stroke-color", "white",
626
"line-width", (double)3,
629
case GDK_LEAVE_NOTIFY:
631
"stroke-color", "black",
632
"line-width", (double)1,
635
case GDK_BUTTON_PRESS:
636
switch(event->button.button)
639
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
640
gc_drag_offset_save(event);
641
goo_canvas_item_raise(data->group, NULL);
646
case GDK_MOTION_NOTIFY:
647
gc_drag_item_move(event, data->group);
650
case GDK_BUTTON_RELEASE:
655
PieceItem *piece_src;
656
PieceItem *piece_dst;
659
item_x = event->button.x;
660
item_y = event->button.y;
661
goo_canvas_convert_from_item_space(goo_canvas_item_get_canvas(item),
665
/* Search the column (x) where this item is ungrabbed */
666
for(i=0; i<=number_of_item_x; i++)
667
if(position[i][0]->x < item_x &&
668
position[i+1][0]->x > item_x)
671
/* Bad drop / Outside of column area */
672
/* Bad drop / On the same column */
673
if(col<0 || col > number_of_item_x || col == data->i)
675
gc_sound_play_ogg ("sounds/eraser2.wav", NULL);
677
/* Return to the original position */
678
gc_item_absolute_move (data->group, data->x , data->y);
683
/* Now search the free line (y) */
684
line = number_of_item_y;
685
for(i=number_of_item_y-1; i>=0; i--)
686
if(position[col][i]->color == -1)
689
/* Bad drop / Too many pieces here */
690
if(line >= number_of_item_y)
692
gc_sound_play_ogg ("sounds/eraser2.wav", NULL);
694
/* Return to the original position */
695
gc_item_absolute_move (data->group, data->x , data->y);
700
/* Update ontop values for the piece under the grabbed one */
702
position[data->i][data->j-1]->on_top = TRUE;
704
/* Update ontop values for the piece under the ungrabbed one */
706
position[col][line-1]->on_top = FALSE;
709
piece_dst = position[col][line];
711
gc_item_absolute_move (data->group, piece_dst->x , piece_dst->y);
713
gc_sound_play_ogg ("sounds/scroll.wav", NULL);
715
/* Swap values in the pieces */
718
piece_src->x = piece_dst->x;
719
piece_src->y = piece_dst->y;
725
position[tmpi][tmpj]->i = piece_dst->i;
726
position[tmpi][tmpj]->j = piece_dst->j;
730
position[piece_src->i][piece_src->j] = piece_src;
731
position[piece_dst->i][piece_dst->j] = piece_dst;
737
hanoi_destroy_all_items();
738
gc_bonus_display(gamewon, GC_BONUS_SMILEY);