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 086c444..0000000 Binary files a/obj/game.o and /dev/null differ diff --git a/obj/game_logic.o b/obj/game_logic.o new file mode 100644 index 0000000..ca3442c Binary files /dev/null and b/obj/game_logic.o differ diff --git a/obj/images.o b/obj/images.o new file mode 100644 index 0000000..7daa184 Binary files /dev/null and b/obj/images.o differ diff --git a/obj/main.o b/obj/main.o new file mode 100644 index 0000000..945b9cb Binary files /dev/null and b/obj/main.o differ diff --git a/obj/menu_view.o b/obj/menu_view.o new file mode 100644 index 0000000..306e829 Binary files /dev/null and b/obj/menu_view.o differ diff --git a/obj/utils.o b/obj/utils.o new file mode 100644 index 0000000..d61a19d Binary files /dev/null and b/obj/utils.o differ 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 0000000..dbd973c Binary files /dev/null and b/taquin differ