Fin v2 bientot fini

This commit is contained in:
lecointre
2025-12-06 19:44:44 +01:00
parent 9ad4e56bd8
commit 512523b53d
21 changed files with 290 additions and 51 deletions
+18 -2
View File
@@ -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
# Déclare les cibles qui ne correspondent pas à des fichiers
.PHONY: all clean run
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

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.
+10 -3
View File
@@ -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 */
+46 -4
View File
@@ -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 <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;
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,15 +151,33 @@ 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;
}
+15 -6
View File
@@ -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 */
+42 -7
View File
@@ -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 <graph.h>
#include <stdio.h>
@@ -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;
}
+6 -3
View File
@@ -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 */
+34 -4
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 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;
+13 -2
View File
@@ -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 */
+22 -7
View File
@@ -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 <graph.h>
#include <stdlib.h>
#include <stdlib.h> /* Pour exit() et srand() */
#include <stdio.h>
#include <time.h>
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;
}
+50 -5
View File
@@ -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 <graph.h>
@@ -5,30 +9,37 @@
#include <stdlib.h>
#include <string.h>
/* 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;
+12 -5
View File
@@ -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;
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 */
+13
View File
@@ -1,6 +1,14 @@
/* src/utils.c
* Implémentation des fonctions utilitaires (valeur absolue et aléatoire).
*/
#include "utils.h"
#include <stdlib.h>
/*
* 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;
}
+7 -1
View File
@@ -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 */
BIN
View File
Binary file not shown.