/* jeu-sudoku.c : un jeu de sudoku * * Edouard.Thiel@lif.univ-mrs.fr - 21/06/2011 - version 1.2 * * Compilation sous Unix : * gcc -Wall jeu-sudoku.c ez-draw.c -o jeu-sudoku -lX11 -lXext -L/usr/X11R6/lib * Compilation sous Windows : * gcc -Wall jeu-sudoku.c ez-draw.c -o jeu-sudoku.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" /* Ce programme permet d'afficher une grille de sudoku et de jouer. La partie resolution automatique de la grille est a` comple'ter. */ /*------------------------- D E F I N I T I O N S ---------------------------*/ typedef struct { int enonce[81]; /* Acce`s [y*9+x] */ int essai[81]; } Grille; #define TAILLE_CASE 36 #define HAUT_TITRE 25 #define HAUT_LEGEN 10 void win1_event (Ez_event *ev); void win2_event (Ez_event *ev); typedef struct { Ez_window win1; /* Fenetre principale */ Ez_window win2; /* Fenetre d'aide */ } Vue; enum { E_ENONCE, E_JEU, E_GAGNE }; /* Les etats possibles du jeu */ typedef struct { int etat; /* Etat du jeu */ int clic; /* Numero case courante 0..80 ou -1 aucune */ } Controleur; typedef struct { Grille grille; Vue vue; Controleur controleur; } Jeu; /*------------------------------- M O D E L E --------------------------------*/ void grille_init (Grille *grille) { int k; for (k = 0; k < 81; k++) grille->enonce[k] = grille->essai[k] = 0; } void grille_convertir_arg (Grille *grille, char *s) { int i; for (i = 0; i < 81 && s[i] != 0; i++) if (s[i] >= '0' && s[i] <= '9') grille->enonce[i] = grille->essai[i] = s[i] - '0'; } void grille_recopier_enonce (Grille *grille) { int k; for (k = 0; k < 81; k++) grille->essai[k] = grille->enonce[k]; } int grille_valeur_est_possible (Grille *grille, int k, int val) { int i = k/9, j = k%9, t, x, y; for (t = 0; t < 9; t++) if (t*9+j != k && grille->essai[t*9+j] == val) return 0; for (t = 0; t < 9; t++) if (i*9+t != k && grille->essai[i*9+t] == val) return 0; i = (i/3)*3; j = (j/3)*3; for (y = i; y < i+3; y++) for (x = j; x < j+3; x++) if (y*9+x != k && grille->essai[y*9+x] == val) return 0; return 1; } int grille_est_resolue (Grille *grille) { int k; for (k = 0; k < 81; k++) { if (grille->essai[k] == 0) return 0; if (! grille_valeur_est_possible (grille, k, grille->essai[k])) return 0; } return 1; } void grille_resoudre (Grille *grille) { /* Ecrire ici votre algorithme de resolution */ /* Supprimer le code temporaire ci-dessous */ int k; for (k = 0; k < 81; k++) { if (grille->enonce[k] == 0 && grille->essai[k] == 0) grille->essai[k] = 9; } printf ("L'algorithme de re'solution n'est pas encore imple'mente' !!\n"); } /*----------------------------------- V U E ---------------------------------*/ void vue_init (Vue *vue, Jeu *jeu) { vue->win1 = ez_window_create ( TAILLE_CASE*10, TAILLE_CASE*10 + HAUT_TITRE + HAUT_LEGEN, "Sudoku", win1_event); ez_window_dbuf (vue->win1, 1); ez_set_data (vue->win1, jeu); vue->win2 = ez_window_create (400, 300, "Aide", win2_event); ez_window_show (vue->win2, 0); ez_set_data (vue->win2, jeu); ez_auto_quit (0); } int vue_case_est_cliquee (int x, int y) { int a = HAUT_TITRE, b = TAILLE_CASE/2, c = TAILLE_CASE, i, j, k; if (x < b || x >= b+9*c || y < a+b || y >= a+b+9*c) return -1; i = (y-a-b) / c; j = (x-b) / c; k = i*9+j; if (k < 0 || k >= 81) { printf ("Erreur interne dans vue_case_est_cliquee\n"); return -1; } return k; } void vue_afficher_plateau (Ez_window win, Grille *grille, Controleur *ct) { int i, j, k, w, a = HAUT_TITRE, b = TAILLE_CASE/2, c = TAILLE_CASE, d = HAUT_LEGEN; /* Affiche le titre */ ez_set_color (ez_magenta); ez_set_nfont (3); ez_draw_text (win, EZ_MC, c*10/2, a-5, ct->etat == E_GAGNE ? "B R A V O !!" : "S u d o k u"); /* Affiche les chiffres */ for (i = 0; i < 9; i++) for (j = 0; j < 9; j++) { ez_set_nfont (3); k = i*9+j; if (grille->enonce[k] != 0) { /* Enonce du jeu */ ez_set_color (ez_blue); ez_draw_text (win, EZ_MC, b+j*c+b, a+b+i*c+b, "%d", grille->enonce[k]); } else if (grille->essai[k] != 0) { /* Cases jouees */ if (k == ct->clic && ! grille_valeur_est_possible (grille, ct->clic, grille->essai[ct->clic])) ez_set_color (ez_red); else ez_set_color (ez_black); ez_draw_text (win, EZ_MC, b+j*c+b, a+b+i*c+b, "%d", grille->essai[k]); } else if (k == ct->clic && ct->etat == E_JEU) { /* Possibilite's pour cette case */ ez_set_nfont (0); ez_set_color (ez_black); for (w = 1 ; w <= 9; w++) if (grille_valeur_est_possible (grille, ct->clic, w)) ez_draw_text (win, EZ_MC, b+j*c+b + ((w-1)%3)*11 - 11, a+b+i*c+b + ((w-1)/3)*11 - 11, "%d", w); } } /* Dessine la grille */ for (i = 0; i <= 9; i++) { ez_set_color (i % 3 == 0 ? ez_blue : ez_grey); ez_draw_line (win, b+i*c, a+b, b+i*c, a+b+9*c); ez_draw_line (win, b, a+b+i*c, b+9*c, a+b+i*c); } /* Dessine case cliquee */ if (ct->clic >= 0) { ez_set_color (ez_magenta); i = ct->clic / 9; j = ct->clic % 9; ez_draw_rectangle (win, b+j*c+1, a+b+i*c+1, b+j*c+c-1, a+b+i*c+c-1); } /* Affiche legende en bas */ ez_set_color (ez_black); ez_set_nfont (0); ez_draw_text (win, EZ_BL, b, a+c*10+d-4, "Tapez 'h' pour voir l'aide"); ez_draw_text (win, EZ_BR, b+9*c, a+c*10+d-4, ct->etat == E_ENONCE ? "mode enonce" : "mode jeu"); } /*---------------------------- C O N T R O L E U R ---------------------------*/ void controleur_init (Controleur *ct) { ct->etat = E_ENONCE; ct->clic = -1; } void clic_affecter_valeur (Ez_window win, Grille *grille, Controleur *ct, int val) { if (ct->clic < 0) return; if (ct->etat == E_ENONCE) { grille->enonce[ct->clic] = grille->essai[ct->clic] = val; } else if (grille->enonce[ct->clic] == 0) { grille->essai[ct->clic] = val; ct->etat = E_JEU; if (grille_est_resolue (grille)) ct->etat = E_GAGNE; } ez_send_expose (win); } void clic_deplacer_curseur (Ez_window win, Controleur *ct, int val) { ct->clic = (ct->clic < 0) ? 0 : (ct->clic + val + 81) % 81; ez_send_expose (win); } void clic_deduire_valeur (Ez_window win, Grille *grille, Controleur *ct) { if (ct->clic != -1 && ct->etat == E_JEU && grille->essai[ct->clic] == 0) { int w, n = 0, k = -1; for (w = 1 ; w <= 9; w++) if (grille_valeur_est_possible (grille, ct->clic, w)) { k = w; n++; } if (n == 1) { grille->essai[ct->clic] = k; if (grille_est_resolue (grille)) ct->etat = E_GAGNE; } } clic_deplacer_curseur (win, ct, 1); } /*---------- C O N T R O L E U R E V E N E M E N T S W I N 1 ----------*/ void win1_onExpose (Ez_event *ev) { Jeu *jeu = ez_get_data (ev->win); vue_afficher_plateau (ev->win, &jeu->grille, &jeu->controleur); } void win1_onMotionNotify (Ez_event *ev) { (void) ev; /* Parametre inutilise' */ } void win1_onButtonPress (Ez_event *ev) { Jeu *jeu = ez_get_data (ev->win); jeu->controleur.clic = vue_case_est_cliquee (ev->mx, ev->my); ez_send_expose (ev->win); } void win1_onButtonRelease (Ez_event *ev) { (void) ev; /* Parametre inutilise' */ } void win1_onKeyPress (Ez_event *ev) { Jeu *jeu = ez_get_data (ev->win); Grille *grille = &jeu->grille; Vue *vue = &jeu->vue; Controleur *ct = &jeu->controleur; switch (ev->key_sym) { case XK_e : ct->etat = E_ENONCE ; ez_send_expose (ev->win); break; case XK_j : ct->etat = E_JEU; ez_send_expose (ev->win); break; case XK_Up : clic_deplacer_curseur (ev->win, ct, -9); break; case XK_Down : clic_deplacer_curseur (ev->win, ct, 9); break; case XK_Left : clic_deplacer_curseur (ev->win, ct, -1); break; case XK_Right : clic_deplacer_curseur (ev->win, ct, 1); break; case XK_Return : case XK_KP_Enter: clic_deduire_valeur (ev->win, grille, ct); break; case XK_r: if (ct->etat == E_JEU) { grille_resoudre (grille); if (grille_est_resolue (grille)) ct->etat = E_GAGNE; ez_send_expose (ev->win); } break; case XK_Delete : clic_affecter_valeur (ev->win, grille, ct, 0); break; case XK_v : if (ct->etat == E_ENONCE) grille_init (grille); else { ct->etat = E_JEU ; grille_recopier_enonce (grille); } ez_send_expose (ev->win); break; case XK_q : ez_quit (); break; case XK_h : ez_window_show (vue->win2, 1); break; default : if (ev->key_string[0] >= '1' && ev->key_string[0] <= '9') clic_affecter_valeur (ev->win, grille, ct, ev->key_string[0] - '0'); } } void win1_onWindowClose (Ez_event *ev) { (void) ev; /* Parametre inutilise' */ ez_quit (); } 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 WindowClose : win1_onWindowClose (ev); break; } } /*---------- C O N T R O L E U R E V E N E M E N T S W I N 2 ----------*/ void win2_onExpose (Ez_event *ev) { ez_set_color (ez_black); ez_draw_text (ev->win, EZ_TL, 10, 10, "h : aide Suppr : vider une case\n" "e : mode enonce v : vider la grille\n" "j : mode jeu Entree : magique !\n" "q : quitter r : resoudre la grille\n" "\n" "Pour modifier une case, cliquer dessus, puis tapez\n" "un chiffre entre 1 et 9.\n" "\n" "S'il n'y a qu'une seule possibilite, Entree la valide.\n" "\n" "Vous pouvez utiliser les fleches pour vous deplacer.\n" "\n" "\n" "Le programme jeu-sudoku.c fait partie de EZ-Draw :\n" "http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ez-draw" ); } void win2_event (Ez_event *ev) /* Appele'e a chaque evenement sur win2 */ { switch (ev->type) { case Expose : win2_onExpose (ev); break; case ButtonPress : case KeyPress : case WindowClose : ez_window_show (ev->win, 0); break; } } /*------------------ P R O G R A M M E P R I N C I P A L ------------------*/ int main (int argc, char *argv[]) { Jeu jeu; printf ("\nJeu de Sudoku - Edouard Thiel - 20/06/2011\n\n"); if (ez_init() < 0) exit(1); grille_init (&jeu.grille); vue_init (&jeu.vue, &jeu); controleur_init (&jeu.controleur); if (argc == 2) { grille_convertir_arg (&jeu.grille, argv[1]); jeu.controleur.etat = E_JEU; } else printf ("Usage : %s [grille]\nExemples :\n" "%s 720000040300090060000080003007109680042000900800306005270000010004600290003710006\n" "%s 907100000405320001000070200000090003008507400500010000006030000100046907000001506\n" "%s 480000006001006050062500400000004000600098001000100800007039210050000603300000049\n", argv[0], argv[0], argv[0], argv[0]); ez_main_loop (); exit(0); }