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 0000000..413c49e Binary files /dev/null and b/obj/game_view.o differ diff --git a/obj/main.o b/obj/main.o index 945b9cb..9e8a3b2 100644 Binary files a/obj/main.o and b/obj/main.o differ diff --git a/src/defs.h b/src/defs.h new file mode 100644 index 0000000..ea890fe --- /dev/null +++ b/src/defs.h @@ -0,0 +1,12 @@ +/* src/defs.h + * Définitions des constantes globales (taille de fenêtre, cycles). + * Inclus par main.c et game_view.c. + */ +#ifndef DEFS_H +#define DEFS_H + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 +#define REFRESH_CYCLE 50000L /* 50 ms */ + +#endif /* DEFS_H */ diff --git a/src/game_view.c b/src/game_view.c new file mode 100644 index 0000000..8963608 --- /dev/null +++ b/src/game_view.c @@ -0,0 +1,182 @@ +/* src/game_view.c + * Implémentation du rendu graphique du plateau et de la boucle d'interaction. + */ + +#include "game_view.h" +#include +#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 dbd973c..ed640ef 100755 Binary files a/taquin and b/taquin differ