diff --git a/Makefile b/Makefile index baa435e..8e2713c 100644 --- a/Makefile +++ b/Makefile @@ -1,29 +1,45 @@ +# Makefile pour le projet Taquin - SAE S1.02 / S1.04 + +# Variables de compilation CC = gcc CFLAGS = -Wall -Wextra -std=c89 LDFLAGS = -lgraph TARGET = taquin +# 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 +# (Transforme src/*.c en obj/*.o) OBJS = $(patsubst src/%.c, obj/%.o, $(SRCS)) +# ---------------------------------------------------------------------- +# Cibles principales +# ---------------------------------------------------------------------- - +# Cible par défaut (crée l'exécutable) all: dir $(TARGET) +# 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 $@ +# Exécute le programme run: $(TARGET) ./$(TARGET) +# Nettoyage clean: - @rm -rf obj $(TARGET) + rm -f $(EXEC) $(OBJS) + rmdir $(OBJ_DIR) 2>/dev/null || true -.PHONY : all clean run +# Déclare les cibles qui ne correspondent pas à des fichiers +.PHONY: all clean run diff --git a/assets/capy.jpg b/assets/capy.jpg new file mode 100644 index 0000000..ec2f109 Binary files /dev/null and b/assets/capy.jpg differ diff --git a/assets/iut.jpg b/assets/iut.jpg new file mode 100644 index 0000000..edb74cc Binary files /dev/null and b/assets/iut.jpg differ diff --git a/obj/game_logic.o b/obj/game_logic.o index 793d94c..bee3c21 100644 Binary files a/obj/game_logic.o and b/obj/game_logic.o differ diff --git a/obj/game_view.o b/obj/game_view.o index 4a44974..df1ea39 100644 Binary files a/obj/game_view.o and b/obj/game_view.o differ diff --git a/obj/images.o b/obj/images.o index 07c14b4..f106011 100644 Binary files a/obj/images.o and b/obj/images.o differ diff --git a/obj/main.o b/obj/main.o index 42ad70b..49e5cd0 100644 Binary files a/obj/main.o and b/obj/main.o differ diff --git a/obj/menu_view.o b/obj/menu_view.o index 065391f..4b85cc7 100644 Binary files a/obj/menu_view.o and b/obj/menu_view.o differ diff --git a/src/defs.h b/src/defs.h index b9ff645..88c9024 100644 --- a/src/defs.h +++ b/src/defs.h @@ -1,8 +1,15 @@ +/* src/defs.h + * Définitions des constantes globales (taille de fenêtre, cycles). + * Inclus par main.c, game_view.h, etc. + */ #ifndef DEFS_H #define DEFS_H -#define WINDOW_WIDTH 800 -#define WINDOW_HEIGHT 600 +/* Dimensions de la fenêtre finale pour éviter le chevauchement */ +#define WINDOW_WIDTH 1000 +#define WINDOW_HEIGHT 700 + +/* Cycle de rafraîchissement pour la boucle de jeu (50 ms) */ #define REFRESH_CYCLE 50000L -#endif +#endif /* DEFS_H */ diff --git a/src/game_logic.c b/src/game_logic.c index a240655..ab24072 100644 --- a/src/game_logic.c +++ b/src/game_logic.c @@ -1,31 +1,40 @@ +/* src/game_logic.c + * Implémentation du moteur de jeu (corrigé pour la liaison). + */ + #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; - if (r < rows - 1) out_indices[n++] = (r + 1) * cols + c; - if (c > 0) out_indices[n++] = r * cols + (c - 1); - if (c < cols - 1) out_indices[n++] = r * cols + (c + 1); + 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; @@ -36,6 +45,7 @@ void game_init(game_state_t *state, int rows, int cols, tile_t **tiles) state->moves = 0UL; state->all_tiles = tiles; + /* Allocation dynamique de la permutation */ state->permutation = (int *)malloc(num_tiles * sizeof(int)); if (state->permutation == NULL) { @@ -43,9 +53,11 @@ void game_init(game_state_t *state, int rows, int cols, tile_t **tiles) return; } + /* Règle SAE: La case vide (-1) est à l'index 0 (Haut à Gauche) */ state->permutation[0] = -1; state->empty_index = 0; + /* Remplissage des autres tuiles (IDs 0, 1, 2, ... N-2) */ for (i = 1; i < num_tiles; i++) { state->permutation[i] = i - 1; } @@ -53,6 +65,7 @@ void game_init(game_state_t *state, int rows, int cols, tile_t **tiles) state->is_solved = 1; } +/* Libère la mémoire allouée par game_init */ void game_free(game_state_t *state) { if (state->permutation != NULL) { @@ -61,6 +74,7 @@ void game_free(game_state_t *state) } } +/* 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; @@ -72,12 +86,14 @@ int game_can_move(const game_state_t *state, int index_to_move) 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; @@ -86,28 +102,35 @@ int game_move_tile(game_state_t *state, int 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; + /* 1. La case vide doit être en PREMIÈRE position (index 0) */ if (state->permutation[0] != -1) { return 0; } + /* 2. Les tuiles restantes (index 1 à N-1) doivent être en ordre [0, 1, 2, ...] */ for (i = 1; i < num_tiles; i++) { + /* La tuile à la position 'i' doit avoir l'ID 'i - 1' */ if (state->permutation[i] != i - 1) { return 0; } @@ -116,6 +139,7 @@ int game_is_solved(const game_state_t *state) return 1; } +/* Mélange garanti solvable par N mouvements aléatoires (étapes) */ void game_shuffle_safe(game_state_t *state, int steps) { int prev_move = -1; @@ -127,14 +151,32 @@ void game_shuffle_safe(game_state_t *state, int steps) 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; } + + /* --- PARTIE CRITIQUE: FORCER LA CASE VIDE EN HAUT À GAUCHE (INDEX 0) --- */ + + if (state->empty_index != 0) { + int current_empty_idx = state->empty_index; + + /* 1. Swap des IDs entre l'index 0 et l'index vide actuel */ + int temp_tile_id = state->permutation[0]; + state->permutation[0] = state->permutation[current_empty_idx]; + state->permutation[current_empty_idx] = temp_tile_id; + + /* 2. Mettre à jour la nouvelle position de la case vide */ + state->empty_index = 0; + } + /* ---------------------------------------------------------------------- */ state->moves = 0UL; state->is_solved = 0; diff --git a/src/game_logic.h b/src/game_logic.h index ed0d7e6..6071486 100644 --- a/src/game_logic.h +++ b/src/game_logic.h @@ -1,29 +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" +#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; - unsigned long moves; - int is_solved; + 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; + 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 +#endif /* GAME_LOGIC_H */ diff --git a/src/game_view.c b/src/game_view.c index f88c808..bb6790b 100644 --- a/src/game_view.c +++ b/src/game_view.c @@ -1,3 +1,7 @@ +/* src/game_view.c + * Implémentation du rendu graphique du plateau et de la boucle d'interaction. + */ + #include "game_view.h" #include #include @@ -5,17 +9,21 @@ extern int _X; extern int _Y; -#define MARGIN 50 +#define MARGIN 50 /* Marge autour du plateau */ +/* --- Fonctions Utilitaires Privées --- */ +/* Vérifie si un clic est sur une zone rectangulaire 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; @@ -24,11 +32,13 @@ void game_render(const game_state_t *state) 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); + /* 1. Affichage du Plateau (Tuiles et Case Vide) */ for (i = 0; i < state->rows * state->cols; i++) { int tile_id = state->permutation[i]; int x_pos, y_pos; @@ -36,12 +46,14 @@ void game_render(const game_state_t *state) 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) { + 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, @@ -50,6 +62,7 @@ void game_render(const game_state_t *state) DessinerRectangle(x_pos, y_pos, tile_display_w, tile_display_h); } else { + /* Dessine la case vide en jaune */ ChoisirCouleurDessin(CouleurParNom("yellow")); RemplirRectangle(x_pos, y_pos, tile_display_w, tile_display_h); ChoisirCouleurDessin(C_BORDURE); @@ -57,6 +70,7 @@ void game_render(const game_state_t *state) } } + /* 2. Affichage de l'Image Modèle (en haut à droite) */ if (state->all_tiles != NULL && state->all_tiles[0] != NULL) { tile_t *sample_tile = state->all_tiles[0]; @@ -68,16 +82,19 @@ void game_render(const game_state_t *state) RemplirRectangle(model_x - 5, model_y - 5, model_size + 10, model_size + 10); ChoisirCouleurDessin(CouleurParNom("black")); + /* Affiche l'image entière (source 0,0) avec une taille réduite */ ChargerImage(sample_tile->path, model_x, model_y, 0, 0, - sample_tile->width * state->cols, sample_tile->height * state->rows); + model_size, model_size); } + /* 3. Affichage du Compteur de Coups */ char buffer[100]; sprintf(buffer, "Coups: %lu", state->moves); EcrireTexte(MARGIN, WINDOW_HEIGHT - 30, buffer, 1); + /* 4. Bouton Retour Menu */ int btn_x = WINDOW_WIDTH - 200; int btn_y = WINDOW_HEIGHT - 60; @@ -86,6 +103,7 @@ void game_render(const game_state_t *state) ChoisirCouleurDessin(CouleurParNom("black")); EcrireTexte(btn_x + 10, btn_y + 25, "MENU (R)", 1); + /* 5. Affichage de la victoire */ if (state->is_solved) { ChoisirCouleurDessin(CouleurParNom("red")); EcrireTexte(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2, "BRAVO ! PRESSEZ UNE TOUCHE.", 2); @@ -93,6 +111,7 @@ void game_render(const game_state_t *state) } +/* 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; @@ -100,6 +119,7 @@ void game_loop(game_state_t *state) while (go_on && !state->is_solved) { + /* Rendu et rafraîchissement */ if (Microsecondes() > next_update) { game_render(state); @@ -107,6 +127,7 @@ void game_loop(game_state_t *state) next_update = Microsecondes() + REFRESH_CYCLE; } + /* Gestion des événements Souris */ if (SourisCliquee()) { int i; @@ -115,11 +136,13 @@ void game_loop(game_state_t *state) int tile_w = state->all_tiles[0]->width; int tile_h = state->all_tiles[0]->height; + /* Vérifier le bouton RETOUR MENU */ if (is_in_tile_rect(x_click, y_click, WINDOW_WIDTH - 200, WINDOW_HEIGHT - 60, 150, 40)) { - go_on = 0; + go_on = 0; /* Quitte la boucle de jeu pour revenir au menu */ break; } + /* Gérer le clic sur les tuiles */ for (i = 0; i < state->rows * state->cols; i++) { int r = i / state->cols; int c = i % state->cols; @@ -128,12 +151,14 @@ void game_loop(game_state_t *state) 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(); @@ -142,45 +167,55 @@ void game_loop(game_state_t *state) int empty_c = state->empty_index % state->cols; if (key == XK_Escape) { - exit(EXIT_SUCCESS); + exit(EXIT_SUCCESS); /* Quitte l'application entièrement si ESC */ } + /* Touche 'R' pour revenir au menu (Nouvelle Partie) */ else if (key == 'r' || key == 'R') { go_on = 0; } + /* Logique de déplacement Clavier (corrigée) */ else if (key == XK_Up) { + /* HAUT : Déplace la tuile en BAS (r + 1) vers le vide (r) */ target_index = (empty_r + 1) * state->cols + empty_c; } else if (key == XK_Down) { + /* BAS : Déplace la tuile en HAUT (r - 1) vers le vide (r) */ target_index = (empty_r - 1) * state->cols + empty_c; } else if (key == XK_Left) { + /* GAUCHE : Déplace la tuile à DROITE (c + 1) vers le vide (c) */ target_index = empty_r * state->cols + (empty_c + 1); } else if (key == XK_Right) { + /* DROITE : Déplace la tuile à GAUCHE (c - 1) vers le vide (c) */ 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; + return; /* Retourne au main pour relancer le menu */ } else if (key == 'q' || key == 'Q') { - exit(EXIT_SUCCESS); + exit(EXIT_SUCCESS); /* Quitte le programme entier */ } } Microsecondes(); } } + /* Si la boucle est sortie par 'R' (go_on = 0), on retourne au main.c */ return; } diff --git a/src/game_view.h b/src/game_view.h index f7637ef..ba9b6b7 100644 --- a/src/game_view.h +++ b/src/game_view.h @@ -1,13 +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" - +/* 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 +#endif /* GAME_VIEW_H */ diff --git a/src/images.c b/src/images.c index 1a07e60..b60bd99 100644 --- a/src/images.c +++ b/src/images.c @@ -1,33 +1,58 @@ +/* src/images.c + * Gère le découpage de l'image source en tuiles (structures tile_t). + */ + #include "images.h" #include #include #include +/* Tableau statique des chemins d'images disponibles (synchronisé avec l'ordre du menu) */ static const char *IMAGE_PATHS[] = { "assets/spiderman.jpg", /* Index 0 */ - "assets/spiderman.jpg", /* Index 1 */ - "assets/spiderman.jpg" /* Index 2 */ + "assets/capy.jpg", /* Index 1 */ + "assets/iut.jpg" /* Index 2 */ }; +/* Retourne les informations de l'image choisie, y compris les dimensions sources réelles. */ image_source_t get_image_source(int selected_index) { image_source_t src; + /* 1. Définition du chemin selon l'index */ if (selected_index >= 0 && selected_index < 3) { strcpy(src.path, IMAGE_PATHS[selected_index]); } else { strcpy(src.path, IMAGE_PATHS[0]); /* Fallback */ } - src.width = 800; - src.height = 480; + /* 2. DÉFINITION DES TAILLES RÉELLES (Utilisation des données 'identify') */ + if (selected_index == 0) { + /* Spiderman (720x720) */ + src.width = 720; + src.height = 720; + } else if (selected_index == 1) { + /* Capybara (1000x662) */ + src.width = 1000; + src.height = 662; + } else { /* Index 2 */ + /* IUT (1200x800) */ + src.width = 1200; + src.height = 800; + } + + /* 3. ASSURER LA DIVISIBILITÉ PAR 8 (Ajustement pour le découpage propre) */ + /* On retire le reste de la division (modulo 8) pour éviter les erreurs de bordure. */ + src.width = src.width - (src.width % 8); + src.height = src.height - (src.height % 8); (void) selected_index; 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; @@ -35,20 +60,24 @@ tile_t **images_slice(image_source_t img_src, int rows, int cols) 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 *)); 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) { 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; @@ -63,6 +92,7 @@ tile_t **images_slice(image_source_t img_src, int rows, int cols) return tiles; } +/* Libère la mémoire allouée par images_slice */ void images_free_tiles(tile_t **tiles, int num_tiles) { int i; diff --git a/src/images.h b/src/images.h index b784adc..a53ca1c 100644 --- a/src/images.h +++ b/src/images.h @@ -1,24 +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; + 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 +#endif /* IMAGES_H */ diff --git a/src/main.c b/src/main.c index b9de518..623df93 100644 --- a/src/main.c +++ b/src/main.c @@ -1,25 +1,32 @@ +/* src/main.c + * Point d'entrée du programme Taquin. + * Gère la boucle principale des événements et le cycle de vie (Menu <-> Jeu). + */ + #include "menu_view.h" #include "game_logic.h" #include "images.h" #include "game_view.h" #include "defs.h" #include -#include +#include /* Pour exit() et srand() */ #include -#include int main(void) { menu_state_t menu_state; unsigned long next_update; + /* 1. Initialisation de l'environnement IUT */ InitialiserGraphique(); CreerFenetre(10, 10, WINDOW_WIDTH, WINDOW_HEIGHT); + /* Utilisation de srand(1) pour l'aléatoire stable en C89 */ + srand(1); - srand((unsigned int)time(NULL)); - + /* GRANDE BOUCLE PRINCIPALE : Répète tout le cycle (Menu -> Jeu) */ do { + /* 2. Phase de Configuration (Menu) */ menu_init(&menu_state); next_update = Microsecondes() + REFRESH_CYCLE; @@ -37,9 +44,11 @@ int main(void) } menu_handle_key(&menu_state); + /* Sortie anticipée du menu par ESC */ if (menu_state.ready_to_play == -1) goto end_program; } + /* 3. Lancement du Jeu (si le joueur a appuyé sur "Démarrer") */ if (menu_state.ready_to_play == 1) { image_source_t img_src; @@ -47,12 +56,13 @@ int main(void) game_state_t game_state; int num_tiles; + /* 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); if (tiles == NULL) { fprintf(stderr, "Erreur: Allocation tuiles echouee. Retour au menu.\n"); - continue; + continue; /* Retour au début du do...while */ } num_tiles = menu_state.rows * menu_state.cols; @@ -62,18 +72,23 @@ int main(void) printf("DEBUG: Jeu prêt. Grille %d x %d melangee. Lancement de la partie...\n", game_state.rows, game_state.cols); + /* Lancement de la BOUCLE DE JEU (bloque jusqu'à Victoire ou Retour Menu) */ game_loop(&game_state); + /* 4. Nettoyage et Recommencement */ + /* Si game_loop retourne, nous nettoyons et relançons le menu. */ images_free_tiles(tiles, num_tiles); game_free(&game_state); + /* Laisse la boucle do...while redémarrer au menu */ } - } while (1); + } while (1); /* La boucle est infinie, la sortie se fait par goto ou exit() */ -end_program:; +end_program:; /* Étiquette pour le goto */ + /* Sortie du programme */ FermerGraphique(); return EXIT_SUCCESS; } diff --git a/src/menu_view.c b/src/menu_view.c index b9f32d3..430478e 100644 --- a/src/menu_view.c +++ b/src/menu_view.c @@ -1,3 +1,7 @@ +/* src/menu_view.c + * Implémentation complète de l'interface de configuration (Menu) + */ + #include "menu_view.h" #include "defs.h" #include @@ -5,30 +9,37 @@ #include #include +/* Constantes de positionnement */ #define IMG_PREVIEW_SIZE 150 -#define IMG_START_X 50 +#define IMG_START_X 250 #define IMG_START_Y 150 #define IMG_SPACING 200 #define BUTTON_SIZE 30 #define DIM_START_Y 350 +/* Variables globales de la bibliothèque IUT pour la souris */ extern int _X; extern int _Y; +/* --- PROTOTYPES DES UTILITAIRES PRIVÉS --- */ static int is_in_rect(int x_click, int y_click, int rx, int ry, int rw, int rh); static void draw_button(int x, int y, const char *text, couleur C_BG); static void draw_text_centered(int x, int y, int w, int h, const char *text, int size); +/* --- IMPLÉMENTATION DES FONCTIONS PUBLIQUES (API du Menu) --- */ +/* Initialise l'état du menu */ void menu_init(menu_state_t *state) { - state->selected_image = 0; + state->selected_image = 0; /* Image 1 sélectionnée par défaut */ + /* Ces constantes sont maintenant trouvées dans menu_view.h */ state->rows = MIN_DIMENSION; state->cols = MIN_DIMENSION; state->ready_to_play = 0; } +/* Dessine l'écran de configuration du menu */ void menu_render(const menu_state_t *state) { int i; @@ -40,22 +51,35 @@ void menu_render(const menu_state_t *state) char buffer[50]; char path_buffer[30]; - const char *path_fixed = "assets/spiderman.jpg"; /* FIXE LE CHEMIN */ EffacerEcran(C_FOND); ChoisirCouleurDessin(C_TEXTE); + /* 1. Titre et Section Image */ EcrireTexte(IMG_START_X, 50, "CONFIGURATION DU JEU TAQUIN", 2); EcrireTexte(IMG_START_X, 100, "1. Choisir l'image :", 1); + /* BOUCLE : Affichage des 3 aperçus */ for (i = 0; i < NUM_IMAGES; i++) { int x = IMG_START_X + i * IMG_SPACING; int y = IMG_START_Y; - strcpy(path_buffer, path_fixed); + /* Définition du chemin */ + if (i == 0) { + strcpy(path_buffer, "assets/spiderman.jpg"); + } else if (i == 1) { + strcpy(path_buffer, "assets/capy.jpg"); + } else { + strcpy(path_buffer, "assets/iut.jpg"); + } - ChargerImage(path_buffer, x, y, 0, 0, IMG_PREVIEW_SIZE, IMG_PREVIEW_SIZE); + /* Affiche l'image */ + ChargerImage(path_buffer, + x, y, + 0, 0, + IMG_PREVIEW_SIZE, IMG_PREVIEW_SIZE); + /* Encadrement si sélectionné */ if (state->selected_image == i) { ChoisirCouleurDessin(C_SELECTION); DessinerRectangle(x - 5, y - 5, IMG_PREVIEW_SIZE + 10, IMG_PREVIEW_SIZE + 10); @@ -63,27 +87,33 @@ void menu_render(const menu_state_t *state) } ChoisirCouleurDessin(C_TEXTE); + /* 2. Section Dimensions */ int dim_section_x = IMG_START_X; int button_l_offset = 180; int button_c_offset = 400; EcrireTexte(dim_section_x, DIM_START_Y, "2. Choisir les dimensions (3x3 a 8x8) :", 1); + /* Affichage de la taille LIGNES */ EcrireTexte(dim_section_x, DIM_START_Y + 40, "Lignes :", 0); sprintf(buffer, "%d", state->rows); EcrireTexte(dim_section_x + 100, DIM_START_Y + 40, buffer, 0); + /* Affichage de la taille COLONNES */ EcrireTexte(dim_section_x + 250, DIM_START_Y + 40, "Colonnes :", 0); sprintf(buffer, "%d", state->cols); EcrireTexte(dim_section_x + 350, DIM_START_Y + 40, buffer, 0); + /* Boutons de contrôle LIGNES */ draw_button(dim_section_x + button_l_offset, DIM_START_Y + 30, "+", C_BOUTON); draw_button(dim_section_x + button_l_offset + 40, DIM_START_Y + 30, "-", C_BOUTON); + /* Boutons de contrôle COLONNES */ draw_button(dim_section_x + button_c_offset, DIM_START_Y + 30, "+", C_BOUTON); draw_button(dim_section_x + button_c_offset + 40, DIM_START_Y + 30, "-", C_BOUTON); + /* 3. Bouton DÉMARRER LA PARTIE */ ChoisirCouleurDessin(C_START); RemplirRectangle(300, 500, 200, 50); ChoisirCouleurDessin(CouleurParNom("white")); @@ -91,6 +121,7 @@ void menu_render(const menu_state_t *state) } +/* Gère l'interaction après un clic de souris */ void menu_handle_mouse_click(menu_state_t *state) { int i; @@ -101,6 +132,7 @@ void menu_handle_mouse_click(menu_state_t *state) int button_l_offset = 180; int button_c_offset = 400; + /* 1. Clic sur les images */ for (i = 0; i < NUM_IMAGES; i++) { int x_img = IMG_START_X + i * IMG_SPACING; int y_img = IMG_START_Y; @@ -111,28 +143,35 @@ void menu_handle_mouse_click(menu_state_t *state) } } + /* 2. Clic sur les boutons de Lignes (+/-) */ + /* + Lignes */ if (is_in_rect(x_click, y_click, dim_section_x + button_l_offset, DIM_START_Y + 30, btn_size, btn_size)) { if (state->rows < MAX_DIMENSION) state->rows++; return; } + /* - Lignes */ if (is_in_rect(x_click, y_click, dim_section_x + button_l_offset + 40, DIM_START_Y + 30, btn_size, btn_size)) { if (state->rows > MIN_DIMENSION) state->rows--; return; } + /* 3. Clic sur les boutons de Colonnes (+/-) */ + /* + Colonnes */ if (is_in_rect(x_click, y_click, dim_section_x + button_c_offset, DIM_START_Y + 30, btn_size, btn_size)) { if (state->cols < MAX_DIMENSION) state->cols++; return; } + /* - Colonnes */ if (is_in_rect(x_click, y_click, dim_section_x + button_c_offset + 40, DIM_START_Y + 30, btn_size, btn_size)) { if (state->cols > MIN_DIMENSION) state->cols--; return; } + /* 4. Clic sur DÉMARRER */ if (is_in_rect(x_click, y_click, 300, 500, 200, 50)) { state->ready_to_play = 1; return; @@ -140,6 +179,7 @@ void menu_handle_mouse_click(menu_state_t *state) } +/* Gère l'interaction clavier */ void menu_handle_key(menu_state_t *state) { if (ToucheEnAttente()) { @@ -166,12 +206,15 @@ void menu_handle_key(menu_state_t *state) } } +/* --- IMPLÉMENTATION DES UTILITAIRES PRIVÉS --- */ +/* Vérifie si (x_click, y_click) est dans le rectangle */ static int is_in_rect(int x_click, int y_click, int rx, int ry, int rw, int rh) { return (x_click >= rx && x_click <= rx + rw && y_click >= ry && y_click <= ry + rh); } +/* Dessine un bouton rectangulaire rempli avec du texte centré */ static void draw_button(int x, int y, const char *text, couleur C_BG) { ChoisirCouleurDessin(C_BG); @@ -180,10 +223,12 @@ static void draw_button(int x, int y, const char *text, couleur C_BG) draw_text_centered(x, y, BUTTON_SIZE, BUTTON_SIZE, (char*)text, 1); } +/* Centre le texte dans une zone (x, y, w, h) */ static void draw_text_centered(int x, int y, int w, int h, const char *text, int size) { int text_w = TailleChaineEcran((char*)text, size); int text_h_sup = TailleSupPolice(size); + /* Calcul de la position de départ pour centrer le texte */ int draw_x = x + (w - text_w) / 2; int draw_y = y + (h + text_h_sup) / 2; diff --git a/src/menu_view.h b/src/menu_view.h index 4041c3d..02b22b2 100644 --- a/src/menu_view.h +++ b/src/menu_view.h @@ -1,20 +1,27 @@ +/* src/menu_view.h + * Définition de l'état du menu et des constantes. + */ + #ifndef MENU_VIEW_H #define MENU_VIEW_H +/* Constantes de dimension de la grille */ #define NUM_IMAGES 3 #define MIN_DIMENSION 3 #define MAX_DIMENSION 8 +/* Structure de l'état du menu */ typedef struct { - int selected_image; - int rows; - int cols; - int ready_to_play; -} menu_state_t; + 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é, -1 si demande de quitter */ +} menu_state_t; +/* 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); void menu_handle_key(menu_state_t *state); -#endif +#endif /* MENU_VIEW_H */ diff --git a/src/utils.c b/src/utils.c index 8b2a4c5..dc2fb3b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,6 +1,14 @@ +/* src/utils.c + * Implémentation des fonctions utilitaires (valeur absolue et aléatoire). + */ + #include "utils.h" #include +/* + * Renvoie la valeur absolue d'un entier. + * Utilisé dans game_logic.c pour vérifier l'adjacence des tuiles. + */ int abs_val(int n) { if (n < 0) { @@ -9,8 +17,13 @@ int abs_val(int n) return n; } +/* + * Renvoie un entier pseudo-aléatoire entre 0 et max-1. + * Utilisé par game_shuffle_safe pour choisir le prochain mouvement. + */ int random_int(int max) { if (max <= 0) return 0; + /* La fonction srand() est appelée une seule fois dans main.c. */ return rand() % max; } diff --git a/src/utils.h b/src/utils.h index 1949ec1..99ec4d3 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,8 +1,14 @@ +/* src/utils.h + * Déclarations des fonctions utilitaires de base. + */ + #ifndef UTILS_H #define UTILS_H +/* Renvoie la valeur absolue d'un entier. */ int abs_val(int n); +/* Renvoie un entier pseudo-aléatoire entre 0 et max-1. */ int random_int(int max); -#endif +#endif /* UTILS_H */ diff --git a/taquin b/taquin index c6c5199..635d91f 100755 Binary files a/taquin and b/taquin differ