/* jeu-nim-g.c : le jeu de NIM * * Edouard.Thiel@lif.univ-mrs.fr - 23/03/2010 - version 0.9 * * Compilation sous Unix : * gcc -Wall jeu-nim-g.c ez-gtk.c -o jeu-nim-g `pkg-config gtk+-2.0 --cflags --libs` * * This program is free software under the terms of the * GNU Lesser General Public License (LGPL) version 2.1. */ #include "ez-gtk.h" /*----------------------------- G L O B A L E S -----------------------------*/ /* Nombre min et max de lignes, nombre max de colonnes */ #define MIN_J 3 #define MAX_J 5 #define MAX_I 15 /* Position du premier crayon */ #define CRA_X0 50 #define CRA_Y0 60 /* Largeur, hauteur de la pointe, hauteur totale d'un crayon */ #define CRA_W 10 #define CRA_P 14 #define CRA_H 50 /* Ecart entre deux crayons */ #define CRA_DX 16 #define CRA_DY 10 /* Taille de la fene^tre */ #define WIN_W (CRA_X0+(CRA_W+CRA_DX)*MAX_I+CRA_DX+20) #define WIN_H (CRA_Y0+(CRA_H+CRA_DY)*MAX_J+30) /* Tableau des lignes et nombre de lignes */ int cra_tab[MAX_J]; int cra_nli = 0; /* Se'lection courante */ int cur_i = -1, cur_j = -1; /* Etat du jeu */ enum { E_DEB, E_H1, E_H2, E_O1, E_O2, E_HG, E_OG } etat = E_DEB; /* Widgets, timers */ GtkWidget *win1, *area1; int timer1_id = -1; /*-------------------------- C O N V E R S I O N S --------------------------*/ /* * Conversion entre coordonne'es e'cran x,y et coordonne'es grille i,j */ int cra_itox (int i) { return CRA_X0 + (CRA_W + CRA_DX) * i; } int cra_jtoy (int j) { return CRA_Y0 + (CRA_H + CRA_DY) * j; } int cra_xtoi (int x) { int k = x - CRA_X0 + CRA_DX/2; if (k < 0) return -1; k = k / (CRA_W + CRA_DX); if (k >= MAX_I) return -1; return k; } int cra_ytoj (int y) { int k = y - CRA_Y0 + CRA_DY/2; if (k < 0) return -1; k = k / (CRA_H + CRA_DY); if (k >= MAX_J) return -1; return k; } /*------------------ G E S T I O N D E S C R A Y O N S ------------------*/ void init_crayons () { int i; cra_nli = MIN_J + ezg_random (MAX_J-MIN_J+1); for (i = 0; i < cra_nli; i++) cra_tab[i] = 1 + ezg_random (MAX_I); } int supprimer_crayons (int i, int j) { if (j < 0 || j >= cra_nli || i < 0 || i >= cra_tab[j]) return -1; cra_tab[j] = i; return 0; } void trouver_crayon (int mx, int my) { cur_i = cra_xtoi (mx); cur_j = cra_ytoj (my); if (cur_j >= cra_nli) cur_j = -1; } /*---------------------------- S T R A T E G I E ----------------------------*/ /* References : * * [1] Delahaye JP, Strategies magiques au pays de Nim. * Pour La Science, mars 2009, n. 307, p 88-93. * * [2] http://en.wikipedia.org/wiki/Nim */ int fin_jeu () { int j, k = 0; for (j = 0; j < cra_nli; j++) k += cra_tab[j]; return k > 0 ? 0 : 1; } int nim_addition (int a, int b) { int i, j, k = 0; for (i = 0, j = 1; a >= j || b >= j; i++, j *= 2) k += (a & j) ^ (b & j); return k; } int nim_somme () { int i, k = 0; for (i = 0; i < cra_nli; i++) k = nim_addition (k, cra_tab[i]); return k; } void jouer_ordi () { int i, j, k, ns; ns = nim_somme (); /* printf ("\n(+) totale = %d\n", ns); */ if (ns != 0) { /* Configuration gagnante, l'ordi ne peut plus perdre */ for (j = 0; j < cra_nli; j++) { i = nim_addition (cra_tab[j], ns); /* printf ("%2d (+) %2d = %2d : %s\n", cra_tab[j], ns, i, i <= cra_tab[j] ? "decroissant ok" : "croissant"); */ if (i <= cra_tab[j]) break; } } else { /* On enleve un crayon en attendant un faux pas du joueur */ for (j = 0, k = 1; k < cra_nli; k++) if (cra_tab[k] > cra_tab[j]) j = k; i = cra_tab[j] - 1; } cur_i = i; cur_j = j; } /*------------------------------ D E S S I N S ------------------------------*/ void dessiner_crayon_xy (GtkWidget *widget, GdkGC *gc, int x, int y, Ezg_color color) { ezg_set_color (gc, color); gdk_draw_rectangle (widget->window, gc, TRUE, x, y+CRA_P, CRA_W+1, CRA_H-CRA_P+1); ezg_set_color (gc, EZG_GREY); gdk_draw_rectangle (widget->window, gc, FALSE, x, y+CRA_P, CRA_W, CRA_H-CRA_P); ezg_set_color (gc, EZG_BLACK); gdk_draw_line (widget->window, gc, x+CRA_W/2, y, x, y+CRA_P); gdk_draw_line (widget->window, gc, x+CRA_W/2, y, x+CRA_W, y+CRA_P); ezg_set_color (gc, EZG_RED); gdk_draw_line (widget->window, gc, x+CRA_W/2, y, x+CRA_W/2, y+CRA_P/2); } void dessiner_crayon_ij (GtkWidget *widget, GdkGC *gc, int i, int j, Ezg_color color) { dessiner_crayon_xy (widget, gc, cra_itox (i), cra_jtoy (j), color); } void dessiner_selection (GtkWidget *widget, GdkGC *gc, PangoLayout *layout, int i, int j, int color) { int xa = cra_itox (i) - CRA_DX/2, ya = cra_jtoy (j) - CRA_DY/2, xb = WIN_W - 5, yb = cra_jtoy (j) + CRA_H + CRA_DY/2, xc = WIN_W - 10, yc = cra_jtoy (j) + CRA_H/2; ezg_set_color (gc, color); ezg_set_nfont (layout, 2); gdk_draw_rectangle (widget->window, gc, FALSE, xa, ya, xb-xa, yb-ya); if (j >= 0 && j < cra_nli) { if (i < 0) i = 0; else if (i > cra_tab[j]) i = cra_tab[j]; ezg_draw_text (widget, gc, layout, EZG_MR, xc, yc, "%d", i); } } void area1_redessiner (GtkWidget *widget) { int j, i; Ezg_color c; char *s; /* On recupere le contexte graphique associe' au widget */ GdkGC *gc = ezg_get_gc (widget); /* On cree un layout pour dessiner du texte */ PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL); /* On dessine le fond en blanc */ ezg_area_clear (widget); switch (etat) { case E_DEB : s = "\n** Jeu de Nim **"; break; case E_H1 : s = "A vous de jouer : cliquez sur un crayon"; break; case E_H2 : s = ""; break; case E_O1 : s = "L'ordinateur joue ..."; break; case E_O2 : s = ""; break; case E_HG : s = "Bravo vous avez gagne !!"; break; case E_OG : s = "Desole vous avez perdu !!"; break; default : s = ""; } ezg_set_color (gc, EZG_BLUE); ezg_set_nfont (layout, 2); ezg_draw_text (widget, gc, layout, EZG_TC, WIN_W/2, 10, s); if (etat == E_DEB) { ezg_set_color (gc, EZG_MAGENTA); ezg_set_nfont (layout, 1); ezg_draw_text (widget, gc, layout, EZG_TC, WIN_W/2, WIN_H*2/7, "Vous jouez contre l'ordinateur.\n\n" "Chacun joue a tour de role, et prend dans\n" "une ligne autant de crayons qu'il veut.\n\n" "Celui qui prend le dernier crayon a gagne.\n\n" "Tapez 'n' pour commencer ..."); ezg_set_color (gc, EZG_RED); ezg_set_nfont (layout, 0); ezg_draw_text (widget, gc, layout, EZG_TC, WIN_W/2, WIN_H*4/5, "Le programme jeu-nim-g.c fait partie de EZ-Draw-GTK :\n" "http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ez-draw-gtk"); } else if (etat == E_H1 || etat == E_H2 || etat == E_O1 || etat == E_O2) { ezg_set_color (gc, EZG_GREY); for (j = 0; j < cra_nli; j++) ezg_draw_text (widget, gc, layout, EZG_ML, 10, cra_jtoy (j) + CRA_H/2, "%2d", cra_tab[j]); for (j = 0; j < cra_nli; j++) for (i = 0; i < cra_tab[j]; i++) { c = EZG_YELLOW; if (j == cur_j && i >= cur_i && cur_i > -1) c = EZG_WHITE; dessiner_crayon_ij (widget, gc, i, j, c); } } if (cur_j != -1 && cur_i != -1) { if (etat == E_H1 || etat == E_H2) dessiner_selection (widget, gc, layout, cur_i, cur_j, EZG_GREEN); else if (etat == E_O1 || etat == E_O2) dessiner_selection (widget, gc, layout, cur_i, cur_j, EZG_BLUE); } ezg_set_color (gc, EZG_BLACK); ezg_set_nfont (layout, 0); ezg_draw_text (widget, gc, layout, EZG_BL, 10, WIN_H-8, "r : regles n : nouvelle partie q : quitter"); /* Libere la memoire */ g_object_unref (G_OBJECT (layout)); } /*--------------------------- E V E N E M E N T S ---------------------------*/ gint area1_onExpose (GtkWidget *widget, GdkEventExpose *ev) { area1_redessiner (widget); return TRUE; /* L'evenement a ete traite' */ } gboolean area1_onTimeout (gpointer data) { if (etat == E_O1) { etat = E_O2; supprimer_crayons (cur_i, cur_j); ezg_area_expose (area1); timer1_id = g_timeout_add (300, area1_onTimeout, NULL); } else if (etat == E_O2) { if (fin_jeu ()) etat = E_OG; else etat = E_H1; cur_i = cur_j = -1; ezg_area_expose (area1); } return FALSE; /* Supprime le timer */ } gint area1_onMotionNotify (GtkWidget *widget, GdkEventMotion *ev) { if (etat == E_H1) { trouver_crayon (ev->x, ev->y); ezg_area_expose (widget); } return TRUE; /* L'evenement a ete traite' */ } gint area1_onButtonPress (GtkWidget *widget, GdkEventButton *ev) { if (etat == E_H1) { if (supprimer_crayons (cur_i, cur_j) == 0) { etat = E_H2; ezg_area_expose (widget); } } return TRUE; /* L'evenement a ete traite' */ } gint area1_onButtonRelease (GtkWidget *widget, GdkEventButton *ev) { if (etat == E_H2) { if (fin_jeu ()) etat = E_HG; else { etat = E_O1; jouer_ordi (); } ezg_area_expose (widget); if (timer1_id != -1) g_source_remove (timer1_id); timer1_id = g_timeout_add (500, area1_onTimeout, NULL); } return TRUE; /* L'evenement a ete traite' */ } gint area1_onKeyPress (GtkWidget *widget, GdkEventKey *ev) { switch (ev->keyval) { case GDK_q : gtk_main_quit (); break; case GDK_r : etat = E_DEB; ezg_area_expose (widget); if (timer1_id != -1) { g_source_remove (timer1_id); timer1_id = -1; } break; case GDK_n : etat = E_H1; init_crayons (); cur_i = cur_j = -1; ezg_area_expose (widget); if (timer1_id != -1) { g_source_remove (timer1_id); timer1_id = -1; } break; } return TRUE; /* L'evenement a ete traite' */ } /*------------------ P R O G R A M M E P R I N C I P A L ------------------*/ int main (int argc, char *argv[]) { /* Initialise gtk et analyse la ligne de commande */ gtk_init (&argc, &argv); init_crayons (); /* Cree une fenetre, donne un titre et une taille */ win1 = ezg_window_create (WIN_W, WIN_H, "Jeu de NIM"); /* La destruction de la fenetre fera sortir de gtk_main */ g_signal_connect (G_OBJECT (win1), "destroy", gtk_main_quit, NULL); /* On cree une zone de dessin, qui prend toute la place dans la fenetre */ area1 = ezg_area_create (win1); /* On autorise certains evenements */ GTK_WIDGET_SET_FLAGS (area1, GTK_CAN_FOCUS); gtk_widget_set_events (area1, GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK); /* On attache des callbacks pour gerer certains evenements */ g_signal_connect (G_OBJECT (area1), "expose_event" , G_CALLBACK (area1_onExpose) , NULL); g_signal_connect (G_OBJECT (area1), "key_press_event" , G_CALLBACK (area1_onKeyPress) , NULL); g_signal_connect (G_OBJECT (area1), "button_press_event" , G_CALLBACK (area1_onButtonPress) , NULL); g_signal_connect (G_OBJECT (area1), "button_release_event", G_CALLBACK (area1_onButtonRelease), NULL); g_signal_connect (G_OBJECT (area1), "motion_notify_event" , G_CALLBACK (area1_onMotionNotify) , NULL); /* Rend visibles la fenetre et son contenu */ gtk_widget_show_all (win1); /* Boucle d'evenements qui fait "vivre" la fenetre */ gtk_main (); exit (0); }