1
/* gcompris - algebra_guesscount.c
3
* Copyright (C) 2001 Pascal Georges
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
static GcomprisBoard *gcomprisBoard = NULL;
22
static gboolean board_paused = TRUE;
24
static void start_board (GcomprisBoard *agcomprisBoard);
25
static void pause_board (gboolean pause);
26
static void end_board (void);
27
static gboolean is_our_board (GcomprisBoard *gcomprisBoard);
28
static void set_level (guint level);
31
static gint process_time_id = 0;
33
static void process_time(void);
34
static void game_won(void);
35
static void destroy_board(void);
38
* 1evel 1 : 2 numbers and 1 operation
39
* 1evel 2 : 3 numbers and 2 operations
40
* 1evel 3 : 4 numbers and 3 operations
41
* 1evel 4 : 5 numbers and 4 operations
43
#define NUMBER_OF_SUBLEVELS 3 // 3
44
#define NUMBER_OF_LEVELS 4 // 4
47
#define TEXT_COLOR_FRONT "yellow"
48
#define TEXT_COLOR_BACK "black"
50
#define TEXT_RESULT_COLOR_FRONT "red"
51
#define TEXT_RESULT_COLOR_BACK "orange"
57
#define BUTTON_WIDTH 81
58
#define BUTTON_HEIGHT 64
59
#define HORIZONTAL_SEPARATION 20
60
#define VERTICAL_SEPARATION 20
62
static char* background_images[] = {"opt/animals/tiger1_by_Ralf_Schmode",
63
"opt/animals/tigerdrink001.jpg",
64
"opt/animals/tigercub003.jpg",
65
"opt/animals/tigerplay001.jpg"};
66
static const char oper_values[] = {'+', '-', 'x', ':', '='};
67
static const char *oper_images[] = {"plus", "minus", "by", "div", "equal"};
68
static const int num_values[] = {1,2,3,4,5,6,7,8,9,10,25,50,100};
80
typedef struct _token token;
88
GnomeCanvasItem * item;
91
// contains the values, NUM OPER NUM OPER NUM etc.
92
token token_value[MAX_NUMBER*2-1];
93
token * ptr_token_selected[MAX_NUMBER*2-1];
95
static const int y_equal_offset[] = {Y_ANS,Y_ANS+BUTTON_HEIGHT+VERTICAL_SEPARATION,
96
Y_ANS+2*BUTTON_HEIGHT+2*VERTICAL_SEPARATION,Y_ANS+3*BUTTON_HEIGHT+3*VERTICAL_SEPARATION};
98
static const int x_token_offset[] = {X_NUM1,X_OPE,X_NUM2,X_OPE,X_NUM2,X_OPE,X_NUM2,X_OPE,X_NUM2};
99
static const int y_token_offset[] = {Y_ANS, Y_ANS,Y_ANS,
100
Y_ANS+BUTTON_HEIGHT+VERTICAL_SEPARATION, Y_ANS+BUTTON_HEIGHT+VERTICAL_SEPARATION,
101
Y_ANS+2*BUTTON_HEIGHT+2*VERTICAL_SEPARATION,Y_ANS+2*BUTTON_HEIGHT+2*VERTICAL_SEPARATION,
102
Y_ANS+3*BUTTON_HEIGHT+3*VERTICAL_SEPARATION, Y_ANS+3*BUTTON_HEIGHT+3*VERTICAL_SEPARATION};
104
static char answer_oper[MAX_NUMBER-1];
105
static int answer_num_index[MAX_NUMBER];
106
static int token_count;
107
static int result_to_find;
109
/* ================================================================ */
110
static GnomeCanvasGroup *boardRootItem = NULL;
112
static GdkPixbuf * num_pixmap[NUM_VALUES];
113
static GdkPixbuf * oper_pixmap[5];
114
static GdkPixbuf *button_pixmap = NULL;
116
static GnomeCanvasItem *oper_item[4];
117
static GnomeCanvasItem *num_item[MAX_NUMBER];
118
static GnomeCanvasItem *equal_item[NUMBER_OF_LEVELS];
119
static GnomeCanvasItem *calcul_line_item[NUMBER_OF_LEVELS*2];
120
static GnomeCanvasItem *calcul_line_item_back[NUMBER_OF_LEVELS*2];
121
static GnomeCanvasItem *result_item_front, *result_item_back;
123
static GnomeCanvasItem *algebra_guesscount_create_item(GnomeCanvasGroup *parent);
124
static void algebra_guesscount_destroy_all_items(void);
125
static void algebra_guesscount_next_level(void);
126
static gint item_event_num(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
127
static gint item_event_oper(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
128
static gint item_event_oper_moved(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
130
static int generate_numbers();
131
static int token_result();
133
/* Description of this plugin */
134
static BoardPlugin menu_bp =
140
"Pascal Georges pascal.georges1@free.fr>",
158
/* ==================================== */
160
* Main entry point mandatory for each Gcompris's game
161
* ---------------------------------------------------
163
GET_BPLUGIN_INFO(algebra_guesscount)
165
/* ==================================== */
166
// in : boolean TRUE = PAUSE : FALSE = CONTINUE
167
static void pause_board (gboolean pause){
168
if(gcomprisBoard==NULL)
171
if(gamewon == TRUE && pause == FALSE) {
175
board_paused = pause;
178
/* ==================================== */
179
static void start_board (GcomprisBoard *agcomprisBoard) {
183
if(agcomprisBoard!=NULL){
184
gcomprisBoard=agcomprisBoard;
187
g_warning("loading pixmaps in start_board\n");
188
for (i=0; i<NUM_VALUES; i++) {
189
str = g_strdup_printf("%s/%d.png", gcomprisBoard->boarddir,num_values[i]);
190
num_pixmap[i] = gc_pixmap_load(str);
193
for (i=0; i<5; i++) {
194
str = g_strdup_printf("%s/%s.png", gcomprisBoard->boarddir,oper_images[i]);
195
oper_pixmap[i] = gc_pixmap_load(str);
199
str = g_strdup_printf("%s/%s", gcomprisBoard->boarddir,"button.png");
200
button_pixmap = gc_pixmap_load(str);
203
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),"opt/animals/tiger1_by_Ralf_Schmode.jpg");
204
gcomprisBoard->level=1;
205
gcomprisBoard->maxlevel=NUMBER_OF_LEVELS;
206
gcomprisBoard->sublevel=1;
207
gcomprisBoard->number_of_sublevel=NUMBER_OF_SUBLEVELS; /* Go to next level after this number of 'play' */
208
gc_score_start(SCORESTYLE_NOTE,
210
gcomprisBoard->height - 50,
211
gcomprisBoard->number_of_sublevel);
212
gc_bar_set(GC_BAR_LEVEL);
214
algebra_guesscount_next_level();
222
/* ==================================== */
223
static void end_board () {
224
if(gcomprisBoard!=NULL) {
228
algebra_guesscount_destroy_all_items();
230
gcomprisBoard = NULL;
233
/* ==================================== */
234
static void set_level (guint level) {
235
if(gcomprisBoard!=NULL) {
236
gcomprisBoard->level=level;
237
gcomprisBoard->sublevel=1;
238
algebra_guesscount_next_level();
242
/* ==================================== */
243
static gboolean is_our_board (GcomprisBoard *gcomprisBoard) {
245
if(g_strcasecmp(gcomprisBoard->type, "algebra_guesscount")==0) {
246
/* Set the plugin entry */
247
gcomprisBoard->plugin=&menu_bp;
254
/* ==================================== */
255
/* set initial values for the next level */
256
static void algebra_guesscount_next_level() {
257
gc_bar_set_level(gcomprisBoard);
259
algebra_guesscount_destroy_all_items();
263
gc_score_set(gcomprisBoard->sublevel);
265
/* Try the next level */
266
algebra_guesscount_create_item(gnome_canvas_root(gcomprisBoard->canvas));
268
/* ==================================== */
269
/* Destroy all the items */
270
static void algebra_guesscount_destroy_all_items() {
271
if(boardRootItem!=NULL)
272
gtk_object_destroy (GTK_OBJECT(boardRootItem));
274
boardRootItem = NULL;
276
/* ==================================== */
277
static int token_result() {
283
g_assert(ptr_token_selected[0]->isNumber);
284
result = num_values[ptr_token_selected[0]->num];
286
for (i=2; i<token_count; i+=2) {
287
g_assert(!ptr_token_selected[i-1]->isNumber);
288
switch (ptr_token_selected[i-1]->oper) {
289
case '+' : result += num_values[ptr_token_selected[i]->num];
291
case '-' : if (result - num_values[ptr_token_selected[i]->num] < 0)
293
result -= num_values[ptr_token_selected[i]->num];
295
case 'x' : result *= num_values[ptr_token_selected[i]->num];
297
case ':' : if (result%num_values[ptr_token_selected[i]->num] != 0)
299
result /= num_values[ptr_token_selected[i]->num];
301
default : g_warning("bug in token_result()\n"); break;
306
/* ==================================== */
307
static void update_line_calcul() {
311
// finds which line has to be zeroed.
312
if (token_count%2 == 0)
313
line = (int)(token_count/2-1);
315
line = (int)(token_count/2);
320
sprintf(str, "%d",token_result());
321
gnome_canvas_item_set(calcul_line_item[line*2], "text", BLANK, NULL);
322
gnome_canvas_item_set(calcul_line_item_back[line*2], "text", BLANK, NULL);
324
if(line < gcomprisBoard->level-1) { /* No next line to update on last line */
325
gnome_canvas_item_set(calcul_line_item[line*2+1], "text", BLANK, NULL);
326
gnome_canvas_item_set(calcul_line_item_back[line*2+1], "text", BLANK, NULL);
330
/* ==================================== */
331
static int generate_numbers() {
333
gboolean minus, divide;
335
for (i=0; i<gcomprisBoard->level+1; i++) {
336
j = g_random_int_range(0,NUM_VALUES-1);
337
answer_num_index[i] = j;
340
result = num_values[answer_num_index[0]];
341
for (i=0; i<gcomprisBoard->level; i++) {
342
// + and x can always be chosen, but we must ensure - and / are valid
343
minus = (result - num_values[answer_num_index[i+1]] >= 0);
345
if(gcomprisBoard->level > 2 && num_values[answer_num_index[i+1]] <= 5) { /* Avoid div operator at lower level */
346
divide = (result % num_values[answer_num_index[i+1]] == 0);
350
r = 2 + minus + divide;
352
switch (g_random_int_range(1,r)) {
354
answer_oper[i] = '+';
355
result += num_values[answer_num_index[i+1]];
357
case 2 : // prevent result from getting too big and accept only < 10 bor the by operator
358
if ( (result*num_values[answer_num_index[i+1]] < 1000 ) &&
359
( num_values[answer_num_index[i+1]] < 10) ) {
360
answer_oper[i] = 'x';
361
result *= num_values[answer_num_index[i+1]];
363
answer_oper[i] = '+';
364
result += num_values[answer_num_index[i+1]];
369
answer_oper[i] = '-';
370
result -= num_values[answer_num_index[i+1]];
371
g_assert(result >= 0);
373
answer_oper[i] = ':';
374
g_assert(result%num_values[answer_num_index[i+1]] == 0);
375
result /= num_values[answer_num_index[i+1]];
379
if ( g_random_int_range(0,1) == 0) {
380
answer_oper[i] = '-';
381
result -= num_values[answer_num_index[i+1]];
382
g_assert(result >= 0);
384
answer_oper[i] = ':';
385
g_assert(result%num_values[answer_num_index[i+1]] == 0);
386
result /= num_values[answer_num_index[i+1]];
389
default : g_warning("Bug in guesscount\n"); break;
394
/* ==================================== */
395
static GnomeCanvasItem *algebra_guesscount_create_item(GnomeCanvasGroup *parent) {
399
result_to_find = generate_numbers();
401
boardRootItem = GNOME_CANVAS_GROUP(
402
gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
403
gnome_canvas_group_get_type (),
408
// the intermediate result, line by line, when empty is BLANK
409
for (i=0; i<gcomprisBoard->level; i++) {
410
calcul_line_item_back[i*2] = gnome_canvas_item_new (boardRootItem,
411
gnome_canvas_text_get_type (),
413
"font", gc_skin_font_board_title_bold,
414
"x", (double) X_EQUAL+BUTTON_WIDTH*1.5 + 1,
415
"y", (double) y_equal_offset[i]+BUTTON_HEIGHT/2 + 1,
416
"anchor", GTK_ANCHOR_CENTER,
417
"fill_color", TEXT_COLOR_BACK,
419
calcul_line_item[i*2] = gnome_canvas_item_new (boardRootItem,
420
gnome_canvas_text_get_type (),
422
"font", gc_skin_font_board_title_bold,
423
"x", (double) X_EQUAL+BUTTON_WIDTH*1.5,
424
"y", (double) y_equal_offset[i]+BUTTON_HEIGHT/2,
425
"anchor", GTK_ANCHOR_CENTER,
426
"fill_color", TEXT_COLOR_FRONT,
430
for (i=0; i<gcomprisBoard->level-1; i++) {
431
calcul_line_item_back[i*2+1] = gnome_canvas_item_new (boardRootItem,
432
gnome_canvas_text_get_type (),
434
"font", gc_skin_font_board_title_bold,
435
"x", (double) X_NUM1+BUTTON_WIDTH/2 + 1,
436
"y", (double) y_equal_offset[i+1]+BUTTON_HEIGHT/2 + 1,
437
"anchor", GTK_ANCHOR_CENTER,
438
"fill_color", TEXT_COLOR_BACK,
440
calcul_line_item[i*2+1] = gnome_canvas_item_new (boardRootItem,
441
gnome_canvas_text_get_type (),
443
"font", gc_skin_font_board_title_bold,
444
"x", (double) X_NUM1+BUTTON_WIDTH/2,
445
"y", (double) y_equal_offset[i+1]+BUTTON_HEIGHT/2,
446
"anchor", GTK_ANCHOR_CENTER,
447
"fill_color", TEXT_COLOR_FRONT,
451
xOffset = (gcomprisBoard->width - 4 * BUTTON_WIDTH - 3 * HORIZONTAL_SEPARATION)/2;
452
for (i=0; i<4; i++) {
453
oper_item[i] = gnome_canvas_item_new (boardRootItem,
454
gnome_canvas_pixbuf_get_type (),
455
"pixbuf", oper_pixmap[i],
456
"x", (double) xOffset ,
458
"width", (double) BUTTON_WIDTH,
459
"height", (double) BUTTON_HEIGHT,
463
xOffset += BUTTON_WIDTH+HORIZONTAL_SEPARATION;
464
gtk_signal_connect(GTK_OBJECT(oper_item[i]), "event", (GtkSignalFunc) item_event_oper,
465
GINT_TO_POINTER(&(token_value[i*2+1])) );
466
token_value[i*2+1].isNumber = FALSE;
467
token_value[i*2+1].isMoved = FALSE;
468
token_value[i*2+1].oper = oper_values[i];
471
// displays the target result
472
sprintf(str,"%d",result_to_find);
473
result_item_back = gnome_canvas_item_new (boardRootItem,
474
gnome_canvas_text_get_type (),
476
"font", gc_skin_font_board_title_bold,
477
"x", (double) xOffset+BUTTON_WIDTH +1,
478
"y", (double) Y_OPE+BUTTON_HEIGHT/2 +1,
479
"anchor", GTK_ANCHOR_CENTER,
480
"fill_color", TEXT_RESULT_COLOR_BACK,
482
result_item_front = gnome_canvas_item_new (boardRootItem,
483
gnome_canvas_text_get_type (),
485
"font", gc_skin_font_board_title_bold,
486
"x", (double) xOffset+BUTTON_WIDTH,
487
"y", (double) Y_OPE+BUTTON_HEIGHT/2,
488
"anchor", GTK_ANCHOR_CENTER,
489
"fill_color", TEXT_RESULT_COLOR_FRONT,
492
xOffset = (gcomprisBoard->width - (gcomprisBoard->level+1) * BUTTON_WIDTH - gcomprisBoard->level * HORIZONTAL_SEPARATION)/2;
493
for (i=0; i<gcomprisBoard->level+1; i++) {
494
num_item[i] = gnome_canvas_item_new (boardRootItem,
495
gnome_canvas_pixbuf_get_type (),
496
"pixbuf", num_pixmap[answer_num_index[i]],
497
"x", (double) xOffset ,
499
"width", (double) BUTTON_WIDTH,
500
"height", (double) BUTTON_HEIGHT,
504
sid = gtk_signal_connect(GTK_OBJECT(num_item[i]), "event", (GtkSignalFunc) item_event_num,
505
(void *)&(token_value[i*2]));
506
token_value[i*2].isNumber = TRUE;
507
token_value[i*2].num = answer_num_index[i];
508
token_value[i*2].signal_id = sid;
509
token_value[i*2].item = num_item[i];
510
token_value[i*2].isMoved = FALSE;
511
token_value[i*2].xOffset_original = xOffset;
512
xOffset += BUTTON_WIDTH+HORIZONTAL_SEPARATION;
516
for (i=0; i<gcomprisBoard->level; i++) {
517
equal_item[i] = gnome_canvas_item_new (boardRootItem,
518
gnome_canvas_pixbuf_get_type (),
519
"pixbuf", oper_pixmap[4],
520
"x", (double) X_EQUAL ,
521
"y", (double) y_equal_offset[i],
522
"width", (double) BUTTON_WIDTH,
523
"height", (double) BUTTON_HEIGHT,
531
/* ==================================== */
532
static void game_won() {
533
gcomprisBoard->sublevel++;
535
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
536
/* Try the next level */
537
gcomprisBoard->sublevel=1;
538
gcomprisBoard->level++;
540
if(gcomprisBoard->level>gcomprisBoard->maxlevel) {
541
gc_bonus_end_display(GC_BOARD_FINISHED_TUXPLANE);
544
gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),background_images[gcomprisBoard->level-1]);
546
algebra_guesscount_next_level();
549
/* ==================================== */
550
static void process_time(){
551
if (process_time_id) {
552
gtk_timeout_remove (process_time_id);
555
gc_bonus_display(gamewon, GC_BONUS_RANDOM);
557
/* ==================================== */
558
static int oper_char_to_pixmap_index(char oper) {
561
g_assert(oper == '+' || oper == '-' || oper == 'x' || oper == ':' || oper == '=');
564
if (oper_values[i] == oper)
569
/* ==================================== */
570
static gint item_event_oper(GnomeCanvasItem *item, GdkEvent *event, gpointer data){
571
token *t = ( token *)data;
573
GnomeCanvasItem * tmp_item;
577
// first verify it is oper turn
578
if (token_count % 2 == 0 || token_count >= 2*gcomprisBoard->level+1)
581
switch (event->type) {
582
case GDK_BUTTON_PRESS:
583
gc_sound_play_ogg ("sounds/flip.wav", NULL);
584
ptr_token_selected[token_count] = t;
585
tmp_item = gnome_canvas_item_new (boardRootItem,
586
gnome_canvas_pixbuf_get_type (),
587
"pixbuf", oper_pixmap[oper_char_to_pixmap_index(t->oper)],
588
"x", (double) x_token_offset[token_count],
589
"y", (double) y_token_offset[token_count],
590
"width", (double) BUTTON_WIDTH,
591
"height", (double) BUTTON_HEIGHT,
596
gtk_signal_connect(GTK_OBJECT(tmp_item), "event", (GtkSignalFunc) item_event_oper_moved, GINT_TO_POINTER(token_count));
602
/* ==================================== */
603
static gint item_event_oper_moved(GnomeCanvasItem *item, GdkEvent *event, gpointer data){
604
int count = GPOINTER_TO_INT(data);
608
// we only allow the undo of an operation if it is the last element displayed
609
switch (event->type) {
610
case GDK_BUTTON_PRESS:
611
gc_sound_play_ogg ("sounds/flip.wav", NULL);
612
if (count == token_count) {
613
gtk_object_destroy (GTK_OBJECT(item));
615
update_line_calcul();
622
/* ==================================== */
623
static gint item_event_num(GnomeCanvasItem *item, GdkEvent *event, gpointer data){
624
token *t = (token *)data;
630
switch (event->type){
631
case GDK_BUTTON_PRESS:
632
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
634
if (item != ptr_token_selected[token_count-1]->item)
636
// we put back in its original place a number item
637
gc_item_absolute_move(item, t->xOffset_original, Y_NUM);
639
update_line_calcul();
641
} else { // the item is at its original place
642
if (token_count % 2 == 1 || token_count > 2*gcomprisBoard->level+1)
645
ptr_token_selected[token_count-1] = t;
646
// some operations are illegal A - B must be > 0 and A/B an integer
647
if (token_result() == NO_RESULT && token_count != 1) {
652
gc_item_absolute_move(item, x_token_offset[token_count-1], y_token_offset[token_count-1]);
655
// update result text items
656
if (token_count != 1 && token_count % 2 == 1) {
657
sprintf(str,"%d",token_result());
659
gnome_canvas_item_set(calcul_line_item[token_count-3], "text", str, NULL);
660
gnome_canvas_item_set(calcul_line_item_back[token_count-3], "text", str, NULL);
662
if(token_count < 2*gcomprisBoard->level+1) { /* No next line to update on last line */
663
gnome_canvas_item_set(calcul_line_item[token_count-2], "text", str, NULL);
664
gnome_canvas_item_set(calcul_line_item_back[token_count-2], "text", str, NULL);
667
gamewon = (result_to_find == token_result());
669
process_time_id = gtk_timeout_add (2000, (GtkFunction) process_time, NULL);
679
/* ======================================= */
680
static void destroy_board() {
683
for (i=0; i<NUM_VALUES; i++)
684
gdk_pixbuf_unref(num_pixmap[i]);
686
gdk_pixbuf_unref(oper_pixmap[i]);