algebra_guesscount.c 21.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* gcompris - algebra_guesscount.c
 *
 * Copyright (C) 2001 Pascal Georges
 *
 *   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 2 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, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "gcompris/gcompris.h"

22 23
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
24

25 26 27 28 29 30
static void		 start_board (GcomprisBoard *agcomprisBoard);
static void		 pause_board (gboolean pause);
static void		 end_board (void);
static gboolean		 is_our_board (GcomprisBoard *gcomprisBoard);
static void		 set_level (guint level);

31 32
static int gamewon;
static gint process_time_id = 0;
33 34 35 36

static void		 process_time(void);
static void		 game_won(void);
static void		 destroy_board(void);
37 38 39 40 41 42 43

/* 4 levels :
 * 1evel 1 : 2 numbers and 1 operation
 * 1evel 2 : 3 numbers and 2 operations
 * 1evel 3 : 4 numbers and 3 operations
 * 1evel 4 : 5 numbers and 4 operations
 */
44
#define NUMBER_OF_SUBLEVELS 3 // 3
45
#define NUMBER_OF_LEVELS 4 // 4
46 47
#define MAX_NUMBER 5

48 49 50 51 52 53
#define TEXT_COLOR_FRONT "yellow"
#define TEXT_COLOR_BACK "black"

#define TEXT_RESULT_COLOR_FRONT "red"
#define TEXT_RESULT_COLOR_BACK "orange"

54
#define BLANK "___"
55 56 57

#define NO_RESULT -1

58 59 60
#define BUTTON_WIDTH 81
#define BUTTON_HEIGHT 64
#define HORIZONTAL_SEPARATION 20
61
#define VERTICAL_SEPARATION 20
62

Bruno Coudoin's avatar
Bruno Coudoin committed
63 64 65 66
static char* background_images[] = {"algebra_guesscount/tiger1_by_Ralf_Schmode",
				    "algebra_guesscount/tigerdrink001.jpg",
				    "algebra_guesscount/tigercub003.jpg",
				    "algebra_guesscount/tigerplay001.jpg"};
67 68 69
static const char  oper_values[] = {'+', '-', 'x', ':', '='};
static const char *oper_images[] = {"plus", "minus", "by", "div", "equal"};
static const int   num_values[] = {1,2,3,4,5,6,7,8,9,10,25,50,100};
70 71 72
#define NUM_VALUES 13
#define Y_OPE 20
#define Y_NUM 100
73 74 75 76 77 78 79 80 81 82
#define Y_ANS 200

#define X_NUM1 300
#define X_OPE 400
#define X_NUM2 500
#define X_EQUAL 600
#define X_RESULT 700

typedef struct _token token;
struct _token {
Bruno Coudoin's avatar
Bruno Coudoin committed
83 84 85 86 87 88 89
  gboolean isNumber;
  gboolean isMoved;
  char oper;
  int num;
  int xOffset_original;
  int signal_id;
  GnomeCanvasItem * item;
90 91 92 93 94 95 96
};

// contains the values, NUM OPER NUM OPER NUM etc.
token token_value[MAX_NUMBER*2-1];
token * ptr_token_selected[MAX_NUMBER*2-1];

static const int y_equal_offset[] = {Y_ANS,Y_ANS+BUTTON_HEIGHT+VERTICAL_SEPARATION,
Bruno Coudoin's avatar
Bruno Coudoin committed
97
				     Y_ANS+2*BUTTON_HEIGHT+2*VERTICAL_SEPARATION,Y_ANS+3*BUTTON_HEIGHT+3*VERTICAL_SEPARATION};
98 99 100

static const int x_token_offset[] = {X_NUM1,X_OPE,X_NUM2,X_OPE,X_NUM2,X_OPE,X_NUM2,X_OPE,X_NUM2};
static const int y_token_offset[] = {Y_ANS, Y_ANS,Y_ANS,
Bruno Coudoin's avatar
Bruno Coudoin committed
101 102 103
				     Y_ANS+BUTTON_HEIGHT+VERTICAL_SEPARATION, Y_ANS+BUTTON_HEIGHT+VERTICAL_SEPARATION,
				     Y_ANS+2*BUTTON_HEIGHT+2*VERTICAL_SEPARATION,Y_ANS+2*BUTTON_HEIGHT+2*VERTICAL_SEPARATION,
				     Y_ANS+3*BUTTON_HEIGHT+3*VERTICAL_SEPARATION, Y_ANS+3*BUTTON_HEIGHT+3*VERTICAL_SEPARATION};
104 105

static char answer_oper[MAX_NUMBER-1];
106 107 108
static int answer_num_index[MAX_NUMBER];
static int token_count;
static int result_to_find;
109 110 111 112 113

/* ================================================================ */
static GnomeCanvasGroup *boardRootItem = NULL;

static GdkPixbuf * num_pixmap[NUM_VALUES];
114
static GdkPixbuf * oper_pixmap[5];
115 116 117 118
static GdkPixbuf *button_pixmap = NULL;

static GnomeCanvasItem *oper_item[4];
static GnomeCanvasItem *num_item[MAX_NUMBER];
119 120
static GnomeCanvasItem *equal_item[NUMBER_OF_LEVELS];
static GnomeCanvasItem *calcul_line_item[NUMBER_OF_LEVELS*2];
121 122
static GnomeCanvasItem *calcul_line_item_back[NUMBER_OF_LEVELS*2];
static GnomeCanvasItem *result_item_front, *result_item_back;
123 124 125 126 127 128

static GnomeCanvasItem *algebra_guesscount_create_item(GnomeCanvasGroup *parent);
static void algebra_guesscount_destroy_all_items(void);
static void algebra_guesscount_next_level(void);
static gint item_event_num(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
static gint item_event_oper(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
129 130 131 132
static gint item_event_oper_moved(GnomeCanvasItem *item, GdkEvent *event, gpointer data);

static int generate_numbers();
static int token_result();
133 134

/* Description of this plugin */
135
static BoardPlugin menu_bp =
136 137 138
  {
    NULL,
    NULL,
139 140
    "Guess operations",
    "Guess operations",
141 142 143 144 145 146 147 148 149 150
    "Pascal Georges pascal.georges1@free.fr>",
    NULL,
    NULL,
    NULL,
    NULL,
    start_board,
    pause_board,
    end_board,
    is_our_board,
    NULL,
151
    NULL,
152 153
    set_level,
    NULL,
154 155
    NULL,
    NULL,
156 157 158 159 160 161 162 163
    NULL
  };

/* ==================================== */
/*
 * Main entry point mandatory for each Gcompris's game
 * ---------------------------------------------------
 */
164
GET_BPLUGIN_INFO(algebra_guesscount)
165 166 167 168 169 170 171 172

/* ==================================== */
// in : boolean TRUE = PAUSE : FALSE = CONTINUE
static void pause_board (gboolean pause){
  if(gcomprisBoard==NULL)
    return;

  if(gamewon == TRUE && pause == FALSE) {
Bruno Coudoin's avatar
Bruno Coudoin committed
173 174
    game_won();
  }
175 176 177 178 179 180

  board_paused = pause;
}

/* ==================================== */
static void start_board (GcomprisBoard *agcomprisBoard) {
Bruno Coudoin's avatar
Bruno Coudoin committed
181
  int i;
182 183 184
  gchar *str;

  if(agcomprisBoard!=NULL){
Bruno Coudoin's avatar
Bruno Coudoin committed
185 186 187
    gcomprisBoard=agcomprisBoard;

    // load pixmap files
188
    g_warning("loading pixmaps in start_board\n");
Bruno Coudoin's avatar
Bruno Coudoin committed
189 190
    for (i=0; i<NUM_VALUES; i++) {
      str = g_strdup_printf("%s/%d.png", gcomprisBoard->boarddir,num_values[i]);
191
      num_pixmap[i] = gc_pixmap_load(str);
Bruno Coudoin's avatar
Bruno Coudoin committed
192 193 194
      g_free(str);
    }
    for (i=0; i<5; i++) {
195
      str = g_strdup_printf("%s/%s.png", gcomprisBoard->boarddir,oper_images[i]);
196
      oper_pixmap[i] = gc_pixmap_load(str);
Bruno Coudoin's avatar
Bruno Coudoin committed
197
      g_free(str);
198
    }
Bruno Coudoin's avatar
Bruno Coudoin committed
199 200

    str = g_strdup_printf("%s/%s", gcomprisBoard->boarddir,"button.png");
201
    button_pixmap = gc_pixmap_load(str);
Bruno Coudoin's avatar
Bruno Coudoin committed
202 203
    g_free(str);

Bruno Coudoin's avatar
Bruno Coudoin committed
204
    gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),"algebra_guesscount/tiger1_by_Ralf_Schmode.jpg");
Bruno Coudoin's avatar
Bruno Coudoin committed
205 206 207 208
    gcomprisBoard->level=1;
    gcomprisBoard->maxlevel=NUMBER_OF_LEVELS;
    gcomprisBoard->sublevel=1;
    gcomprisBoard->number_of_sublevel=NUMBER_OF_SUBLEVELS; /* Go to next level after this number of 'play' */
209
    gc_score_start(SCORESTYLE_NOTE,
Bruno Coudoin's avatar
Bruno Coudoin committed
210 211 212
			 50,
			 gcomprisBoard->height - 50,
			 gcomprisBoard->number_of_sublevel);
213
    gc_bar_set(GC_BAR_LEVEL);
Bruno Coudoin's avatar
Bruno Coudoin committed
214 215

    algebra_guesscount_next_level();
216

Bruno Coudoin's avatar
Bruno Coudoin committed
217
    gamewon = FALSE;
218

Bruno Coudoin's avatar
Bruno Coudoin committed
219 220
    pause_board(FALSE);
  }
221 222 223 224 225
}

/* ==================================== */
static void end_board () {
  if(gcomprisBoard!=NULL) {
Bruno Coudoin's avatar
Bruno Coudoin committed
226
    pause_board(TRUE);
227
    gc_score_end();
Bruno Coudoin's avatar
Bruno Coudoin committed
228 229 230
    destroy_board();
    algebra_guesscount_destroy_all_items();
  }
231
  gcomprisBoard = NULL;
232 233 234 235 236
}

/* ==================================== */
static void set_level (guint level) {
  if(gcomprisBoard!=NULL) {
Bruno Coudoin's avatar
Bruno Coudoin committed
237 238 239 240
    gcomprisBoard->level=level;
    gcomprisBoard->sublevel=1;
    algebra_guesscount_next_level();
  }
241 242 243
}

/* ==================================== */
244
static gboolean is_our_board (GcomprisBoard *gcomprisBoard) {
245
  if (gcomprisBoard) {
Bruno Coudoin's avatar
Bruno Coudoin committed
246 247 248 249 250
    if(g_strcasecmp(gcomprisBoard->type, "algebra_guesscount")==0) {
      /* Set the plugin entry */
      gcomprisBoard->plugin=&menu_bp;
      return TRUE;
    }
251
  }
252 253 254 255 256 257
  return FALSE;
}

/* ==================================== */
/* set initial values for the next level */
static void algebra_guesscount_next_level() {
258
  gc_bar_set_level(gcomprisBoard);
259 260 261

  algebra_guesscount_destroy_all_items();
  gamewon = FALSE;
Bruno Coudoin's avatar
Bruno Coudoin committed
262
  token_count = 0;
263

264
  gc_score_set(gcomprisBoard->sublevel);
265 266 267 268 269 270 271 272 273 274 275 276 277

  /* Try the next level */
  algebra_guesscount_create_item(gnome_canvas_root(gcomprisBoard->canvas));
}
/* ==================================== */
/* Destroy all the items */
static void algebra_guesscount_destroy_all_items() {
  if(boardRootItem!=NULL)
    gtk_object_destroy (GTK_OBJECT(boardRootItem));

  boardRootItem = NULL;
}
/* ==================================== */
278
static int token_result() {
Bruno Coudoin's avatar
Bruno Coudoin committed
279 280 281 282 283
  int result, i;

  if (token_count < 2)
    return NO_RESULT;

284
  g_assert(ptr_token_selected[0]->isNumber);
Bruno Coudoin's avatar
Bruno Coudoin committed
285 286 287
  result = num_values[ptr_token_selected[0]->num];

  for (i=2; i<token_count; i+=2) {
288
    g_assert(!ptr_token_selected[i-1]->isNumber);
Bruno Coudoin's avatar
Bruno Coudoin committed
289 290 291 292 293 294 295 296 297 298 299 300 301
    switch (ptr_token_selected[i-1]->oper) {
    case '+' : 	result += num_values[ptr_token_selected[i]->num];
      break;
    case '-' :		if (result - num_values[ptr_token_selected[i]->num] < 0)
      return NO_RESULT;
      result -= num_values[ptr_token_selected[i]->num];
      break;
    case 'x' : 	result *= num_values[ptr_token_selected[i]->num];
      break;
    case ':' :		if (result%num_values[ptr_token_selected[i]->num] != 0)
      return NO_RESULT;
      result /= num_values[ptr_token_selected[i]->num];
      break;
302
    default : g_warning("bug in token_result()\n"); break;
Bruno Coudoin's avatar
Bruno Coudoin committed
303 304 305
    }
  }
  return result;
306 307
}
/* ==================================== */
308
static void update_line_calcul() {
Bruno Coudoin's avatar
Bruno Coudoin committed
309 310 311 312 313 314 315 316 317
  int line;
  char str[12];

  // finds which line has to be zeroed.
  if (token_count%2 == 0)
    line = (int)(token_count/2-1);
  else
    line = (int)(token_count/2);

318 319 320
  if(line==-1)
    return;

Bruno Coudoin's avatar
Bruno Coudoin committed
321
  sprintf(str, "%d",token_result());
322
  gnome_canvas_item_set(calcul_line_item[line*2],      "text", BLANK, NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
323
  gnome_canvas_item_set(calcul_line_item_back[line*2], "text", BLANK, NULL);
324 325 326 327 328

  if(line < gcomprisBoard->level-1) {			/* No next line to update on last line */
    gnome_canvas_item_set(calcul_line_item[line*2+1],      "text", BLANK, NULL);
    gnome_canvas_item_set(calcul_line_item_back[line*2+1], "text", BLANK, NULL);
  }
329

330 331
}
/* ==================================== */
332
static int generate_numbers() {
Bruno Coudoin's avatar
Bruno Coudoin committed
333
  int i, r, j, result;
334 335 336
  gboolean minus, divide;

  for (i=0; i<gcomprisBoard->level+1; i++) {
337
    j = g_random_int_range(0,NUM_VALUES-1);
338
    answer_num_index[i] = j;
339 340
  }

341 342
  result = num_values[answer_num_index[0]];
  for (i=0; i<gcomprisBoard->level; i++) {
343
    // + and x can always be chosen, but we must ensure - and / are valid
Bruno Coudoin's avatar
Bruno Coudoin committed
344
    minus = (result - num_values[answer_num_index[i+1]] >= 0);
345

346 347 348 349 350 351
    if(gcomprisBoard->level > 2 && num_values[answer_num_index[i+1]] <= 5) {       /* Avoid div operator at lower level */
      divide = (result % num_values[answer_num_index[i+1]] == 0);
    } else {
      divide = 0;
    }
    r = 2 + minus + divide;
352

353
    switch (g_random_int_range(1,r)) {
354
    case 1 :
355
      answer_oper[i] = '+';
Bruno Coudoin's avatar
Bruno Coudoin committed
356 357
      result += num_values[answer_num_index[i+1]];
      break;
358 359 360
    case 2 :		// prevent result from getting too big and accept only < 10 bor the by operator
      if ( (result*num_values[answer_num_index[i+1]] < 1000 ) &&
	   ( num_values[answer_num_index[i+1]] < 10) ) {
Bruno Coudoin's avatar
Bruno Coudoin committed
361 362 363 364 365 366 367
	answer_oper[i] = 'x';
	result *= num_values[answer_num_index[i+1]];
      } else {
	answer_oper[i] = '+';
	result += num_values[answer_num_index[i+1]];
      }
      break;
368 369 370 371
    case 3 :
      if (minus) {
	answer_oper[i] = '-';
	result -= num_values[answer_num_index[i+1]];
372
	g_assert(result >= 0);
373 374
      } else {
	answer_oper[i] = ':';
375
	g_assert(result%num_values[answer_num_index[i+1]] == 0);
376 377
	result /= num_values[answer_num_index[i+1]];
      }
Bruno Coudoin's avatar
Bruno Coudoin committed
378
      break;
379
    case 4 :
380
      if ( g_random_int_range(0,1) == 0) {
381 382
	answer_oper[i] = '-';
	result -= num_values[answer_num_index[i+1]];
383
	g_assert(result >= 0);
384 385
      } else {
	answer_oper[i] = ':';
386
	g_assert(result%num_values[answer_num_index[i+1]] == 0);
387 388
	result /= num_values[answer_num_index[i+1]];
      }
Bruno Coudoin's avatar
Bruno Coudoin committed
389
      break;
390
    default : g_warning("Bug in guesscount\n"); break;
391 392
    }
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
393
  return result;
394 395 396 397
}
/* ==================================== */
static GnomeCanvasItem *algebra_guesscount_create_item(GnomeCanvasGroup *parent) {
  int i, xOffset, sid;
Bruno Coudoin's avatar
Bruno Coudoin committed
398
  char str[10];
399

Bruno Coudoin's avatar
Bruno Coudoin committed
400
  result_to_find = generate_numbers();
401

Bruno Coudoin's avatar
Bruno Coudoin committed
402
  boardRootItem = GNOME_CANVAS_GROUP(
403 404 405 406 407
				     gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
							    gnome_canvas_group_get_type (),
							    "x", (double) 0,
							    "y", (double) 0,
							    NULL));
408

Bruno Coudoin's avatar
Bruno Coudoin committed
409 410 411 412 413
  // the intermediate result, line by line, when empty is BLANK
  for (i=0; i<gcomprisBoard->level; i++) {
    calcul_line_item_back[i*2] = gnome_canvas_item_new (boardRootItem,
							gnome_canvas_text_get_type (),
							"text", BLANK,
414
							"font", gc_skin_font_board_title_bold,
Bruno Coudoin's avatar
Bruno Coudoin committed
415 416 417 418 419 420 421 422
							"x", (double) X_EQUAL+BUTTON_WIDTH*1.5 + 1,
							"y", (double) y_equal_offset[i]+BUTTON_HEIGHT/2 + 1,
							"anchor", GTK_ANCHOR_CENTER,
							"fill_color", TEXT_COLOR_BACK,
							NULL);
    calcul_line_item[i*2] = gnome_canvas_item_new (boardRootItem,
						   gnome_canvas_text_get_type (),
						   "text", BLANK,
423
						   "font", gc_skin_font_board_title_bold,
Bruno Coudoin's avatar
Bruno Coudoin committed
424 425 426 427 428 429 430 431 432 433 434
						   "x", (double) X_EQUAL+BUTTON_WIDTH*1.5,
						   "y", (double) y_equal_offset[i]+BUTTON_HEIGHT/2,
						   "anchor", GTK_ANCHOR_CENTER,
						   "fill_color", TEXT_COLOR_FRONT,
						   NULL);
  }

  for (i=0; i<gcomprisBoard->level-1; i++) {
    calcul_line_item_back[i*2+1] = gnome_canvas_item_new (boardRootItem,
							  gnome_canvas_text_get_type (),
							  "text", BLANK,
435
							  "font", gc_skin_font_board_title_bold,
Bruno Coudoin's avatar
Bruno Coudoin committed
436 437 438 439 440 441 442 443
							  "x", (double) X_NUM1+BUTTON_WIDTH/2 + 1,
							  "y", (double) y_equal_offset[i+1]+BUTTON_HEIGHT/2 + 1,
							  "anchor", GTK_ANCHOR_CENTER,
							  "fill_color", TEXT_COLOR_BACK,
							  NULL);
    calcul_line_item[i*2+1] = gnome_canvas_item_new (boardRootItem,
						     gnome_canvas_text_get_type (),
						     "text", BLANK,
444
						     "font", gc_skin_font_board_title_bold,
Bruno Coudoin's avatar
Bruno Coudoin committed
445 446 447 448 449 450
						     "x", (double) X_NUM1+BUTTON_WIDTH/2,
						     "y", (double) y_equal_offset[i+1]+BUTTON_HEIGHT/2,
						     "anchor", GTK_ANCHOR_CENTER,
						     "fill_color", TEXT_COLOR_FRONT,
						     NULL);
  }
451 452

  xOffset = (gcomprisBoard->width - 4 * BUTTON_WIDTH - 3 * HORIZONTAL_SEPARATION)/2;
Bruno Coudoin's avatar
Bruno Coudoin committed
453 454 455 456 457 458 459 460 461 462 463 464
  for (i=0; i<4; i++) {
    oper_item[i] = gnome_canvas_item_new (boardRootItem,
					  gnome_canvas_pixbuf_get_type (),
					  "pixbuf", oper_pixmap[i],
					  "x", (double) xOffset ,
					  "y", (double) Y_OPE,
					  "width", (double) BUTTON_WIDTH,
					  "height", (double) BUTTON_HEIGHT,
					  "width_set", TRUE,
					  "height_set", TRUE,
					  NULL);
    xOffset += BUTTON_WIDTH+HORIZONTAL_SEPARATION;
465 466
    gtk_signal_connect(GTK_OBJECT(oper_item[i]), "event", (GtkSignalFunc) item_event_oper,
		       GINT_TO_POINTER(&(token_value[i*2+1])) );
Bruno Coudoin's avatar
Bruno Coudoin committed
467 468 469 470 471 472 473 474 475 476
    token_value[i*2+1].isNumber = FALSE;
    token_value[i*2+1].isMoved = FALSE;
    token_value[i*2+1].oper = oper_values[i];
  }

  // displays the target result
  sprintf(str,"%d",result_to_find);
  result_item_back = gnome_canvas_item_new (boardRootItem,
					    gnome_canvas_text_get_type (),
					    "text", str,
477
					    "font", gc_skin_font_board_title_bold,
Bruno Coudoin's avatar
Bruno Coudoin committed
478 479 480 481 482 483 484 485
					    "x", (double) xOffset+BUTTON_WIDTH +1,
					    "y", (double) Y_OPE+BUTTON_HEIGHT/2 +1,
					    "anchor", GTK_ANCHOR_CENTER,
					    "fill_color", TEXT_RESULT_COLOR_BACK,
					    NULL);
  result_item_front = gnome_canvas_item_new (boardRootItem,
					     gnome_canvas_text_get_type (),
					     "text", str,
486
					     "font", gc_skin_font_board_title_bold,
Bruno Coudoin's avatar
Bruno Coudoin committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
					     "x", (double) xOffset+BUTTON_WIDTH,
					     "y", (double) Y_OPE+BUTTON_HEIGHT/2,
					     "anchor", GTK_ANCHOR_CENTER,
					     "fill_color", TEXT_RESULT_COLOR_FRONT,
					     NULL);

  xOffset = (gcomprisBoard->width - (gcomprisBoard->level+1) * BUTTON_WIDTH - gcomprisBoard->level * HORIZONTAL_SEPARATION)/2;
  for (i=0; i<gcomprisBoard->level+1; i++) {
    num_item[i] = gnome_canvas_item_new (boardRootItem,
					 gnome_canvas_pixbuf_get_type (),
					 "pixbuf", num_pixmap[answer_num_index[i]],
					 "x", (double) xOffset ,
					 "y", (double) Y_NUM,
					 "width", (double) BUTTON_WIDTH,
					 "height", (double) BUTTON_HEIGHT,
					 "width_set", TRUE,
					 "height_set", TRUE,
					 NULL);
    sid = gtk_signal_connect(GTK_OBJECT(num_item[i]), "event", (GtkSignalFunc) item_event_num,
			     (void *)&(token_value[i*2]));
    token_value[i*2].isNumber = TRUE;
    token_value[i*2].num = answer_num_index[i];
    token_value[i*2].signal_id = sid;
    token_value[i*2].item = num_item[i];
    token_value[i*2].isMoved = FALSE;
    token_value[i*2].xOffset_original = xOffset;
    xOffset += BUTTON_WIDTH+HORIZONTAL_SEPARATION;
  }

  // items "="
  for (i=0; i<gcomprisBoard->level; i++) {
    equal_item[i] = gnome_canvas_item_new (boardRootItem,
					   gnome_canvas_pixbuf_get_type (),
					   "pixbuf", oper_pixmap[4],
					   "x", (double) X_EQUAL ,
					   "y", (double) y_equal_offset[i],
					   "width", (double) BUTTON_WIDTH,
					   "height", (double) BUTTON_HEIGHT,
					   "width_set", TRUE,
					   "height_set", TRUE,
					   NULL);
  }
529

530 531 532 533 534 535 536 537 538 539
  return NULL;
}
/* ==================================== */
static void game_won() {
  gcomprisBoard->sublevel++;

  if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
    /* Try the next level */
    gcomprisBoard->sublevel=1;
    gcomprisBoard->level++;
540

541
    if(gcomprisBoard->level>gcomprisBoard->maxlevel) {
542
      gc_bonus_end_display(GC_BOARD_FINISHED_TUXPLANE);
Bruno Coudoin's avatar
Bruno Coudoin committed
543
      return;
544
    }
545
    gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),background_images[gcomprisBoard->level-1]);
Bruno Coudoin's avatar
Bruno Coudoin committed
546
  }
547 548 549 550 551 552 553 554 555
  algebra_guesscount_next_level();
}

/* ==================================== */
static void process_time(){
  if (process_time_id) {
    gtk_timeout_remove (process_time_id);
    process_time_id = 0;
  }
556
  gc_bonus_display(gamewon, GC_BONUS_RANDOM);
557 558
}
/* ==================================== */
559
static int oper_char_to_pixmap_index(char oper) {
Bruno Coudoin's avatar
Bruno Coudoin committed
560
  int i;
561

562
  g_assert(oper == '+' || oper == '-' || oper == 'x' || oper == ':' || oper == '=');
563

Bruno Coudoin's avatar
Bruno Coudoin committed
564 565 566
  for (i=0; i<5; i++)
    if (oper_values[i] == oper)
      return i;
567

Bruno Coudoin's avatar
Bruno Coudoin committed
568
  return -1;
569 570
}
/* ==================================== */
571
static gint item_event_oper(GnomeCanvasItem *item, GdkEvent *event, gpointer data){
Bruno Coudoin's avatar
Bruno Coudoin committed
572
  token *t = (	token *)data;
573

Bruno Coudoin's avatar
Bruno Coudoin committed
574
  GnomeCanvasItem * tmp_item;
575

Bruno Coudoin's avatar
Bruno Coudoin committed
576 577 578 579
  if(board_paused)
    return FALSE;
  // first verify it is oper turn
  if (token_count % 2 == 0 || token_count >= 2*gcomprisBoard->level+1)
580 581
    return FALSE;

582
  switch (event->type) {
Bruno Coudoin's avatar
Bruno Coudoin committed
583
  case GDK_BUTTON_PRESS:
584
    gc_sound_play_ogg ("sounds/flip.wav", NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
585 586
    ptr_token_selected[token_count] = t;
    tmp_item = gnome_canvas_item_new (boardRootItem,
587
				      gnome_canvas_pixbuf_get_type (),
588
				      "pixbuf", oper_pixmap[oper_char_to_pixmap_index(t->oper)],
589 590 591 592 593 594 595
				      "x", (double) x_token_offset[token_count],
				      "y", (double) y_token_offset[token_count],
				      "width", (double) BUTTON_WIDTH,
				      "height", (double) BUTTON_HEIGHT,
				      "width_set", TRUE,
				      "height_set", TRUE,
				      NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
596 597 598
    token_count++;
    gtk_signal_connect(GTK_OBJECT(tmp_item), "event", (GtkSignalFunc) item_event_oper_moved, GINT_TO_POINTER(token_count));
    break;
599
  default : break;
Bruno Coudoin's avatar
Bruno Coudoin committed
600
  }
601 602 603
  return FALSE;
}
/* ==================================== */
604
static gint item_event_oper_moved(GnomeCanvasItem *item, GdkEvent *event, gpointer data){
Bruno Coudoin's avatar
Bruno Coudoin committed
605
  int count = GPOINTER_TO_INT(data);
606

Bruno Coudoin's avatar
Bruno Coudoin committed
607
  if(board_paused)
608 609
    return FALSE;
  // we only allow the undo of an operation if it is the last element displayed
610
  switch (event->type) {
Bruno Coudoin's avatar
Bruno Coudoin committed
611
  case GDK_BUTTON_PRESS:
612
    gc_sound_play_ogg ("sounds/flip.wav", NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
613 614 615 616
    if (count == token_count) {
      gtk_object_destroy (GTK_OBJECT(item));
      token_count--;
      update_line_calcul();
617
    }
Bruno Coudoin's avatar
Bruno Coudoin committed
618
    break;
619
  default : break;
Bruno Coudoin's avatar
Bruno Coudoin committed
620
  }
621 622 623
  return FALSE;
}
/* ==================================== */
624
static gint item_event_num(GnomeCanvasItem *item, GdkEvent *event, gpointer data){
Bruno Coudoin's avatar
Bruno Coudoin committed
625
  token *t = (token *)data;
626
  char str[12];
627

Bruno Coudoin's avatar
Bruno Coudoin committed
628
  if(board_paused)
629 630
    return FALSE;

631
  switch (event->type){
Bruno Coudoin's avatar
Bruno Coudoin committed
632
  case GDK_BUTTON_PRESS:
633
    gc_sound_play_ogg ("sounds/bleep.wav", NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
634 635 636 637
    if (t->isMoved) {
      if (item != ptr_token_selected[token_count-1]->item)
	return FALSE;
      // we put back in its original place a number item
638
      gc_item_absolute_move(item, t->xOffset_original, Y_NUM);
Bruno Coudoin's avatar
Bruno Coudoin committed
639 640 641 642 643 644 645 646 647 648 649 650 651 652
      token_count--;
      update_line_calcul();
      t->isMoved = FALSE;
    } else { // the item is at its original place
      if (token_count % 2 == 1 || token_count > 2*gcomprisBoard->level+1)
	return FALSE;
      token_count++;
      ptr_token_selected[token_count-1] = t;
      // some operations are illegal A - B must be > 0 and A/B an integer
      if (token_result() == NO_RESULT && token_count != 1) {
	token_count--;
	return FALSE;
      }

653
      gc_item_absolute_move(item, x_token_offset[token_count-1], y_token_offset[token_count-1]);
Bruno Coudoin's avatar
Bruno Coudoin committed
654 655 656 657 658
      t->isMoved = TRUE;

      // update result text items
      if (token_count != 1 && token_count % 2 == 1) {
	sprintf(str,"%d",token_result());
659

Bruno Coudoin's avatar
Bruno Coudoin committed
660 661
	gnome_canvas_item_set(calcul_line_item[token_count-3], "text", str, NULL);
	gnome_canvas_item_set(calcul_line_item_back[token_count-3], "text", str, NULL);
662 663 664 665 666

	if(token_count < 2*gcomprisBoard->level+1) {			/* No next line to update on last line */
	  gnome_canvas_item_set(calcul_line_item[token_count-2], "text", str, NULL);
	  gnome_canvas_item_set(calcul_line_item_back[token_count-2], "text", str, NULL);
	}
667 668 669 670 671 672

	gamewon = (result_to_find == token_result());
	if(gamewon)
	  process_time_id = gtk_timeout_add (2000, (GtkFunction) process_time, NULL);


Bruno Coudoin's avatar
Bruno Coudoin committed
673
      }
674
    }
Bruno Coudoin's avatar
Bruno Coudoin committed
675 676 677
    break;
  default : break;
  }
678 679 680 681
  return FALSE;
}
/* ======================================= */
static void destroy_board() {
Bruno Coudoin's avatar
Bruno Coudoin committed
682 683 684 685 686 687
  int i;

  for (i=0; i<NUM_VALUES; i++)
    gdk_pixbuf_unref(num_pixmap[i]);
  for (i=0; i<5; i++)
    gdk_pixbuf_unref(oper_pixmap[i]);
688
}