/* jeu-nim.c : le jeu de NIM * * Edouard.Thiel@lif.univ-mrs.fr - 03/07/2011 - version 1.2 * * Compilation sous Unix : * gcc -Wall jeu-nim.c ez-draw.c -o jeu-nim -lX11 -lXext -L/usr/X11R6/lib * Compilation sous Windows : * gcc -Wall jeu-nim.c ez-draw.c -o jeu-nim.exe -lgdi32 * * This program is free software under the terms of the * GNU Lesser General Public License (LGPL) version 2.1. */ #include "ez-draw.h" /*------------------------- D E F I N I T I O N S ---------------------------*/ /* Nombre min et max de lignes, nombre max de colonnes */ #define MIN_I 3 #define MAX_I 5 #define MAX_J 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_L 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 WIN1_L (CRA_X0+(CRA_L+CRA_DX)*MAX_J+CRA_DX+20) #define WIN1_H (CRA_Y0+(CRA_H+CRA_DY)*MAX_I+30) typedef struct { int tac[MAX_I]; /* tableau de crayons : nb par ligne */ int nli; /* nb de lignes pour cette partie */ int cur_i, cur_j; /* position courante */ enum { E_DEB, E_H1, E_H2, E_O1, E_O2, E_HG, E_OG } etat; } App; typedef struct { App *app; Ez_window win1; int win1_use_dbuf; } Gui; /*------------------------------- M O D E L E -------------------------------*/ void init_crayons (App *app) { int i; app->nli = MIN_I + ez_random (MAX_I-MIN_I+1); for (i = 0; i < app->nli; i++) app->tac[i] = 1 + ez_random (MAX_J); } int supprimer_crayons (App *app, int i, int j) { if (i < 0 || i >= app->nli || j < 0 || j >= app->tac[i]) return -1; app->tac[i] = j; return 0; } /* Strategie - 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 jeu_est_fini (App *app) { int i, k = 0; for (i = 0; i < app->nli; i++) k += app->tac[i]; 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 (App *app) { int i, k = 0; for (i = 0; i < app->nli; i++) k = nim_addition (k, app->tac[i]); return k; } void calculer_coup_ordi (App *app) { int i, j = 0, k, ns; ns = nim_somme (app); /* printf ("\n(+) totale = %d\n", ns); */ if (ns != 0) { /* Configuration gagnante, l'ordi ne peut plus perdre */ for (i = 0; i < app->nli; i++) { j = nim_addition (app->tac[i], ns); /* printf ("%2d (+) %2d = %2d : %s\n", tac[i], ns, j, j <= tac[i] ? "decroissant ok" : "croissant"); */ if (j <= app->tac[i]) break; } } else { /* On enleve un crayon en attendant un faux pas du joueur */ for (i = 0, k = 1; k < app->nli; k++) if (app->tac[k] > app->tac[i]) i = k; j = app->tac[i] - 1; } app->cur_i = i; app->cur_j = j; } /*---------------------------------- V U E ---------------------------------*/ /* Coordonnees pixels x,y --> coordonnees grille j,i */ int cra_jtox (int j) { return CRA_X0 + (CRA_L + CRA_DX) * j; } int cra_itoy (int i) { return CRA_Y0 + (CRA_H + CRA_DY) * i; } /* Coordonnes grille j,i --> coordonnees pixels x,y */ int cra_xtoj (int x) { int k = x - CRA_X0 + CRA_DX/2; if (k < 0) return -1; k = k / (CRA_L + CRA_DX); if (k >= MAX_J) return -1; return k; } int cra_ytoi (int y) { int k = y - CRA_Y0 + CRA_DY/2; if (k < 0) return -1; k = k / (CRA_H + CRA_DY); if (k >= MAX_I) return -1; return k; } void trouver_crayon (App *app, int mx, int my) { app->cur_j = cra_xtoj (mx); app->cur_i = cra_ytoi (my); if (app->cur_i >= app->nli) app->cur_i = -1; } void dessiner_crayon_xy (Ez_window win, int x, int y, Ez_uint32 color) { ez_set_color (color); ez_fill_rectangle (win, x, y+CRA_P, x+CRA_L, y+CRA_H); ez_set_color (ez_grey); ez_draw_rectangle (win, x, y+CRA_P, x+CRA_L, y+CRA_H); ez_set_color (ez_black); ez_draw_line (win, x+CRA_L/2, y, x, y+CRA_P); ez_draw_line (win, x+CRA_L/2, y, x+CRA_L, y+CRA_P); ez_set_color (ez_red); ez_draw_line (win, x+CRA_L/2, y, x+CRA_L/2, y+CRA_P/2); } void dessiner_crayon_ij (Ez_window win, int i, int j, Ez_uint32 color) { dessiner_crayon_xy (win, cra_jtox (j), cra_itoy (i), color); } void dessiner_selection (Ez_window win, App *app, int color) { int i = app->cur_i, j = app->cur_j, xa = cra_jtox (j) - CRA_DX/2, ya = cra_itoy (i) - CRA_DY/2, xb = WIN1_L - 5, yb = cra_itoy (i) + CRA_H + CRA_DY/2, xc = WIN1_L - 10, yc = cra_itoy (i) + CRA_H/2; ez_set_color (color); ez_set_nfont (2); ez_draw_rectangle (win, xa, ya, xb, yb); if (i >= 0 && i < app->nli) { if (j < 0) j = 0; else if (j > app->tac[i]) j = app->tac[i]; ez_draw_text (win, EZ_MR, xc, yc, "%2d", j); } } /*-------------------- E V E N E M E N T S W I N 1 ----------------------*/ void win1_onExpose (Ez_event *ev) { Gui *gui = ez_get_data (ev->win); App *app = gui->app; int i, j; Ez_uint32 c; char *s; switch (app->etat) { case E_DEB : s = "\n** Jeu de Nim **"; break; case E_H1 : s = "A vous de jouer : prenez des crayons"; 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 = ""; } ez_set_color (ez_blue); ez_set_nfont (2); ez_draw_text (ev->win, EZ_TC, WIN1_L/2, 10, s); if (app->etat == E_DEB) { ez_set_color (ez_magenta); ez_set_nfont (1); ez_draw_text (ev->win, EZ_TC, WIN1_L/2, WIN1_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 ..."); ez_set_color (ez_red); ez_set_nfont (0); ez_draw_text (ev->win, EZ_TC, WIN1_L/2, WIN1_H*4/5, "Le programme jeu-nim.c fait partie de EZ-Draw :\n" "http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ez-draw"); } else if (app->etat == E_H1 || app->etat == E_H2 || app->etat == E_O1 || app->etat == E_O2) { ez_set_color (ez_grey); for (i = 0; i < app->nli; i++) ez_draw_text (ev->win, EZ_ML, 10, cra_itoy (i) + CRA_H/2, "%2d", app->tac[i]); for (i = 0; i < app->nli; i++) for (j = 0; j < app->tac[i]; j++) { c = ez_yellow; if (i == app->cur_i && j >= app->cur_j && app->cur_j > -1) c = ez_white; dessiner_crayon_ij (ev->win, i, j, c); } } if (app->cur_i != -1 && app->cur_j != -1) { if (app->etat == E_H1 || app->etat == E_H2) dessiner_selection (ev->win, app, ez_green); else if (app->etat == E_O1 || app->etat == E_O2) dessiner_selection (ev->win, app, ez_blue); } ez_set_color (ez_black); ez_set_nfont (0); ez_draw_text (ev->win, EZ_BL, 10, WIN1_H-8, "r : regles n : nouvelle partie d : double buffer (est %s) q : quitter", gui->win1_use_dbuf ? "ON" : "OFF"); } void win1_onMotionNotify (Ez_event *ev) { Gui *gui = ez_get_data (ev->win); App *app = gui->app; if (app->etat == E_H1) { trouver_crayon (app, ev->mx, ev->my); ez_send_expose (ev->win); } } void win1_onButtonPress (Ez_event *ev) { Gui *gui = ez_get_data (ev->win); App *app = gui->app; if (app->etat == E_H1) { if (supprimer_crayons (app, app->cur_i, app->cur_j) == 0) { app->etat = E_H2; ez_send_expose (ev->win); } } } void win1_onButtonRelease (Ez_event *ev) { Gui *gui = ez_get_data (ev->win); App *app = gui->app; if (app->etat == E_H2) { if (jeu_est_fini (app)) app->etat = E_HG; else { app->etat = E_O1; calculer_coup_ordi (app); } ez_send_expose (ev->win); ez_start_timer (ev->win, 500); } } void win1_onKeyPress (Ez_event *ev) { Gui *gui = ez_get_data (ev->win); App *app = gui->app; switch (ev->key_sym) { case XK_q : ez_quit (); break; case XK_d : /* Active ou desactive l'affichage double-buffer */ gui->win1_use_dbuf = ! gui->win1_use_dbuf; ez_window_dbuf (ev->win, gui->win1_use_dbuf); ez_send_expose (ev->win); break; case XK_r : app->etat = E_DEB; ez_send_expose (ev->win); ez_start_timer (ev->win, -1); break; case XK_n : app->etat = E_H1; init_crayons (app); trouver_crayon (app, ev->mx, ev->my); ez_send_expose (ev->win); ez_start_timer (ev->win, -1); break; } } void win1_onTimerNotify (Ez_event *ev) { Gui *gui = ez_get_data (ev->win); App *app = gui->app; if (app->etat == E_O1) { app->etat = E_O2; supprimer_crayons (app, app->cur_i, app->cur_j); ez_send_expose (ev->win); ez_start_timer (ev->win, 300); } else if (app->etat == E_O2) { if (jeu_est_fini (app)) app->etat = E_OG; else { app->etat = E_H1; trouver_crayon (app, ev->mx, ev->my); } ez_send_expose (ev->win); } } void win1_event (Ez_event *ev) /* Appele'e a chaque evenement sur win1 */ { switch (ev->type) { case Expose : win1_onExpose (ev); break; case MotionNotify : win1_onMotionNotify (ev); break; case ButtonPress : win1_onButtonPress (ev); break; case ButtonRelease : win1_onButtonRelease (ev); break; case KeyPress : win1_onKeyPress (ev); break; case TimerNotify : win1_onTimerNotify (ev); break; } } /*------------------------ I N I T G E N E R A L E ------------------------*/ void app_init (App *app) { init_crayons (app); app->etat = E_DEB; app->cur_i = app->cur_j = -1; } void gui_init (Gui *gui, App *app) { gui->app = app; gui->win1 = ez_window_create (WIN1_L, WIN1_H, "Jeu de NIM", win1_event); gui->win1_use_dbuf = 1; ez_window_dbuf (gui->win1, gui->win1_use_dbuf); ez_set_data (gui->win1, gui); } /*------------------ P R O G R A M M E P R I N C I P A L ------------------*/ int main () { App app; Gui gui; if (ez_init() < 0) exit(1); app_init (&app); gui_init (&gui, &app); ez_main_loop (); exit(0); }