From 9b3c3fcec30273042679c02177152ed22384dd39 Mon Sep 17 00:00:00 2001 From: lecointre Date: Mon, 1 Dec 2025 22:10:36 +0100 Subject: [PATCH] Fin menu --- Makefile | 6 +- obj/game_view.o | Bin 0 -> 5064 bytes obj/main.o | Bin 3304 -> 3368 bytes src/defs.h | 12 ++++ src/game_view.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++ src/game_view.h | 16 +++++ src/main.c | 32 +++------ taquin | Bin 21848 -> 22048 bytes 8 files changed, 225 insertions(+), 23 deletions(-) create mode 100644 obj/game_view.o create mode 100644 src/defs.h create mode 100644 src/game_view.c create mode 100644 src/game_view.h diff --git a/Makefile b/Makefile index 140eca7..21a9cfc 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ CFLAGS = -Wall -Wextra -std=c89 LDFLAGS = -lgraph TARGET = taquin -# Liste de tous les fichiers sources (.c) -SRCS = src/main.c src/menu_view.c src/game_logic.c src/images.c src/utils.c +# Liste de tous les fichiers sources (.c) - INCLUT TOUS LES MODULES +SRCS = src/main.c src/menu_view.c src/game_logic.c src/images.c src/utils.c src/game_view.c # Liste de tous les fichiers objets (.o) correspondants OBJS = $(patsubst src/%.c, obj/%.o, $(SRCS)) @@ -38,3 +38,5 @@ run: $(TARGET) # Nettoyage clean: @rm -rf obj $(TARGET) + +.PHONY : all clean run diff --git a/obj/game_view.o b/obj/game_view.o new file mode 100644 index 0000000000000000000000000000000000000000..413c49ef9602b5f06249ac86c35d200b8728c68e GIT binary patch literal 5064 zcmb<-^>JfjWMqH=Mg}_u1P><4z~CW(U^{@B4h(z@ybPfpoliS!|8$oA=nVbR>HEQ@ z)Afx@r|*ky*FV;-UurbFL%(#pzG1vz>H4Q$rrY(0wd;o(k?znB-L5Y{fkZB&>4jLZa4BZYIovkq-jcCF;ovjIAVb=zj6~|j6{{R0Evg~CA zBLjm6Tn9(@RFI5E@+FUhkC;3dFLX0>wx)n}gYD`rVCe?yo%#J=XKMzMga=&WXXn(M z|NsAYw}MFi^tUbh~zRSLlE!mu?RY2<@N(qPsm5z|>TbvE5riR(dqQ;RuKM+@tx3 zKzA=FJi4cX0;_u~SRvSL&|rO?h!OOVh#{cQqZ<*x1e7*|!;ldg?%e_~h64-#`a_mr zN&cR+fB*kOB|N$VBs_XuCwTPof+EeM^PETLad7g2q?lgU2|Gb)&ZG0tJ}V{$hW$ni z3=9w{NMLq5D6}3ZF~#ArolKx$ho!g4}~7Z|VA@*w>@mbp6vcV6Rw>H9Xo$?9)Gd?_y7N`2TFvyLl1xq?7Rl^ z{%hH0*8_|oF;FsaUC|9m1Fi@7dy4-5|BvdqcMJ>+2$4?L7sp+X{0C=3kVXNI?$8z8 zUMw@e|Lk;q12)nDSuFHJW9zm#Hh*Ln0 zxX|tTf$>78>yg*VjkPaA5K8_-jAr3qk5J;!SoH4NS^h5I@Mu-{mjkPa= zK?=ms6ug2cXs$i-zeKP*^aw~A5j@Scdl*XiL1N$}%-^!{FDO@lysH3;?nR)KOKxfc z8SByQAmPz@43RSs;fr9k9w^aA@H$<;G}nH~E754K{o+@m)Li?eu!OC-_Df(X7b3MH zl)v6S;pAZk28Q&a#7c&woW$g8hRW2OocwYI=ls%wVk-sJoKl9O)D#A%AjdF&1x1B` zAlKkv*C>ThKUaki|4?TSS3OWYn*GxJhY6;ks+CMYC>4AWBxC`v6Z zPOVZ<_fuCW$S*BY@XIePOU=niRS1CS%P&<>4}{7EmS&ceq!#IMF)+A0J6kDexch}_ zDj1sR8R;1+7#SFu7+4sXg6sxi76t|e1qKENQ08*?3uRzpU_1a41Id9fBa{!y^&q|o zh+tq~V5|yaV5|^el;&aQn83)uAj1H`An5=Q;m9Y@#^lV)#^lYz&H<6L0f{p(FkAL{j?c`CFUibFjW0?~E`i7urRJri7J;&RPHJgUKw^s7_S`?I;T#}fV zo|DQDl$u+RlZhlyTu_vmSCYoynp~7wlp2y+QIg6KA75OOn4BG-oRJ-$mYA6XaZpZv zegT7TW^z$}acXjYUP@{)gL8gCW@=GXeqJg=aDHh~X0dZlW?^Y+Dnon(LwqDeTW)?? zD%fufA^D}r8L6&$jwL0jc_pb(K0|6nW(h+HG=+n*E~tF?1FE6^|NmbD6$gp2F)%Q| z#6jT=l7guR!U5{O`6U_e*D6iJ*N$sAC60ZD<>b0CS|gc=AE=R^|!gCx#{B+d$Q z2m=EHhyv*W*#R<_7bJkiJ)%hB$o85biE|^F?|>wZtUew|9N9hdpyD73#NC0XMa=g4l5=VCDFQ_<(0+|CU_drJR zK+6G;7_z;JNa7&3!`x$uBn~PsVB(QD#IupaL3I#J{S+i|L8Ne4gd~n^{x&3WAtd!j zpyD8Z2_uQ0MG^3OAk1x5JRbQZwSyiV|~Ep)R3}0{I_Q#6W`wZWzq}p=c5ywQxlY3=GoHID@H&$u)oq69xtb z4p1aBFff3)AibbG3Q_}#V-T$dYI`v-FhG+pNC=FlqS*@;gsO$HL9`CY4hr-eLd^ul zCCEG&o&mKV)UJSufoPcfL2MAVgz7f{ageYnv_J;61CfN`Ej3VBfW)1k`ax`D90=8q zECyo3^nutQ9K^uD08)zX{s@pD0|NuPyU^XA1+^bte+N`QsC@>q2Zqu0gHj~0#M}yY9oOx c00}@b8kYf7Heu6$0qTBOI{~U49)B=C0It2{QUCw| literal 0 HcmV?d00001 diff --git a/obj/main.o b/obj/main.o index 945b9cba7878da5042bcef1da0429320c997d862..9e8a3b2f78c874c139c7ef91ac58c82ed6340397 100644 GIT binary patch delta 305 zcmaDMxk74!2IGp2np%vE_a}!iZf5uBb)DeRd3-VxQ_$oHrXxb~ObiSRj(h@bOwPP) z9?i_mLX-8Fs~F`suVkLj$oOjVMrQHJ39J&VY>W&H0+S1wg;~WQEKZTh6It(brYGj6 z#^>bc7fjA)v){ad?F1v^`N@?W`nI1K8Nh&rfq_Asfq~)Ae+c-4Brbs@E(&r80|Ns{ zz2xMx9KRXmC!gdr=e)rNHip4rGAEZgWBg=CE_=rK$&Fn0j0uwua@jMkoy^E>&v delta 247 zcmZ1>^+Ixj24luXO)W;o%acPGH%}I3@?~aVV3-`wbW}{3iGhK^kx!tF$(fhUqnVkR zjfsbyWAa8$xycdC5sbo{H!{y>Wc)NakX4_NXL2K}_~Z?&4<^rIv)g=)?Gz*9t;q{H z^cg=)zQ`fY%Ly`yfq{XAfq_9{GB4+EM&ZevT;`k`*ciY-W3nfgIb-nTMlO5C;K>KM z>=`2_GjiKAE}iViZO?dcawE4r +#include +#include + +/* Constantes et variables globales (définies dans d'autres modules ou par l'IUT) */ +extern int _X; +extern int _Y; +#define MARGIN 50 /* Marge autour du plateau */ + + +/* --- Fonctions Utilitaires Privées --- */ + +/* Vérifie si un clic est sur une tuile donnée */ +static int is_in_tile_rect(int x_click, int y_click, int tile_x, int tile_y, int tile_w, int tile_h) +{ + return (x_click >= tile_x && x_click <= tile_x + tile_w && + y_click >= tile_y && y_click <= tile_y + tile_h); +} + +/* --- Fonctions de Rendu et de Logique --- */ + +/* Dessine le plateau de jeu */ +void game_render(const game_state_t *state) +{ + int i, r, c; + couleur C_FOND = CouleurParNom("gray"); + couleur C_BORDURE = CouleurParNom("black"); + + EffacerEcran(C_FOND); + + /* On prend la taille des tuiles calculée lors du découpage */ + int tile_display_w = state->all_tiles[0]->width; + int tile_display_h = state->all_tiles[0]->height; + + ChoisirCouleurDessin(C_BORDURE); + + /* Boucle sur toutes les cases du plateau */ + for (i = 0; i < state->rows * state->cols; i++) { + int tile_id = state->permutation[i]; + int x_pos, y_pos; + + r = i / state->cols; + c = i % state->cols; + + /* Position d'affichage sur l'écran */ + x_pos = MARGIN + c * tile_display_w; + y_pos = MARGIN + r * tile_display_h; + + if (tile_id != -1) { /* C'est une tuile (non vide) */ + tile_t *current_tile = state->all_tiles[tile_id]; + + /* ChargerImage(fichier, x_dst, y_dst, x_src, y_src, l, h) */ + ChargerImage(current_tile->path, + x_pos, y_pos, + current_tile->src_x, current_tile->src_y, + current_tile->width, current_tile->height); + + /* Dessine la bordure */ + DessinerRectangle(x_pos, y_pos, tile_display_w, tile_display_h); + + } else { + /* Dessine la case vide en couleur distincte (jaune) */ + ChoisirCouleurDessin(CouleurParNom("yellow")); + RemplirRectangle(x_pos, y_pos, tile_display_w, tile_display_h); + ChoisirCouleurDessin(C_BORDURE); + DessinerRectangle(x_pos, y_pos, tile_display_w, tile_display_h); + } + } + + /* Affichage du compteur de coups */ + char buffer[100]; + sprintf(buffer, "Coups: %lu", state->moves); + EcrireTexte(MARGIN, WINDOW_HEIGHT - 30, buffer, 1); + + /* Affichage de la victoire */ + if (state->is_solved) { + ChoisirCouleurDessin(CouleurParNom("red")); + EcrireTexte(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2, "BRAVO ! PRESSEZ UNE TOUCHE.", 2); + } +} + + +/* Gère les entrées (clic/clavier) et la boucle principale du jeu */ +void game_loop(game_state_t *state) +{ + unsigned long next_update = Microsecondes() + REFRESH_CYCLE; + int go_on = 1; + + while (go_on && !state->is_solved) + { + /* Rendu et rafraîchissement */ + if (Microsecondes() > next_update) + { + game_render(state); + CopierZone(0, 0, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0); + next_update = Microsecondes() + REFRESH_CYCLE; + } + + /* Gestion des événements Souris */ + if (SourisCliquee()) + { + int i; + int x_click = _X; + int y_click = _Y; + int tile_w = state->all_tiles[0]->width; + int tile_h = state->all_tiles[0]->height; + + /* Parcourir toutes les tuiles pour voir laquelle a été cliquée */ + for (i = 0; i < state->rows * state->cols; i++) { + int r = i / state->cols; + int c = i % state->cols; + + int x_pos = MARGIN + c * tile_w; + int y_pos = MARGIN + r * tile_h; + + if (is_in_tile_rect(x_click, y_click, x_pos, y_pos, tile_w, tile_h)) { + /* Tenter de déplacer la tuile cliquée (indice i) */ + game_move_tile(state, i); + break; + } + } + } + + /* Gestion des événements Clavier */ + if (ToucheEnAttente()) { + int key = Touche(); + + int target_index = -1; + int empty_r = state->empty_index / state->cols; + int empty_c = state->empty_index % state->cols; + + if (key == XK_Escape) { + go_on = 0; + } + /* CORRECTION FINALE : Inverse le mapping de commande pour la perception intuitive */ + else if (key == XK_Up) { + /* Le joueur appuie HAUT (monte) -> la tuile en BAS (r+1) doit monter */ + target_index = (empty_r + 1) * state->cols + empty_c; + } else if (key == XK_Down) { + /* Le joueur appuie BAS (descend) -> la tuile en HAUT (r-1) doit descendre */ + target_index = (empty_r - 1) * state->cols + empty_c; + } + else if (key == XK_Left) { + /* Le joueur appuie GAUCHE -> la tuile à DROITE (c+1) va à gauche */ + target_index = empty_r * state->cols + (empty_c + 1); + } else if (key == XK_Right) { + /* Le joueur appuie DROITE -> la tuile à GAUCHE (c-1) va à droite */ + target_index = empty_r * state->cols + (empty_c - 1); + } + + /* Tenter le mouvement s'il y a une cible */ + if (target_index != -1) { + game_move_tile(state, target_index); + } + } + } + + /* Si la boucle est terminée par la victoire ou ESC */ + if (state->is_solved) { + game_render(state); + CopierZone(0, 0, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0); + printf("BRAVO ! Partie terminee en %lu coups. Pressez 'N' pour Nouvelle Partie ou 'Q' pour Quitter.\n", state->moves); + + /* Gère le choix de rejouer/quitter après la victoire */ + while (1) { + if (ToucheEnAttente()) { + int key = Touche(); + if (key == 'n' || key == 'N') { + return; /* Retourne au main pour relancer le menu */ + } else if (key == 'q' || key == 'Q') { + exit(EXIT_SUCCESS); /* Quitte le programme entier */ + } + } + Microsecondes(); + } + } +} diff --git a/src/game_view.h b/src/game_view.h new file mode 100644 index 0000000..8abefee --- /dev/null +++ b/src/game_view.h @@ -0,0 +1,16 @@ +/* src/game_view.h + * Définitions du module de la vue du plateau de jeu. + */ +#ifndef GAME_VIEW_H +#define GAME_VIEW_H + +#include "game_logic.h" +#include "defs.h" /* Nécessaire pour les constantes de fenêtre */ + +/* Dessine le plateau de jeu, les tuiles à leur position actuelle, et le compteur de coups. */ +void game_render(const game_state_t *state); + +/* Gère la boucle principale du jeu, les événements (clics/clavier) et les rafraîchissements. */ +void game_loop(game_state_t *state); + +#endif /* GAME_VIEW_H */ diff --git a/src/main.c b/src/main.c index bb57b5c..e3fecd4 100644 --- a/src/main.c +++ b/src/main.c @@ -4,20 +4,14 @@ */ #include "menu_view.h" -#include "game_logic.h" /* Nouveau: pour initialiser le jeu */ -#include "images.h" /* Nouveau: pour le chargement des tuiles */ -#include "utils.h" /* Nouveau: pour l'aléatoire */ +#include "game_logic.h" +#include "images.h" +#include "game_view.h" /* NOUVEAU: Pour la boucle de jeu */ +#include "defs.h" /* NOUVEAU: Pour les constantes de fenêtre */ #include #include #include -#include /* Pour srand() */ - -/* Constantes de la fenêtre */ -#define WINDOW_WIDTH 800 -#define WINDOW_HEIGHT 600 - -/* Temps de rafraîchissement (pour économiser le CPU) */ -#define REFRESH_CYCLE 50000L /* 50 ms */ +#include int main(void) { @@ -28,9 +22,7 @@ int main(void) InitialiserGraphique(); CreerFenetre(10, 10, WINDOW_WIDTH, WINDOW_HEIGHT); - /* Initialisation du générateur aléatoire (OBLIGATOIRE pour game_shuffle) */ srand((unsigned int)time(NULL)); - menu_init(&menu_state); next_update = Microsecondes() + REFRESH_CYCLE; @@ -41,7 +33,6 @@ int main(void) if (Microsecondes() > next_update) { menu_render(&menu_state); - /* Force l'affichage */ CopierZone(0, 0, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0); next_update = Microsecondes() + REFRESH_CYCLE; } @@ -54,7 +45,7 @@ int main(void) menu_handle_key(&menu_state); } - /* 3. Transition vers le jeu ou sortie */ + /* 3. Transition vers le jeu */ if (menu_state.ready_to_play == 1) { image_source_t img_src; @@ -62,22 +53,21 @@ int main(void) game_state_t game_state; int num_tiles = menu_state.rows * menu_state.cols; - /* 3.1. Préparation des données et allocation */ + /* Préparation des données et initialisation du moteur de jeu */ img_src = get_image_source(menu_state.selected_image); tiles = images_slice(img_src, menu_state.rows, menu_state.cols); - - /* Initialisation du jeu */ game_init(&game_state, menu_state.rows, menu_state.cols, tiles); - /* Mélange (Partie 3) */ + /* Mélange le plateau */ game_shuffle_safe(&game_state, 500); printf("DEBUG: Jeu prêt. Grille %d x %d melangee. Lancement de la boucle de jeu...\n", game_state.rows, game_state.cols); - /* TODO: Boucle de JEU (GRANDE PARTIE 3) */ + /* Lancement de la BOUCLE DE JEU */ + game_loop(&game_state); - /* 3.2. Nettoyage après la fin de la partie */ + /* Nettoyage final (après que le joueur a fini de jouer) */ images_free_tiles(tiles, num_tiles); game_free(&game_state); } diff --git a/taquin b/taquin index dbd973c166d07ea6a103da7bcbcb287994b987fe..ed640ef52b15bade806ea2cddc8ad79be7dfe916 100755 GIT binary patch delta 5659 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 delta 3604 zcmZ3mhVjNK#t9Na9|Re|zyVA$Fx>E+sI`pi1}}trgLmRBJ+4Ag2(M6dvJj&lmk%q1 z>%%(Pi&2l!V{#?qf5ierh#)$B1R~6EWb#a=_agl3rJPRNg{v1OKAYECyU=lMXxHk= zbC@+`l^7Tp7#SEC*g(n|87vqW7??mzkhsd^hs^4XizYX+h)-5v@tEAe;xSo)U1IVB zmII8FCNE?aXIu!DyTDq(*aK!Iuo*C}oIH_DpK$|-&)5qlizk0%)1REdA~D&5U4pT7 zb0GUP#>sCuy4jf+7(sAyBd53NQAP#^0g%lM3=CT)PGe(WU|~296%)zKHOn{2N2!IH{#9{moXyTU8koJ$8UL^nj|Nj{4 zlF1i@8W?v>4ivUx+&sBQSc7rJYa$$Rv&E|OxMG8m(#F)-Q`#?s#(1OUZ{$gNYfXMWU zc1bd%GJvH!kH6qTwt)j=fJZNH8k!BM5L0@0fLshx12GC_$*~{*|96MBbh~!++Aw)^ zyKeC4Jm}HOsyF$SxGEEuym zL36ApFAAu(fQP)`Hh1|XYGk@R(A^qhECT9-K=q&e@pN(319g7|34_5KV)EF zfJB|#W@%|}M*XBO|NnQF{^)l7vr~?NfngVj=sf=7_Ghr|--IEy>wJOOZZdhQOfX-< zm;e7eT|acYUg*qucd`M`r*BnC^Dq@Mt|ys_N0r zT51F`?%*y^HrNMEYJx%_vEwgRPF9fbVvL)-TK*B^>CJr#5=@MnCNEd=vli9^g%%5w zM{no>kLE)h9-YTLISzYB06YHDFtg42LmnNI5TFIop`Tu|B_u4<57fv2#U|{&n zFZau%`3;9h^AV22n-k6A**AaCHelXlP{3HvD9vmQs&>Q~7#KF(`~Uw30|P_Dga7{@ zFfcGwKK%b*fsuis=kfpl9*hhOhaUg`pTWq$5dQrC{|-h5hOC$W|F2+VV90s(|NjX_ z28KPa{{Mf$$iQIy`u~3pCI*JxumAtoU}9i+^ZNgP4<-hNuW$bU&tPI;`1$t#{|+Vw z2E%v%o&T?3VqoBY|Ns9PCI*IE@BjaQ!^FT4_u>D49%cpxg^&OL8!$64y!`n8e+V-J zgW~7^|4Wz|7&d?Y|9=KE1H+lm|Nrk`p8U#4O?Jlp|NqrM+0TU?T+XX7Fff>a{B-yK z{|l3Kjg_?mAN>C>3@S7n`2^aSoO#(inwgo|n0VNsb&L!H149b~1B1<@$yLU3OhJz( z&oY){JU)4+u{`6h$v2JV8Rty?X)MopX0oJ-Jd^Uv$yO$EOx-UhN14bm&Y4_kBG1(N zYVs@-Ii}3llXscOF*UxPe9J_M$^6aae7o3xS+1W9! zm~3e;&ls^e(!QOM6C@78FDCzV=$F(11rGxQLpex@fq_8*-00wASUh>5qd4Q8$vYkO zCADQClCZir8mcB{GNY5eAgt<^fGA>Un;hsUKKTF>%j6y>5yL*Ps2~HZNd)FHFfc3x zw{^rBUO+2snE3X|7oEf<1(`rDkzjZLt%G5*0h1Y>#TgGwR%8)pya*ya?|^D&20n&c zQV=yv(2x>kVqg$r;A4O_?Z9b>fq~%-RDCA2T>xrifpmVG9LOTh$O>*F34usYbw&mT zAtru?6mYzO)IhKVIn9gGB7Yqf(SF%L&YPNAp#aqm-v88V~hZk*9BDiuJgyuq@u zP}vEf<~9QZ!%2`70|P@INS3i0O!iIQ=q@ffnHdyAk_-;eu!F_Ww8;RR}Jjol(ka$Foh5M5QJ!FOX7#2cP=`Dy63|x~7J;WI$ zK%}H53&f`{p-pR8Tu-0O=(%+AA=dNO1&&c>=a$~Uk