From 99cb83582a52782b73365c0aa39e0cf7f7b521e3 Mon Sep 17 00:00:00 2001 From: lecointre Date: Sun, 30 Nov 2025 15:56:48 +0100 Subject: [PATCH] Menu v3 --- Makefile | 44 ++++++++----- obj/game.o | Bin 1656 -> 0 bytes obj/game_logic.o | Bin 0 -> 4024 bytes obj/images.o | Bin 0 -> 3048 bytes obj/main.o | Bin 0 -> 3304 bytes obj/menu_view.o | Bin 0 -> 6160 bytes obj/utils.o | Bin 0 -> 1496 bytes src/game_logic.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++ src/game_logic.h | 38 +++++++++++ src/images.c | 89 +++++++++++++++++++++++++ src/images.h | 35 ++++++++++ src/main.c | 56 ++++++++++------ src/menu_view.h | 8 ++- src/utils.c | 21 ++++++ src/utils.h | 14 ++++ taquin | Bin 0 -> 21848 bytes 16 files changed, 433 insertions(+), 37 deletions(-) delete mode 100644 obj/game.o create mode 100644 obj/game_logic.o create mode 100644 obj/images.o create mode 100644 obj/main.o create mode 100644 obj/menu_view.o create mode 100644 obj/utils.o create mode 100644 src/game_logic.c create mode 100644 src/game_logic.h create mode 100644 src/images.c create mode 100644 src/images.h create mode 100644 src/utils.c create mode 100755 taquin diff --git a/Makefile b/Makefile index 4136f75..140eca7 100644 --- a/Makefile +++ b/Makefile @@ -1,28 +1,40 @@ +# Makefile pour le projet Taquin - SAE S1.02 / S1.04 +# Variables de compilation CC = gcc CFLAGS = -Wall -Wextra -std=c89 -LIBS = -lgraph -OBJ_DIR = obj -SRC_DIR = src +LDFLAGS = -lgraph +TARGET = taquin -SRC = $(wildcard $(SRC_DIR)/*.c) -OBJ = $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) +# 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 -EXEC = taquin +# Liste de tous les fichiers objets (.o) correspondants +OBJS = $(patsubst src/%.c, obj/%.o, $(SRCS)) -all: $(EXEC) +# ---------------------------------------------------------------------- +# Cibles principales +# ---------------------------------------------------------------------- -$(EXEC): $(OBJ) - $(CC) $(CFLAGS) -o $@ $^ $(LIBS) +# Cible par défaut (crée l'exécutable) +all: dir $(TARGET) -$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c - @mkdir -p $(OBJ_DIR) +# Crée le dossier obj/ si non existant +dir: + @mkdir -p obj + +# Règle de liaison : crée l'exécutable taquin à partir de tous les objets +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +# Règle générique pour la compilation de tous les .c en .o +obj/%.o: src/%.c $(CC) $(CFLAGS) -c $< -o $@ -run: $(EXEC) - ./$(EXEC) +# Exécute le programme +run: $(TARGET) + ./$(TARGET) +# Nettoyage clean: - rm -rf $(OBJ_DIR) $(EXEC) - -.PHONY : all clean run + @rm -rf obj $(TARGET) diff --git a/obj/game.o b/obj/game.o deleted file mode 100644 index 086c44426f45105baf056a7d6004f7da93811e84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1656 zcmb<-^>JfjWMqH=Mg}_u1P><4z)-=AU^{@B4h%dD+zg=}oliYFYkzc>e(4PTFd_CZ zk^m}i2S^t}9*sL8_ArCHv$K_ghPz*=rh=h~o{^rRf{}rdiGhWIDFXuw0|SEs0|Nsq z0|SG*Unm0;1LFaZ7|2i%W`y!#ZWjRwGB7YORs}IIRtPXk^RRPFU}RvBVSr$ev>=FZ z_@1-LE`dI z3&4J3U|`S!>tSRtgo=ax#K6E{2^EK=1*l&@YSI&PQ}vP=KvaBYUS1jRNoqw2gI-EvNg{(@QgJbZUUGhJZfaf$ zgI->KNvfW^U#M25HINucXjXJB9er7?2#>p<-{K(h*FKPaCf zOTp}i(WX%S$ZA0BJfjWMqH=Mg}_u1P><4z+l0HU^{@B4h(z@ybPfpoliS!|8$oA@aPQv;L+*( zqTBV)%|8d*aD`E(;>j~JdKecEY z83}6q0tpC@<~If&owYAIOP_RxK7fSA6aMwC4?11{fI_T0^hu`xgz=y=fCa?zIPM?; z_4bS74ho>S0tcA24!lN^Q1ES7>!=v>;sj5de zD@6L>E)50-hJ9dzdZA(vt*_&byZ!(vdMyXj+8z4CBl(iY!ADFUjEA~ie{?c6yZ&J8 zcKrhmh#xiA5eomn6gIzN@nF1goZ}FtKXs$iNP$3GI1BK~{ZU+|r^@l87kJPY(6}`>`%N4MA^!ncM z=ncKm?Rup1_zS7O|3MBu(Om!ve~`Hzy}nO8dP5&{9)I!o56Dk9x`|K_%$ooDv$2a7bzavB49U{utFlLJN5^}XCBRO6ric1JM;-S zu&%+Pv-JSXOY<2R7(jBMFas%rMazY5259Q>=!V33w}SvQP4x0GVbgM`o3Rrrfvy7- z%YPt}pu`ve3b_{^-3}5Sy#b71vpNGLAT%V=fV|e}qyY9U)H|KEFF-1~!FnN~4tJpg zM|UV%O6g{3{_(#A8Yd30B&ZHD0^(Oh61>pOVCnh;rd9+sEuI6r0iMwuu%wkAh6j$j zUH~P`%x?Nfybh_SY zuD!un$IJ>q1p8cW4G%SYu6(+vq17lIr+qK2L+Jz z6F})c#X}pCn4t;d+eH8~@{G&NO^ zi-E!2+1W}#!`&}bQ^C+g&q&Wu!N|bK#K6M96l4Pn0|NudDo{pu_X}lUVqiP~5(CMB zFe8)?N~|Eh2#8={U|_5YVqmNgV3g)z=a|6Az#zi_!60cv5aGxt(8lD<%a+5#4$7q< zc^i;80|Nsm$S{~ZGYca~1xP*yD$jx}&-5Q84K}|9DsKUzVfu@~(qKQ#0f{p(Fx&#s zFnQNzW~K`eH6S|Q;2U3lJ z2MOWAATO~neEg41o*8B+k|ZAk11K)wBH%D(U|^7gio^Keu!O2&W&p(^lmn$q7#J9Y z7#JC>Kn`GFU;u|L0|SE{0|SEqR1GraibLE7hj=j5d^;wHDd4zaU|@*Ap*|L>z6`1! z99Iks4B0r;7vm5I#Sh3|0n8A4!STt!z%UtVzC1J$gVcdkEP{$JhKhsZkAZ<<3l8%c z(i3x2<8$)UGn4g_88Y)yQY+$1^5c{9^NUi78PYQIQsNU+vJ#V1^Gf0~^HMUCQ;Q*5 zGV?M^7;+Oq*^;5SBqg<|h#{@KD6=FLBAr&03S}iH=Edjcm!&c!CKbn*CFU^1#}}6* zCTGVdXJp5xC1&P8G=P-Emt^KZb!8UE7w6}crKUh+i!(~o(sEMcixbmQ8Hy6~Qu1@- zGxJIq80Ny_n}LCWoq>Vj52#T4|Ns9Us5mJ2LHQCUegR1wl!{>DH=yF6VBQc{3nvQ5R$!$ zAkRS6^COAJBZ(v13(n)!_@P?TSgT2xZWpa(W7 zH6uQ)C^0t`>O#sWP`HDV8XKB1uy9X>#urQ)q!z{o(bCX3hN%aMf$#}XqGe!Uuz+d+ zrBx6Ul=eaL;I=aZ1A`ivllD~RSRQ-XdMOy29TNL>IbDKkh?%} z2{I3cS3vF8gb6@tkX=wFn6iYr$pFkl5Er5PL3JBKlmTAjfb0Z`J3;k>*vR-TR6nv9 zhz-*RVuNrHw46bA{|Bgkba$b0|Df9qvdRHecrq|BoB%0cU|;~1MIa}k>j#;M9`_*q93Tk>28R70 d4ibhM2Nw~&egN(=hywrs literal 0 HcmV?d00001 diff --git a/obj/images.o b/obj/images.o new file mode 100644 index 0000000000000000000000000000000000000000..7daa184e298283adfe3625b9d6077bc1299798e5 GIT binary patch literal 3048 zcmb<-^>JfjWMqH=Mg}_u1P><4!0>_{!FB*M9T)@{_!&YyI-dr6G{5oi=&aq*S-K^~ zqnkxlgMop;qto?;;eq3>2i`F;FdTP1a)^{B>Y;+qZ=ZRDd*Acy2GQ}_kc%t>ID0pKy_d+d*she00kXny0+ zS$n3l^hCGo8UFRICpul99Cv*HaznSPz|8NpCpukUbi0bo{9b#e)Adca>l2R|`#8X! zJq|Ih)AfT#bL|I)Qm!2!T`%u2fINQ#%sv2SgG3l!E@5C`*l)tXzyQp1>LS6JQ%^6JUW@0T^}$uyWU~!cD-Zm zdV;?v;Q#;s&8{~XyIpTsyPo0iG5P=hzel(02Y9$Ccy!jj=q!EW(Omn2p``M->mQJx zUq^Mj{z0+hg$LsWk8TE!)&nJ0xa6Qd$0~gsWGq9o>mSB$*FV;-Pip3Yq7@ccuNfzt zJe*iuoLW+>pP8GOo@%I9keAMYEMkNqVhk1mn~R5VcXqZ?&~Wz))l@Jv(KFIBR4_6y zGBL0)Fa>L3VPIfTU|?VXrBipmPzELj#seTRup|Qm10$3V%UdE)3C5}*2F3~jMrj^) zjtPtqGi0E0Cqc9$pFkUvGcTKKGc(hE9(GV_1Ia`3AOnL1$UvAHW@AQ>3XnSzKzbP% z7$$*em^@Q2NE#%96N8LnVfgqTn*wI6B?AKkGuYRdI+z*QFa){4VSx~rfT|EEOhsU`7XiTL9D(xT*4hT@W<x2R2*IXZX|I~+Jl+H1xhFk3=AOkpg4tzJ0gj5GcYi~#AA`f zd62}bki?PQ84mIg)O=nf^<6l`q0R)SAZ8ecfq?HmD6TRg3vl3 zI~d5ufh?20w70$8dp%gpzs5gTQL2wcmeqb z-ENSUA5i-PKnfTb82A_%z_lK_eoicD52W7%n*Iwx93%`i4lW{yLw^L+{he@?5C*z_ E0F*)fssI20 literal 0 HcmV?d00001 diff --git a/obj/main.o b/obj/main.o new file mode 100644 index 0000000000000000000000000000000000000000..945b9cba7878da5042bcef1da0429320c997d862 GIT binary patch literal 3304 zcmb<-^>JfjWMqH=Mg}_u1P><4z>vX-U^{@B4h(z@ybPfpolk>38sE%dU|>k`=w?yX zU|?YI=yZKycwh%e*$WV{GlGeMVV43k1H(Qp1_p-xAObA0A0!KAbsh&(9=)y&9#A$y zzXxl;VUVe<1^@s5f7!{vz`;nB(A(HX(v(d~P|qdWG7 zN2iB?M`whDN4M_-k4_JT&g+n1YCdA%(b-x7vNg1$yR^llyBEaq=yja{vjW84_k|G@ zZji9;4xQ5NI-&EJM=vWV5Ij1MBfQu>7355hZm^LEXM)UtnTJpeW_fgnzDV)V76C_i z2`j`|-LXGT9tIiC;Nt2O>Tadrm0GG$P;_`ziJpRcQD#m~s)A~YLIsG(P0dNnOHWPJ zQ}9X5OHR#A%_~tzNma;6R7lD%P0mRLak5fN_4M?(7#Q50ovjo!-2Fl|6%0-EjPwi@ zj0}uS3@i*x!Im*FFt9K%FeorEFo3+~?ib3y#K3p}BnFdbgz{l&TLdb>SQW&;SRueD z&BM+yfsuhhg@J)V1}Z1a#K6Gd$S2UoB(U0hPo-fy`%N`1l{2G&9zm z#lXM}b~UOFkTpU`>cMVdU|`^7U|&V zVoqjpYLRd}dx|34?ED za#4P9YI1&FN@_7gyeKs~t zQJR*PlNw)~n3l>=P?VWhk_JH{}2F5w;(A{o@ZlVV1S8((k)D!9ZB39BnUMJl-FSD!;!>6 zRUS+{5lI|WeZjQq%Fq40IMHhav-%Z zHi(vnh7n9XNDPEGKn>J@1`;T(g7kv&1V|nfrXX64fdO0_f#Md#hv8tTJ{TWHgUo=j zL9`A70|UrRa`hWR?MJr`)UpJb?+YT3Fvu<>E`udhzX7r!0|UcSsDZFLA0`cRKa6&Q z>W9h0=z~!GFg}ci>4Wh>^(3hLL05hPDuC`TboXaL?T0Fd8O!hkDu5oZ==wpa5R?be z)!l$9gvB!`{z2s)RG0xK4)PDwD!APsh6OZ2VetSe(?Is1>j#+$RSMDu#}hz>I0FNN eEXV@5C?u^RaT!2m6E^)Hpca)ND~5&@vJe1>_f!x7 literal 0 HcmV?d00001 diff --git a/obj/menu_view.o b/obj/menu_view.o new file mode 100644 index 0000000000000000000000000000000000000000..306e82974799c42ab9510d2868e6e916d3e5e974 GIT binary patch literal 6160 zcmb<-^>JfjWMqH=Mg}_u1P><4z%W4s!FB*M9T@l+co{-HI-h!U*8cG5cKvZ2EDmNl zurM<)FhH0b2qq6m#e~?yU|o%GCV<9S1abk)?nC>G7#JA#g9xxIc7g&1ZrY)J zDJZhXT^m47eYuE%fuY;AA@jfq1_p-a14f;$6OOx1nFbN<4Pf->bv@wG%L`KL(Rt3J z^Y{)B6Kp|u=#=i#319`yZ#XhQdK`CxNwAuoAcZiedNdyq@aS#@gy+Mt-`%bgnqRQ)T)@D0#}32qEaHSxxft0>ZX{`MdjHC_}s`dU5b?F*%zdC9x=5A-yQEk|Dh)H8qdH+27C2(>*lEF~rl~ zPr)Tr!OJyNA;d8-)YFf_P*1@*BR{h^vq&LFJu^2kJypSqA+fkPwWL@d%vDX%E67U+ z6-5k2NNQ7y6;d*DQ}c>5^Ye-oG>j{Z6%rLJDl9Y=tQdSU)ALe`6|5LkQy84{bMo^b zJZ%PD1{YUf$DknBAO;3^XJ;z~4R^m#O$9>}JtI9s1tS9^69WqaQwA0W1_lKN1_n@h z>FyWGz{J3K03-%>1Oo#DBa{!yTOcu5Gm5b)h=H*}fKi%{=4C77VdsF#*?`0u7#OC4A{8djJb@Xc0;D$vDlZJG1mN;~OfdNxs5}$MF)(?i zzaVL_-JmiFq$B`Dz~#N+@_Rty3=9nAAQ~=T1eXU_0t^fc9Vqh6V0n-bHVkqK3&Y3% z*yNb8wnP~in8Bfqssq%XVq#!qFkpv>gWbr$z#z%M0E!ca431E77#|!53=9mu3=9kc5F=2?XdL1hIK*plhlAi}*m!wvd#3!fbm8617qRis>%)I!b z)Z`L~uFSklD61$nFD12z!8yM)C$+RFAhF0VKbOHZEiExQwa7KOC^3%#S|CAHxTF>r zXXY`uCKqKErG}(dl%z5g7ZhdYm83B^XCxM-rxtmFiY*3+y40ee)Z~)Hy!4z@hM?5k zf}Bhwf%y31lEmcf_~eZ2__V~#9Ej6067y1WQsZ;;ON&$ElXEhYvl-$e7~&&QWV2H% z8A9?)lQU9X^BhY`K;DG%8A1{>b8=FhGZHiNQXw9Ihy<4w1mx#rCZ~d;285$P1Sq~3 zK>3Y<;SZ=j|NsAg5>y;i#&I$r#B-tIprQwqWI!}Xs1!snFo4TRWcA>_5|a7QYzQek zu&AF5H3wua$b4{5l7WF?HdGwlJhkyMG{92ha9Ll$UPt@K#gT6 zfr^97K@Nu+BynW(FCd9aAi3u*k~pZX0dv0wsL=)uXXJDOYAb?*5ab>Z2C+dHGNuM< zt3WWUosj^Ngr*aadRTpoTz-PYVf8C$mJfjWMqH=Mg}_u1P><4z%YXe!FB*M9T<2Sco;%GI-hpd{%NlL!%)fI?fU2Y zjn~ZGu76?=gCsnf-v~e@>)CgJbiL+&0itGpul>_`;pAZkcV}lS1r2wI&)=OqcOe&5qOUz*? zO3X{i&yCN_D*-cMT8y9!P?&+j@Xvn;0EHVFI81zySOA;CMl8TEN^pf*)b5rw581(Y;OH%dR{X%t% zOA?c_q5Sl`QoVwr{DRb?l1c_WunDOd@o7bgxv3y$P>ezT1H}y+nn^JK*`bMp)FKN> zL(?8iJxuNaRG|b^fQx|v9Gjpt1QLRYt3kz?L4ruQ4vCG-gc=JM(qUj=0GUayenY7J z25=K044D0(`~VSwkTCmUv=vl8L>58LLNHNSFnusVF9rq%b_Pf~f{B6X1t5ZffdSoJ zAiqMTz*G{{eoz_&3qXkrP&xui!zgt9u{h%I2h^c3`$6#s(hI^MvtaQDQig6fNL~XZ z!N9;U0mNZoVBlt8U;xDtx_(eT2Z=+K!Kock{fclA2m>k%;Xum+Z2C2zj>>_ngfP(c F0{|RkcrO3| literal 0 HcmV?d00001 diff --git a/src/game_logic.c b/src/game_logic.c new file mode 100644 index 0000000..9a15f0b --- /dev/null +++ b/src/game_logic.c @@ -0,0 +1,165 @@ +/* src/game_logic.c */ + +#include "game_logic.h" +#include "utils.h" +#include +#include + +/* --- Utilitaires de positionnement --- */ + +/* Convertit un index 1D en coordonnées 2D */ +static void index_to_coords(int index, int cols, int *r, int *c) +{ + *r = index / cols; + *c = index % cols; +} + +/* Trouve les indices des tuiles adjacentes à la case vide */ +static int find_adjacent_indices(int idx, int rows, int cols, int *out_indices) +{ + int r = idx / cols; + int c = idx % cols; + int n = 0; + + if (r > 0) out_indices[n++] = (r - 1) * cols + c; /* Haut */ + if (r < rows - 1) out_indices[n++] = (r + 1) * cols + c; /* Bas */ + if (c > 0) out_indices[n++] = r * cols + (c - 1); /* Gauche */ + if (c < cols - 1) out_indices[n++] = r * cols + (c + 1); /* Droite */ + + return n; +} + + +/* --- Fonctions publiques --- */ + +/* Initialise le jeu à l'état résolu et alloue la mémoire */ +void game_init(game_state_t *state, int rows, int cols, tile_t **tiles) +{ + int num_tiles = rows * cols; + int i; + + state->rows = rows; + state->cols = cols; + state->moves = 0UL; + state->is_solved = 1; + state->all_tiles = tiles; + + /* Allocation dynamique de la permutation (malloc) */ + state->permutation = (int *)malloc(num_tiles * sizeof(int)); + + if (state->permutation == NULL) { + fprintf(stderr, "Erreur: Allocation permutation echouee.\n"); + return; + } + + /* Remplissage à l'état résolu : IDs 0, 1, 2, ... N-2 */ + for (i = 0; i < num_tiles - 1; i++) { + state->permutation[i] = i; + } + + /* La dernière case est la case vide (-1) */ + state->permutation[num_tiles - 1] = -1; + state->empty_index = num_tiles - 1; +} + +/* Libère la mémoire allouée par game_init */ +void game_free(game_state_t *state) +{ + if (state->permutation != NULL) { + free(state->permutation); + state->permutation = NULL; + } + /* Note: tiles sont libérés par images_free_tiles dans main.c */ +} + +/* Vérifie si la tuile peut bouger (adjacence) */ +int game_can_move(const game_state_t *state, int index_to_move) +{ + int r_tile, c_tile; + int r_empty, c_empty; + + if (index_to_move == state->empty_index) return 0; + if (index_to_move < 0 || index_to_move >= state->rows * state->cols) return 0; + + index_to_coords(index_to_move, state->cols, &r_tile, &c_tile); + index_to_coords(state->empty_index, state->cols, &r_empty, &c_empty); + + /* Test d'adjacence géographique (manhattan distance = 1) */ + if (abs_val(r_tile - r_empty) + abs_val(c_tile - c_empty) == 1) { + return 1; + } + return 0; +} + +/* Déplace la tuile vers la case vide si possible */ +int game_move_tile(game_state_t *state, int index_to_move) +{ + int temp_tile_id; + + if (!game_can_move(state, index_to_move)) { + return 0; + } + + /* Échange des valeurs (swap) */ + temp_tile_id = state->permutation[index_to_move]; + state->permutation[index_to_move] = state->permutation[state->empty_index]; + state->permutation[state->empty_index] = temp_tile_id; + + /* Mise à jour de la nouvelle position de la case vide */ + state->empty_index = index_to_move; + + /* Mise à jour des compteurs */ + state->moves++; + state->is_solved = game_is_solved(state); + + return 1; +} + +/* Vérifie si le plateau est dans l'état résolu */ +int game_is_solved(const game_state_t *state) +{ + int num_tiles = state->rows * state->cols; + int i; + + /* La case vide doit être en dernière position (-1) */ + if (state->permutation[num_tiles - 1] != -1) { + return 0; + } + + /* Toutes les autres tuiles doivent être dans l'ordre (ID = Position) */ + for (i = 0; i < num_tiles - 1; i++) { + if (state->permutation[i] != i) { + return 0; + } + } + + return 1; +} + +/* Mélange garanti solvable par N mouvements aléatoires */ +void game_shuffle_safe(game_state_t *state, int steps) +{ + int prev_move = -1; + int choices[4]; + int n_choices; + int pick_index; + int i; + + for (i = 0; i < steps; i++) { + n_choices = find_adjacent_indices(state->empty_index, state->rows, state->cols, choices); + + /* Choisir une tuile adjacente au hasard, sans revenir en arrière immédiatement */ + do { + pick_index = choices[random_int(n_choices)]; + } while (n_choices > 1 && pick_index == prev_move); + + /* Déplacer la tuile (utilise la logique de déplacement) */ + game_move_tile(state, pick_index); + + /* L'ancienne position vide est la tuile qui vient d'être déplacée (pour la prochaine itération) */ + prev_move = pick_index; + } + + state->moves = 0UL; + state->is_solved = 0; +} diff --git a/src/game_logic.h b/src/game_logic.h new file mode 100644 index 0000000..6071486 --- /dev/null +++ b/src/game_logic.h @@ -0,0 +1,38 @@ +/* src/game_logic.h + * Définition de l'état du plateau et des prototypes pour le moteur de jeu. + */ + +#ifndef GAME_LOGIC_H +#define GAME_LOGIC_H + +#include "images.h" /* Nécessaire pour le type tile_t */ + +/* Structure de l'état complet du Taquin */ +typedef struct { + int rows; + int cols; + + /* Tableau 1D alloué dynamiquement pour la permutation */ + int *permutation; + + int empty_index; /* Position actuelle (index 1D) de la case vide */ + unsigned long moves; /* Compteur de coups */ + int is_solved; /* 1 si gagné */ + + tile_t **all_tiles; /* Les tuiles découpées */ +} game_state_t; + + +/* Prototypes des fonctions publiques */ +void game_init(game_state_t *state, int rows, int cols, tile_t **tiles); +void game_free(game_state_t *state); + +/* Logique de jeu */ +int game_move_tile(game_state_t *state, int index_to_move); +int game_can_move(const game_state_t *state, int index_to_move); + +/* Solvabilité */ +int game_is_solved(const game_state_t *state); +void game_shuffle_safe(game_state_t *state, int steps); + +#endif /* GAME_LOGIC_H */ diff --git a/src/images.c b/src/images.c new file mode 100644 index 0000000..ce96fda --- /dev/null +++ b/src/images.c @@ -0,0 +1,89 @@ +/* src/images.c + * Gère le découpage de l'image source en tuiles (structures tile_t). + */ + +#include "images.h" +#include +#include +#include + +/* Stocke les chemins des images disponibles (doit correspondre à NUM_IMAGES dans menu_view.h) */ +static const char *IMAGE_PATHS[] = { + "assets/image1.png", + "assets/image2.png", + "assets/image3.png" +}; + +/* Retourne les informations de l'image choisie */ +image_source_t get_image_source(int selected_index) +{ + image_source_t src; + + /* TODO: Normalement, il faudrait charger l'image ici pour obtenir la VRAIE taille (width/height) + avec une fonction IUT. Pour l'instant, on simule une taille fixe. */ + src.width = 750; + src.height = 450; + + if (selected_index >= 0 && selected_index < 3) { + strcpy(src.path, IMAGE_PATHS[selected_index]); + } else { + strcpy(src.path, IMAGE_PATHS[0]); + } + return src; +} + +/* Crée un tableau de tuiles découpées (allocation dynamique de structures) */ +tile_t **images_slice(image_source_t img_src, int rows, int cols) +{ + int num_tiles = rows * cols; + int tile_w, tile_h; + tile_t **tiles; + int i, j, id = 0; + + /* Calcul des dimensions d'une tuile */ + tile_w = img_src.width / cols; + tile_h = img_src.height / rows; + + /* Allocation du tableau de pointeurs vers les structures de tuiles */ + tiles = (tile_t **) calloc(num_tiles, sizeof(tile_t *)); /* calloc pour initialiser à NULL */ + if (tiles == NULL) return NULL; + + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + /* Allocation de la structure de tuile */ + tile_t *t = (tile_t *) malloc(sizeof(tile_t)); + if (t == NULL) { + /* Gérer l'échec de malloc */ + images_free_tiles(tiles, id); + return NULL; + } + + /* Remplissage des champs (coordonnées de découpe) */ + strcpy(t->path, img_src.path); + t->width = tile_w; + t->height = tile_h; + t->src_x = j * tile_w; + t->src_y = i * tile_h; + t->id = id; + + tiles[id] = t; + id++; + } + } + return tiles; +} + +/* Libère la mémoire allouée par images_slice */ +void images_free_tiles(tile_t **tiles, int num_tiles) +{ + int i; + if (tiles == NULL) return; + + for (i = 0; i < num_tiles; i++) { + if (tiles[i] != NULL) { + free(tiles[i]); + tiles[i] = NULL; + } + } + free(tiles); +} diff --git a/src/images.h b/src/images.h new file mode 100644 index 0000000..a53ca1c --- /dev/null +++ b/src/images.h @@ -0,0 +1,35 @@ +/* src/images.h + * Définition des structures pour la gestion et le découpage de l'image. + */ + +#ifndef IMAGES_H +#define IMAGES_H + +/* Une tuile est une partie de l'image source */ +typedef struct { + char path[30]; + /* Coordonnées de la tuile DANS l'image source */ + int src_x, src_y; + int width, height; + int id; /* ID unique, de 0 à (L*C - 2) */ +} tile_t; + +/* Structure pour l'image source complète */ +typedef struct { + int width; + int height; + char path[30]; +} image_source_t; + +/* Prototypes */ + +/* Obtient les informations de l'image choisie */ +image_source_t get_image_source(int selected_index); + +/* Crée et retourne un tableau de tuiles (découpage) */ +tile_t **images_slice(image_source_t img_src, int rows, int cols); + +/* Libère la mémoire allouée par images_slice (selon la règle free/malloc) */ +void images_free_tiles(tile_t **tiles, int num_tiles); + +#endif /* IMAGES_H */ diff --git a/src/main.c b/src/main.c index 6341921..bb57b5c 100644 --- a/src/main.c +++ b/src/main.c @@ -4,11 +4,15 @@ */ #include "menu_view.h" -#include /* La bibliothèque IUT */ -#include /* Pour EXIT_SUCCESS */ -#include /* Pour printf */ +#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 +#include +#include +#include /* Pour srand() */ -/* Constantes de la fenêtre (doivent être disponibles partout) */ +/* Constantes de la fenêtre */ #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 @@ -24,44 +28,60 @@ 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; - /* 2. Boucle du Menu (tant que le joueur n'a pas validé ou quitté) */ + /* 2. Boucle du Menu */ while (menu_state.ready_to_play == 0) { - /* Gestion du temps et Rendu (dessin) */ if (Microsecondes() > next_update) { menu_render(&menu_state); - - /* FORCER L'AFFICHAGE : Copier l'écran 0 vers l'écran 0 - (Technique souvent nécessaire avec XLib pour forcer le rafraîchissement) */ + /* Force l'affichage */ CopierZone(0, 0, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0); - next_update = Microsecondes() + REFRESH_CYCLE; } - /* Gestion des événements Souris */ if (SourisCliquee()) { - /* _X et _Y sont mis à jour par SourisCliquee() */ menu_handle_mouse_click(&menu_state); } - /* Gestion des événements Clavier */ menu_handle_key(&menu_state); } - /* 3. Sortie */ + /* 3. Transition vers le jeu ou sortie */ if (menu_state.ready_to_play == 1) { - printf("Configuration valide. Image %d choisie, grille %d x %d. Lancement du jeu...\n", - menu_state.selected_image + 1, menu_state.rows, menu_state.cols); - /* TODO: Appeler game_init, game_shuffle et la boucle de jeu (GRANDE PARTIE 2) */ - } + image_source_t img_src; + tile_t **tiles; + game_state_t game_state; + int num_tiles = menu_state.rows * menu_state.cols; + + /* 3.1. Préparation des données et allocation */ + 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) */ + 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) */ + + /* 3.2. Nettoyage après la fin de la partie */ + images_free_tiles(tiles, num_tiles); + game_free(&game_state); + } + FermerGraphique(); return EXIT_SUCCESS; } diff --git a/src/menu_view.h b/src/menu_view.h index 8f5f114..3d34f75 100644 --- a/src/menu_view.h +++ b/src/menu_view.h @@ -1,4 +1,6 @@ -/* src/menu_view.h */ +/* src/menu_view.h + * Définition de l'état du menu et des prototypes pour le module de configuration. + */ #ifndef MENU_VIEW_H #define MENU_VIEW_H @@ -12,10 +14,10 @@ typedef struct { int selected_image; /* Index de l'image (0, 1, ou 2) */ int rows; /* Nombre de lignes (3 à 8) */ int cols; /* Nombre de colonnes (3 à 8) */ - int ready_to_play; /* 1 si le joueur a validé */ + int ready_to_play; /* 1 si le joueur a validé, -1 si demande de quitter */ } menu_state_t; -/* Prototypes des fonctions */ +/* Prototypes des fonctions publiques */ void menu_init(menu_state_t *state); void menu_render(const menu_state_t *state); void menu_handle_mouse_click(menu_state_t *state); diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..1d250c3 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,21 @@ +/* src/utils.c */ + +#include "utils.h" +#include + +/* Renvoie la valeur absolue */ +int abs_val(int n) +{ + if (n < 0) { + return -n; + } + return n; +} + +/* Renvoie un entier aléatoire entre 0 et max-1 */ +int random_int(int max) +{ + if (max <= 0) return 0; + /* rand() est initialisé dans main.c */ + return rand() % max; +} diff --git a/src/utils.h b/src/utils.h index e69de29..43913d1 100644 --- a/src/utils.h +++ b/src/utils.h @@ -0,0 +1,14 @@ +/* src/utils.h + * Déclarations des fonctions utilitaires simples. + */ + +#ifndef UTILS_H +#define UTILS_H + +/* Renvoie la valeur absolue d'un entier */ +int abs_val(int n); + +/* Renvoie un entier aléatoire entre 0 et max-1 */ +int random_int(int max); + +#endif /* UTILS_H */ diff --git a/taquin b/taquin new file mode 100755 index 0000000000000000000000000000000000000000..dbd973c166d07ea6a103da7bcbcb287994b987fe GIT binary patch literal 21848 zcmb<-^>JfjWMqH=W(GS35buK^M8p9?G2HNlG8h;b92hJZco`fR3BTn1*SJ_!(mfq?;?mVv5+(I7X0gg`XNJ`fulzQGF-h0zQG5I#sB zD~Ji@!^B~9p(sd_fdNLt#6kMN_8CCr8PI4Su%QeLFdA7OC~OYcLi8Pgh98Xffckp_ zl!oa81s6!)1*pCYP<=4^1IR%P3=A+D7M>tCg0KbDJaifsA21qSUjO6l!gW`gMLnCl9`EqPKs_$W?pH9ZiR)Ju9=BmalW1r*m{t=KuG}< z{qBCD3``9S2S8#l{frEZD10U;AFLUg_C**N!1(~ApMSlS(`mbK^`gXQ^IB^cI<5`v zS`9J}|KdNeF_fs8*r%a#UWmdLtGArcsdSoX+{PHK?X?%g+NHm!17)zR6GGH4lCbu zpyCctahQ6B`1thP{Ji+$lEk8t_;`kR&k*1El+>cs^vvRt)S?hy=bZe!)R4raoK%P$ zRvFjiqRgVykkpEjREChm%$%H5=ZwV6yj0iZqQpF?NN{OEKz>eUaw>ysT3TXqY7s=z zIU_%_IJ3w(zceScw8$m3xHvP9!8yMmGqorxKQEObB)>E{Bh@v}v7{t5uOyYhEwv~& zwaC3Fu^=O}ur!q+IKQ+gv)DNY#7SicO3f|E$t(&=O)g2yOV3GVa4t$sEpkiEOD!o% zW$?_)EXhpF$t*@T0Agh-k{*ba4Dk^RzM08I`NgTp`FSa+#SHP03{YnVBo_JQ=Q22F zBo?Ko7J23-rl&F#mlP!zR5HYaf-*TfJ~<;hJ}ogbhas&fHI<MVF3VPIf5fh5icQ356}Ac=#@T(BSm1H%m@acGkjEcF0MTpla} zAz=9!RBt1fx3K&Q5(m|p5Y-F}u>1`YSB43KXsCbLp>;1v2!uJ1#KDy?NRWYnK>$e{ zRA)nl86=RzL3Y5z6_CV1VFVM`KoSSp4HGv&5{HE~NX`ODoD(Df#STc~Tu?C(<$)y5 z4HAIj03>l9s2GTfKoW;Gy+OhZ2}t7nU=awBfg}!1YG8>1BynM|2!yCW5(l-HAfgNm z4M^h9raM@w14&#AECL}WAc>1ZguvtsBykC_2!vRGBrXXN0+TC{#HGL@5Ml$8xHLov zOzuDuhxKJZ5(kjPWkCW^d;&>a4k`wsE+B~`w;ygGi7SE>j^fb}ARz=k^UMA6Xnw=t z(apNio`J!m^*{;J{|g??M>r0HP5Ezn%ASGYzv>x#1_pk42ZsNuAbti&?&X93|NsA2 zJ!H?okO8V?US0t6cY*kzqVMGaFn<$>4=VCrZUFOFf%u>z|K$QOe-VfeD*9ed0P|;o z_@E;1WdoQ$3B(5#buSCR{4Nk5RK&eZ0P~wbd{EK$G62l40`Wmb+Div8zX-$!6=g3C z!2B!_A5?_BQ~>jnKzvZq^-=)Lj{@;QRnJQXFh2;y2Nhv2Kl}sv*9*i4RY5NwfcZ`! zKB($>c>&C~0`Wmb+sgxBz7dEID$-tV0Q0p#d{9yLasimH1mc5=u$L3Sd?^qgRCK*; z0P}@Fd{B|~vH;BI0`Wmb)yo7hp9#bV6;Uq(!2Dl!3=A2dqUogrnEwgH2Ng*#4gUTA z4~lQYx1OCB<2*Vac{D%y5D?;Oc);+aN3ZQI1_p-zqNi;c7`~+G@yoY>;+Vms*Y=?u z1A|8|>n#vv`J+VCqnowQ7Lrsr|3BcDZvZJg45dAKZ8w4x_L{B&QJVieIzM-Y^25|g;kj5|X!oUF24{~3F?O8hp28L4OP>*I?BhWyIN9WTL zUXN~DGdqwI6>J$8UL^nj|Nj{45<3P4#@NFM_a06|utDh+WZuj4|NsAodUQSw_Go-F zgMon|#iN@=RfBAq`uqR?&Il$3hFuEG3=I1~DQG{4coF&U|Ns49 zCF}qF|KEB1g~q@C|2=wL8$3FXzu3XZzyQ(T1QKNpI1Dni6_iw8b~3=^(m-;SVA0|L znAXjV3=CNeSqzR~TOD_RtlbH+XBUXr2O=QWuK4r+zen>C0f;?PAhVF|fd~u#{r|u9 zKnV}b^j9pPSc5q7BO3!lw`))90sfYp|NsAYyY@8yVB~MT%)r3V9oo|E+QGm6P-iPx zDzw3)*Y!Z>HIUy5_!th#&A>q;O`@o~qL!tBfi(h;U3?9u#3_LnpD?qk}c667vcy#xII3B&O6F^ox z;sm>L!oDwzpm00>!ipE_7Gd;S&MnarJ>upl@7^Z!3sn@4x(ixdxS5paZ;u)gpDg=Tl`kCTT%Ny4ME_J>Ee>yP80;AQ}` z99Tf56@jJ1OsGkjyt@`!<|8ZAvCVsgH zlp2R(XO^}wba+E>HK!0^J41(cIO zY8Wr{R%jqpaUrYHfT`+1G3ki}$iq;RSm0{VOgez9>K-!#DF5sLMPt~H|NnP_Vq+I5 zA%QdZe$dFsi?iQBMJMY$agg!HU&#FcrRn1@)PDT`@3<3`fPH#db0ioTuxkFy%)l@K zlrK{}v?1kp3G0jMAOHVDvvTtr2anF$6Wy%t77PrXt`E9duq;&t|xa%KK>h1RZ(;fPwyYxx7>jPMN28nkbe=(Pd zfuZ$437xu5rJDmbA+Zh=cnqBWOHrL)^s$1V3`lj3U1LL9Y+85ovFQ7(r{&eg- zu@e+QoyT89LrhQuo8Z9G{DYmp-x5?1f=qN^0cE&u*AtxqEH9NoX&GYPh28)S+(vSs z8_CSy_Yzc8!Hfju8L*L%(*I4j>lb`Z1l4b?2TGt$Ewt>nHkQ2W! z;&);yBRJgTA-?8l{=r-q0A=?EK%K|&+7=pM;FRFF1C)+-f+~q!pmfxE{DnNk6mgKw z;|@HaO#Yf{!fW;kuURI%W}E;|5(*xjwJ*R$(2EOSK~*Gw%R_j*EcfmI|4!FGjkSNI z|1&U@3V3w8zBul{18VoZ+|9ti&{+E?2qXdW{)=vC41o$skiu?Q47~uwP!B|T2uL}| zYY^qkb>(oSAg_UxzD{YZ{S%C&4iu{O{t$K05Pi`Z!13CqvGz|OsygO6b+|gHc^t1r z8f*XXg4A(?HDJF^^djdmm1CP&@bJtZx}CF zy8fw`>302L?fRicq&xIOx9ba#;E#HCQ0z0jW(5ZywB~DmqX03}qcik_N2l)#ke#4B zpY-Mb|L)Qs-L8Ljf->7K5Yc)3#qH1F9RE!ilH+y0KythZhz8jO*5L>)q&+(?plAiv zy-;s>G`}(EtbNm2`l2)RNvH1vpH9~sE}gztx>VJi1*!bP7PZpaAIqr` z_2_0TH3B8SgS$YD{(WGBdRYa9Kw`&Vto-!<|LeHpu0KFQ{8|pCwLA2ONAe|)gO8Xz z7!QFG0#mc=55{iSKj47)QF9%k@DEI3^D7n)#tX;69mj6hKak?=MQ4Bjyf_koH8?%G z!LBQj0aa*VPUrC#aUfrGyZ-Pv?f~lHGC)&Zut)P73sBY8S$YJyvH8OA0I2xub`Y?1 zJyOaA&ZaNBK~1;XBMcRyU^!5jp6GU9;a`8q()CCUJ6O@{Ot4%5i$|~T9gp778{Mu) zI*-4Q`U`GSo#-wAh5w0e2LX>>-zOfup$|HbzxewH)MmQT9r{4?Lg(=p&sZ23IuCcd z-sui~0v5Q+!oa|IqxlD8DN3<{-uyoO0b~cf`Q7{mTtIi0KIsm90t!(`R%ZJTDym^# znh&XPUVwrLqzo1<7rGgssmG%m66YW%&j6>1ULH`x2t&)EZpKci1iB7TEdPN>f)Zl@ zDC9u;B|IRt94KK)fSd?QZ=eAJkk>k$6u^ZfG=e*8!R28$ST7{h;VyLG=nh3oDcuas zKmM0M{sB`ff|?f3f!zSlXy9fLT3Y#Gc;L7zc!cJ->lJWH zj{~Oxa8^6fZ2;zgBODajj=w=g=?zfPdi;e5sAV^!+oknEr|X^O+8c~@9Nn&WtX(gZ z-0u#(1JVeIH0^(&6mqB2^+L1j6~=DYE7q<@YG#4tk#h2h;|}2QON@rxrgyN094NVa zcy!k8=q%lWmfVlK9(czDYTX|>1Zr#5Zege}Zm!+JRIl6Zx&>TN9tHW$qnA}!57ZoG zVe;q=J>b!Nh{L1vm`CUJ7cuYt|9>sz(ari<7u0J+k#l$lN}8@a2zGHn;BF2`gyH291_p-xCJYP= zFLu8H6@;!oJU}%{sdRVf190U8?$Cq!QQ-Xlatb6z{qX1wmGFSXo)gHvZcxSeqB8*0 zUHH)*`ld5Lr`z=o|9aOK;7aaCXMjSt>ka<(u5Vy`6;SyFF0&1~T^}GtLAUD%4@OXd z{KKP@soC`bBdBEzPB$m`dq6{V&8{~XyIpTsyPo0iF@bdEK0vb(qEzr`u6@B!QhD6< z56I82qacNTuLvZXyIo&+FkbNJ29H}>;gah-{(|)lC~1P~3pA~eet)y;A5h6|?fRr< z9w=Ht;eYA%|NroYWM}OkP_1F0M|Y z?p6w3sig`9MTb|F=qb1tfz}r)sHP}XfQa1GoW#8J)KonMpTxZ6)ZEm(5`~mhg`7l% zr2Nw4oKz4eE45TlPY*O{%21vGT0U2ln!=EjlbD>%kdj!Gt&m=nSjmuHl$x5y;Oy_` z=II_9K!Ey^t|fv{7PGxAGQ zQ}wt|++zrK4zdf4Fhq>OA`Fbu%+{c_?Vt($4fp>42MszkJox_~)G4fd`2Rm>l&k0Q z|NkD03=D@J|No!C$iNW({Qv(B&}8w;|NmDoGBD)4`v3m~BLl;pSO5RNU}Ru0e*OPH zXxxAI>;L~Xm>3w|y#D{+gNcFR>zn`oGng0{e!l(xzk`W^!SLPx|0|dn7`Wg6|9^&w zf#KHs|Nq}GF)+k^`2U}WnSnv!|2sf~vJ4Cipal$|2w=?sAR>274)MEn^k%ay*c_7zPH0zz6^T3xg(v9Qg#= zn4EdpJerxA*_e3PLEU2p1_qEFpcM)>kN*ER1K9&pR|ru8k_UB)gC70=KNaL=m^||Y zW{?VyJSZNHKSi^LnGb9UNd5_E?(zBm|Dd(a3^4sne?i*8_OmcDFwA-J|37#M7F^yN zF0aDKz;Ncp|NrI4?qe!~%eyc#FetzL|Gxu8z8Nl`0-E%B`Tsw7$t2AD9I!Ol{uYq^ zFaQ7N1cfI|o|%OaqypsrC7>yiSO5Qm+=+1ie~>g-{}Dz8hRoOh|ARIRz~zg<(%?1f zptYloumArCEewInyEZd3U4Wss%GLStJm>3v(-~In@ zff5$R;IIIhv4M$!Vd?w-|0f}v!PE;e0GghzFflL)fB63&w3r6&))0sikR4x`7#Mgy z{Qs|tY(@e^9wao1M?+vV1V%$(Gz3ONU^E0qLx3J30Ncki0kq;6X)nrlP&){T5868f zau0YFBZvWRl`}Aa7CwWxDd0v80|U72!@$4*+b;xKI1G}9HoF<5KpfCM9q7IxP`d~u z1e5>z?|(jse*o0xW?*0dE$jyIGe9f285qF(#X78*%b@f&D18h{UxU)mp!7E=%?4T_&%nSS2Bp=Yv>B9kgVJG8It@yfLFqOq zJq=1PgVNie^f4%X4N5M9Kpva<-ca@0&`m|)z9RzzLo`%81}YBjGcqtR6oU7nW4gZz zTy{t>!1nvW@&jmLWX2a;eAgzxhQa~T*IK>M#58N}iF7$&|QY_BK-dU_RP z0_~fYV0Zuv0H}rlChYsuL6hUqA`z+*LS4kMzZf))F2umca0^<%f&2WR@&{}_9|P<> z4Dgr+0|UbwusM7Tnb6IJ;65}11H(6HTtaL^kgPcN7YpGKR|knR@iV~6Wtd62AaMph zhKZno5(WkaaDSG8fx#Xs9tqu~2<`_nFfjOl#bLS;bOa9ZY#id1IK=yKh|j|zz8Z%( zXaOLopN?=Lgmo3XpIU$ccJ2mf;}A#&G&X@Pe!nt;(g{cfD&}Vb?VZI;KQhqxJq}Ia z;4vo#1_l!*?ECTk!Rk@X2Jw<{h!-<4FbFb9FmOQk!GhF+a6MEURxW|WK)45oIg`QS zd$hBZ+2Hqd$j=$F)%QAgT-OG5p*nAoR47wc+`=B0X(+E zz`&3P7DuRqv8r*1_c4Rgl_Wy}TKrCChMXG!+gA)rx6`2Fuzkg#dIDs}O0fBS3`Wof z4|vRyfq`KwSRAAR6(7YRu8l+d2H2gL>F_=d^?$(Pd<+Yrd|75{xn43uN>O5Yd{Svi zNq!!LU6NW+5}yn@rT{d-kXam`nHOJ_nq0z=o|u~&pOc@SnXH%0keQc~S`lB8AD^6` zUzAeJkd~R35}%lol?c)qpP84EnVee80G?1N)=Osa^mTN1jSp}P@d#!pEy>IQiN(je z`-R54dc?a0Ir_TBd$b#xumbMEW`UdOBk{7y)s_ z0@zO(iFqkGsqxvVl@Qm0k1aqu90KLM1FVNBfX*$*gt|Q*WJG*PW)9pp@x_qyACOLq zfSifo06Vt;bVLFA84Fm>hk%^WfH)KauT6EJ=i%;Q&b!(D2L6FD*`uPtM6q&PF@Q0mWqKsSYS&=qFpm$A|bj$GiHu zfR5#ePbtoihu8r<5(5%VnR%JeFTI%)Aol(HqW4hjGM5FvQ0v z6&EAB9c)NqQgM7)Vh+Xu7H~J3Kx{0|C{0VtNsTW~OoJVI0apTwKJY0PC?eq0pIIDV zoS##cn!#AmRM0^gk6{Y4Rf;2!`1vw=Qdf+J60~J^d zdTE(?nGAYG`QVVyOU;N+D*}~Q87V~!a2`ZQd}0x3suZjP#!jisOU%toX3$H|F98#J zU<)9IB^4Jl=q2apf~I8=Whn(LPIf1@}22BCzom7!4b5f!UAl|LIWuP7v)3u<;uh4IA$P z=?9JRpzDY9FA?KS@Pl)p;}M|r4K*4}p{F0v9yd^!g3JdgP=M-#(cn33kOUM%rQy~r z2lrzU_QU#*FuD+QA^=n`h=S<{u|aq(ntoV65Jtns?LcZl7~TDwq55I*2kSq<=zNgp zKpLSK7Jg7J18DafNC`|ou5e1~7wWpEwTYz?6GB7Z}XwaSlm|hT#Za-*uB502V zG~2?*BVp(Gp{s}SVf1|l1_scf4KV$%bB<1cjuU_l=7B7RVwgS{{Tez}3DXZ7kAR)a z2+Mz<^baxurVkd6piNyM|HJgd&O16_4^qv*0NRKIVL?ckei;1|8h{qJ2#2L5uyTZ7zVsv03rh+ zc^M&R62i(!h$sUCOh0=51GyEX2APJXJ&-sIYe4OXm4l$+GLYM0{)UyGpy5}T+hKY^ m^aKwO$-tn8mSAD_LS#XGH_&NXFdl;5=L2Djp=m(lG5`R?lcxCq literal 0 HcmV?d00001