This commit is contained in:
lecointre
2025-11-30 15:56:48 +01:00
parent 0939e7369d
commit 99cb83582a
16 changed files with 433 additions and 37 deletions
+28 -16
View File
@@ -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)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+165
View File
@@ -0,0 +1,165 @@
/* src/game_logic.c */
#include "game_logic.h"
#include "utils.h"
#include <stdlib.h>
#include <stdio.h>
/* --- 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;
}
+38
View File
@@ -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 */
+89
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 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);
}
+35
View File
@@ -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 */
+38 -18
View File
@@ -4,11 +4,15 @@
*/
#include "menu_view.h"
#include <graph.h> /* La bibliothèque IUT */
#include <stdlib.h> /* Pour EXIT_SUCCESS */
#include <stdio.h> /* 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 <graph.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h> /* 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;
}
+5 -3
View File
@@ -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);
+21
View File
@@ -0,0 +1,21 @@
/* src/utils.c */
#include "utils.h"
#include <stdlib.h>
/* 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;
}
+14
View File
@@ -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 */
Executable
BIN
View File
Binary file not shown.