Fin v2 bientot fini
This commit is contained in:
@@ -1,29 +1,45 @@
|
|||||||
|
# Makefile pour le projet Taquin - SAE S1.02 / S1.04
|
||||||
|
|
||||||
|
# Variables de compilation
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -std=c89
|
CFLAGS = -Wall -Wextra -std=c89
|
||||||
LDFLAGS = -lgraph
|
LDFLAGS = -lgraph
|
||||||
TARGET = taquin
|
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
|
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))
|
OBJS = $(patsubst src/%.c, obj/%.o, $(SRCS))
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Cibles principales
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Cible par défaut (crée l'exécutable)
|
||||||
all: dir $(TARGET)
|
all: dir $(TARGET)
|
||||||
|
|
||||||
|
# Crée le dossier obj/ si non existant
|
||||||
dir:
|
dir:
|
||||||
@mkdir -p obj
|
@mkdir -p obj
|
||||||
|
|
||||||
|
# Règle de liaison : crée l'exécutable taquin à partir de tous les objets
|
||||||
$(TARGET): $(OBJS)
|
$(TARGET): $(OBJS)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
# Règle générique pour la compilation de tous les .c en .o
|
||||||
obj/%.o: src/%.c
|
obj/%.o: src/%.c
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
# Exécute le programme
|
||||||
run: $(TARGET)
|
run: $(TARGET)
|
||||||
./$(TARGET)
|
./$(TARGET)
|
||||||
|
|
||||||
|
# Nettoyage
|
||||||
clean:
|
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
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 426 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
+10
-3
@@ -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
|
#ifndef DEFS_H
|
||||||
#define DEFS_H
|
#define DEFS_H
|
||||||
|
|
||||||
#define WINDOW_WIDTH 800
|
/* Dimensions de la fenêtre finale pour éviter le chevauchement */
|
||||||
#define WINDOW_HEIGHT 600
|
#define WINDOW_WIDTH 1000
|
||||||
|
#define WINDOW_HEIGHT 700
|
||||||
|
|
||||||
|
/* Cycle de rafraîchissement pour la boucle de jeu (50 ms) */
|
||||||
#define REFRESH_CYCLE 50000L
|
#define REFRESH_CYCLE 50000L
|
||||||
|
|
||||||
#endif
|
#endif /* DEFS_H */
|
||||||
|
|||||||
+46
-4
@@ -1,31 +1,40 @@
|
|||||||
|
/* src/game_logic.c
|
||||||
|
* Implémentation du moteur de jeu (corrigé pour la liaison).
|
||||||
|
*/
|
||||||
|
|
||||||
#include "game_logic.h"
|
#include "game_logic.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.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)
|
static void index_to_coords(int index, int cols, int *r, int *c)
|
||||||
{
|
{
|
||||||
*r = index / cols;
|
*r = index / cols;
|
||||||
*c = 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)
|
static int find_adjacent_indices(int idx, int rows, int cols, int *out_indices)
|
||||||
{
|
{
|
||||||
int r = idx / cols;
|
int r = idx / cols;
|
||||||
int c = idx % cols;
|
int c = idx % cols;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
if (r > 0) out_indices[n++] = (r - 1) * cols + c;
|
if (r > 0) out_indices[n++] = (r - 1) * cols + c; /* Haut */
|
||||||
if (r < rows - 1) out_indices[n++] = (r + 1) * cols + c;
|
if (r < rows - 1) out_indices[n++] = (r + 1) * cols + c; /* Bas */
|
||||||
if (c > 0) out_indices[n++] = r * cols + (c - 1);
|
if (c > 0) out_indices[n++] = r * cols + (c - 1); /* Gauche */
|
||||||
if (c < cols - 1) out_indices[n++] = r * cols + (c + 1);
|
if (c < cols - 1) out_indices[n++] = r * cols + (c + 1); /* Droite */
|
||||||
|
|
||||||
return n;
|
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)
|
void game_init(game_state_t *state, int rows, int cols, tile_t **tiles)
|
||||||
{
|
{
|
||||||
int num_tiles = rows * cols;
|
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->moves = 0UL;
|
||||||
state->all_tiles = tiles;
|
state->all_tiles = tiles;
|
||||||
|
|
||||||
|
/* Allocation dynamique de la permutation */
|
||||||
state->permutation = (int *)malloc(num_tiles * sizeof(int));
|
state->permutation = (int *)malloc(num_tiles * sizeof(int));
|
||||||
|
|
||||||
if (state->permutation == NULL) {
|
if (state->permutation == NULL) {
|
||||||
@@ -43,9 +53,11 @@ void game_init(game_state_t *state, int rows, int cols, tile_t **tiles)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Règle SAE: La case vide (-1) est à l'index 0 (Haut à Gauche) */
|
||||||
state->permutation[0] = -1;
|
state->permutation[0] = -1;
|
||||||
state->empty_index = 0;
|
state->empty_index = 0;
|
||||||
|
|
||||||
|
/* Remplissage des autres tuiles (IDs 0, 1, 2, ... N-2) */
|
||||||
for (i = 1; i < num_tiles; i++) {
|
for (i = 1; i < num_tiles; i++) {
|
||||||
state->permutation[i] = i - 1;
|
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;
|
state->is_solved = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Libère la mémoire allouée par game_init */
|
||||||
void game_free(game_state_t *state)
|
void game_free(game_state_t *state)
|
||||||
{
|
{
|
||||||
if (state->permutation != NULL) {
|
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 game_can_move(const game_state_t *state, int index_to_move)
|
||||||
{
|
{
|
||||||
int r_tile, c_tile;
|
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(index_to_move, state->cols, &r_tile, &c_tile);
|
||||||
index_to_coords(state->empty_index, state->cols, &r_empty, &c_empty);
|
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) {
|
if (abs_val(r_tile - r_empty) + abs_val(c_tile - c_empty) == 1) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
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 game_move_tile(game_state_t *state, int index_to_move)
|
||||||
{
|
{
|
||||||
int temp_tile_id;
|
int temp_tile_id;
|
||||||
@@ -86,28 +102,35 @@ int game_move_tile(game_state_t *state, int index_to_move)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Échange des valeurs (swap) */
|
||||||
temp_tile_id = state->permutation[index_to_move];
|
temp_tile_id = state->permutation[index_to_move];
|
||||||
state->permutation[index_to_move] = state->permutation[state->empty_index];
|
state->permutation[index_to_move] = state->permutation[state->empty_index];
|
||||||
state->permutation[state->empty_index] = temp_tile_id;
|
state->permutation[state->empty_index] = temp_tile_id;
|
||||||
|
|
||||||
|
/* Mise à jour de la nouvelle position de la case vide */
|
||||||
state->empty_index = index_to_move;
|
state->empty_index = index_to_move;
|
||||||
|
|
||||||
|
/* Mise à jour des compteurs */
|
||||||
state->moves++;
|
state->moves++;
|
||||||
state->is_solved = game_is_solved(state);
|
state->is_solved = game_is_solved(state);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Vérifie si le plateau est dans l'état résolu */
|
||||||
int game_is_solved(const game_state_t *state)
|
int game_is_solved(const game_state_t *state)
|
||||||
{
|
{
|
||||||
int num_tiles = state->rows * state->cols;
|
int num_tiles = state->rows * state->cols;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* 1. La case vide doit être en PREMIÈRE position (index 0) */
|
||||||
if (state->permutation[0] != -1) {
|
if (state->permutation[0] != -1) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 2. Les tuiles restantes (index 1 à N-1) doivent être en ordre [0, 1, 2, ...] */
|
||||||
for (i = 1; i < num_tiles; i++) {
|
for (i = 1; i < num_tiles; i++) {
|
||||||
|
/* La tuile à la position 'i' doit avoir l'ID 'i - 1' */
|
||||||
if (state->permutation[i] != i - 1) {
|
if (state->permutation[i] != i - 1) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -116,6 +139,7 @@ int game_is_solved(const game_state_t *state)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mélange garanti solvable par N mouvements aléatoires (étapes) */
|
||||||
void game_shuffle_safe(game_state_t *state, int steps)
|
void game_shuffle_safe(game_state_t *state, int steps)
|
||||||
{
|
{
|
||||||
int prev_move = -1;
|
int prev_move = -1;
|
||||||
@@ -127,15 +151,33 @@ void game_shuffle_safe(game_state_t *state, int steps)
|
|||||||
for (i = 0; i < steps; i++) {
|
for (i = 0; i < steps; i++) {
|
||||||
n_choices = find_adjacent_indices(state->empty_index, state->rows, state->cols, choices);
|
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 {
|
do {
|
||||||
pick_index = choices[random_int(n_choices)];
|
pick_index = choices[random_int(n_choices)];
|
||||||
} while (n_choices > 1 && pick_index == prev_move);
|
} while (n_choices > 1 && pick_index == prev_move);
|
||||||
|
|
||||||
|
/* Déplacer la tuile (utilise la logique de déplacement) */
|
||||||
game_move_tile(state, pick_index);
|
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;
|
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->moves = 0UL;
|
||||||
state->is_solved = 0;
|
state->is_solved = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-6
@@ -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
|
#ifndef GAME_LOGIC_H
|
||||||
#define 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 {
|
typedef struct {
|
||||||
int rows;
|
int rows;
|
||||||
int cols;
|
int cols;
|
||||||
|
|
||||||
|
/* Tableau 1D alloué dynamiquement pour la permutation */
|
||||||
int *permutation;
|
int *permutation;
|
||||||
|
|
||||||
int empty_index;
|
int empty_index; /* Position actuelle (index 1D) de la case vide */
|
||||||
unsigned long moves;
|
unsigned long moves; /* Compteur de coups */
|
||||||
int is_solved;
|
int is_solved; /* 1 si gagné */
|
||||||
|
|
||||||
tile_t **all_tiles;
|
tile_t **all_tiles; /* Les tuiles découpées */
|
||||||
} game_state_t;
|
} game_state_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Prototypes des fonctions publiques */
|
||||||
void game_init(game_state_t *state, int rows, int cols, tile_t **tiles);
|
void game_init(game_state_t *state, int rows, int cols, tile_t **tiles);
|
||||||
void game_free(game_state_t *state);
|
void game_free(game_state_t *state);
|
||||||
|
|
||||||
|
/* Logique de jeu */
|
||||||
int game_move_tile(game_state_t *state, int index_to_move);
|
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);
|
int game_can_move(const game_state_t *state, int index_to_move);
|
||||||
|
|
||||||
|
/* Solvabilité */
|
||||||
int game_is_solved(const game_state_t *state);
|
int game_is_solved(const game_state_t *state);
|
||||||
void game_shuffle_safe(game_state_t *state, int steps);
|
void game_shuffle_safe(game_state_t *state, int steps);
|
||||||
|
|
||||||
#endif
|
#endif /* GAME_LOGIC_H */
|
||||||
|
|||||||
+42
-7
@@ -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 "game_view.h"
|
||||||
#include <graph.h>
|
#include <graph.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -5,17 +9,21 @@
|
|||||||
|
|
||||||
extern int _X;
|
extern int _X;
|
||||||
extern int _Y;
|
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)
|
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 &&
|
return (x_click >= tile_x && x_click <= tile_x + tile_w &&
|
||||||
y_click >= tile_y && y_click <= tile_y + tile_h);
|
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)
|
void game_render(const game_state_t *state)
|
||||||
{
|
{
|
||||||
int i, r, c;
|
int i, r, c;
|
||||||
@@ -24,11 +32,13 @@ void game_render(const game_state_t *state)
|
|||||||
|
|
||||||
EffacerEcran(C_FOND);
|
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_w = state->all_tiles[0]->width;
|
||||||
int tile_display_h = state->all_tiles[0]->height;
|
int tile_display_h = state->all_tiles[0]->height;
|
||||||
|
|
||||||
ChoisirCouleurDessin(C_BORDURE);
|
ChoisirCouleurDessin(C_BORDURE);
|
||||||
|
|
||||||
|
/* 1. Affichage du Plateau (Tuiles et Case Vide) */
|
||||||
for (i = 0; i < state->rows * state->cols; i++) {
|
for (i = 0; i < state->rows * state->cols; i++) {
|
||||||
int tile_id = state->permutation[i];
|
int tile_id = state->permutation[i];
|
||||||
int x_pos, y_pos;
|
int x_pos, y_pos;
|
||||||
@@ -36,12 +46,14 @@ void game_render(const game_state_t *state)
|
|||||||
r = i / state->cols;
|
r = i / state->cols;
|
||||||
c = i % state->cols;
|
c = i % state->cols;
|
||||||
|
|
||||||
|
/* Position d'affichage sur l'écran */
|
||||||
x_pos = MARGIN + c * tile_display_w;
|
x_pos = MARGIN + c * tile_display_w;
|
||||||
y_pos = MARGIN + r * tile_display_h;
|
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];
|
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,
|
ChargerImage(current_tile->path,
|
||||||
x_pos, y_pos,
|
x_pos, y_pos,
|
||||||
current_tile->src_x, current_tile->src_y,
|
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);
|
DessinerRectangle(x_pos, y_pos, tile_display_w, tile_display_h);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
/* Dessine la case vide en jaune */
|
||||||
ChoisirCouleurDessin(CouleurParNom("yellow"));
|
ChoisirCouleurDessin(CouleurParNom("yellow"));
|
||||||
RemplirRectangle(x_pos, y_pos, tile_display_w, tile_display_h);
|
RemplirRectangle(x_pos, y_pos, tile_display_w, tile_display_h);
|
||||||
ChoisirCouleurDessin(C_BORDURE);
|
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) {
|
if (state->all_tiles != NULL && state->all_tiles[0] != NULL) {
|
||||||
tile_t *sample_tile = state->all_tiles[0];
|
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);
|
RemplirRectangle(model_x - 5, model_y - 5, model_size + 10, model_size + 10);
|
||||||
ChoisirCouleurDessin(CouleurParNom("black"));
|
ChoisirCouleurDessin(CouleurParNom("black"));
|
||||||
|
|
||||||
|
/* Affiche l'image entière (source 0,0) avec une taille réduite */
|
||||||
ChargerImage(sample_tile->path,
|
ChargerImage(sample_tile->path,
|
||||||
model_x, model_y,
|
model_x, model_y,
|
||||||
0, 0,
|
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];
|
char buffer[100];
|
||||||
sprintf(buffer, "Coups: %lu", state->moves);
|
sprintf(buffer, "Coups: %lu", state->moves);
|
||||||
EcrireTexte(MARGIN, WINDOW_HEIGHT - 30, buffer, 1);
|
EcrireTexte(MARGIN, WINDOW_HEIGHT - 30, buffer, 1);
|
||||||
|
|
||||||
|
/* 4. Bouton Retour Menu */
|
||||||
int btn_x = WINDOW_WIDTH - 200;
|
int btn_x = WINDOW_WIDTH - 200;
|
||||||
int btn_y = WINDOW_HEIGHT - 60;
|
int btn_y = WINDOW_HEIGHT - 60;
|
||||||
|
|
||||||
@@ -86,6 +103,7 @@ void game_render(const game_state_t *state)
|
|||||||
ChoisirCouleurDessin(CouleurParNom("black"));
|
ChoisirCouleurDessin(CouleurParNom("black"));
|
||||||
EcrireTexte(btn_x + 10, btn_y + 25, "MENU (R)", 1);
|
EcrireTexte(btn_x + 10, btn_y + 25, "MENU (R)", 1);
|
||||||
|
|
||||||
|
/* 5. Affichage de la victoire */
|
||||||
if (state->is_solved) {
|
if (state->is_solved) {
|
||||||
ChoisirCouleurDessin(CouleurParNom("red"));
|
ChoisirCouleurDessin(CouleurParNom("red"));
|
||||||
EcrireTexte(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2, "BRAVO ! PRESSEZ UNE TOUCHE.", 2);
|
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)
|
void game_loop(game_state_t *state)
|
||||||
{
|
{
|
||||||
unsigned long next_update = Microsecondes() + REFRESH_CYCLE;
|
unsigned long next_update = Microsecondes() + REFRESH_CYCLE;
|
||||||
@@ -100,6 +119,7 @@ void game_loop(game_state_t *state)
|
|||||||
|
|
||||||
while (go_on && !state->is_solved)
|
while (go_on && !state->is_solved)
|
||||||
{
|
{
|
||||||
|
/* Rendu et rafraîchissement */
|
||||||
if (Microsecondes() > next_update)
|
if (Microsecondes() > next_update)
|
||||||
{
|
{
|
||||||
game_render(state);
|
game_render(state);
|
||||||
@@ -107,6 +127,7 @@ void game_loop(game_state_t *state)
|
|||||||
next_update = Microsecondes() + REFRESH_CYCLE;
|
next_update = Microsecondes() + REFRESH_CYCLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Gestion des événements Souris */
|
||||||
if (SourisCliquee())
|
if (SourisCliquee())
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -115,11 +136,13 @@ void game_loop(game_state_t *state)
|
|||||||
int tile_w = state->all_tiles[0]->width;
|
int tile_w = state->all_tiles[0]->width;
|
||||||
int tile_h = state->all_tiles[0]->height;
|
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)) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Gérer le clic sur les tuiles */
|
||||||
for (i = 0; i < state->rows * state->cols; i++) {
|
for (i = 0; i < state->rows * state->cols; i++) {
|
||||||
int r = i / state->cols;
|
int r = i / state->cols;
|
||||||
int c = 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;
|
int y_pos = MARGIN + r * tile_h;
|
||||||
|
|
||||||
if (is_in_tile_rect(x_click, y_click, x_pos, y_pos, tile_w, 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);
|
game_move_tile(state, i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Gestion des événements Clavier */
|
||||||
if (ToucheEnAttente()) {
|
if (ToucheEnAttente()) {
|
||||||
int key = Touche();
|
int key = Touche();
|
||||||
|
|
||||||
@@ -142,45 +167,55 @@ void game_loop(game_state_t *state)
|
|||||||
int empty_c = state->empty_index % state->cols;
|
int empty_c = state->empty_index % state->cols;
|
||||||
|
|
||||||
if (key == XK_Escape) {
|
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') {
|
else if (key == 'r' || key == 'R') {
|
||||||
go_on = 0;
|
go_on = 0;
|
||||||
}
|
}
|
||||||
|
/* Logique de déplacement Clavier (corrigée) */
|
||||||
else if (key == XK_Up) {
|
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;
|
target_index = (empty_r + 1) * state->cols + empty_c;
|
||||||
} else if (key == XK_Down) {
|
} 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;
|
target_index = (empty_r - 1) * state->cols + empty_c;
|
||||||
}
|
}
|
||||||
else if (key == XK_Left) {
|
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);
|
target_index = empty_r * state->cols + (empty_c + 1);
|
||||||
} else if (key == XK_Right) {
|
} 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);
|
target_index = empty_r * state->cols + (empty_c - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tenter le mouvement s'il y a une cible */
|
||||||
if (target_index != -1) {
|
if (target_index != -1) {
|
||||||
game_move_tile(state, target_index);
|
game_move_tile(state, target_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Si la boucle est terminée par la victoire ou ESC */
|
||||||
if (state->is_solved) {
|
if (state->is_solved) {
|
||||||
game_render(state);
|
game_render(state);
|
||||||
CopierZone(0, 0, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
|
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);
|
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) {
|
while (1) {
|
||||||
if (ToucheEnAttente()) {
|
if (ToucheEnAttente()) {
|
||||||
int key = Touche();
|
int key = Touche();
|
||||||
if (key == 'n' || key == 'N') {
|
if (key == 'n' || key == 'N') {
|
||||||
return;
|
return; /* Retourne au main pour relancer le menu */
|
||||||
} else if (key == 'q' || key == 'Q') {
|
} else if (key == 'q' || key == 'Q') {
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS); /* Quitte le programme entier */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Microsecondes();
|
Microsecondes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Si la boucle est sortie par 'R' (go_on = 0), on retourne au main.c */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -1,13 +1,16 @@
|
|||||||
|
/* src/game_view.h
|
||||||
|
* Définitions du module de la vue du plateau de jeu.
|
||||||
|
*/
|
||||||
#ifndef GAME_VIEW_H
|
#ifndef GAME_VIEW_H
|
||||||
#define GAME_VIEW_H
|
#define GAME_VIEW_H
|
||||||
|
|
||||||
#include "game_logic.h"
|
#include "game_logic.h"
|
||||||
#include "defs.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);
|
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);
|
void game_loop(game_state_t *state);
|
||||||
|
|
||||||
#endif
|
#endif /* GAME_VIEW_H */
|
||||||
|
|||||||
+34
-4
@@ -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 "images.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Tableau statique des chemins d'images disponibles (synchronisé avec l'ordre du menu) */
|
||||||
static const char *IMAGE_PATHS[] = {
|
static const char *IMAGE_PATHS[] = {
|
||||||
"assets/spiderman.jpg", /* Index 0 */
|
"assets/spiderman.jpg", /* Index 0 */
|
||||||
"assets/spiderman.jpg", /* Index 1 */
|
"assets/capy.jpg", /* Index 1 */
|
||||||
"assets/spiderman.jpg" /* Index 2 */
|
"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 get_image_source(int selected_index)
|
||||||
{
|
{
|
||||||
image_source_t src;
|
image_source_t src;
|
||||||
|
|
||||||
|
/* 1. Définition du chemin selon l'index */
|
||||||
if (selected_index >= 0 && selected_index < 3) {
|
if (selected_index >= 0 && selected_index < 3) {
|
||||||
strcpy(src.path, IMAGE_PATHS[selected_index]);
|
strcpy(src.path, IMAGE_PATHS[selected_index]);
|
||||||
} else {
|
} else {
|
||||||
strcpy(src.path, IMAGE_PATHS[0]); /* Fallback */
|
strcpy(src.path, IMAGE_PATHS[0]); /* Fallback */
|
||||||
}
|
}
|
||||||
|
|
||||||
src.width = 800;
|
/* 2. DÉFINITION DES TAILLES RÉELLES (Utilisation des données 'identify') */
|
||||||
src.height = 480;
|
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;
|
(void) selected_index;
|
||||||
|
|
||||||
return src;
|
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)
|
tile_t **images_slice(image_source_t img_src, int rows, int cols)
|
||||||
{
|
{
|
||||||
int num_tiles = rows * 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;
|
tile_t **tiles;
|
||||||
int i, j, id = 0;
|
int i, j, id = 0;
|
||||||
|
|
||||||
|
/* Calcul des dimensions d'une tuile */
|
||||||
tile_w = img_src.width / cols;
|
tile_w = img_src.width / cols;
|
||||||
tile_h = img_src.height / rows;
|
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 *));
|
tiles = (tile_t **) calloc(num_tiles, sizeof(tile_t *));
|
||||||
if (tiles == NULL) return NULL;
|
if (tiles == NULL) return NULL;
|
||||||
|
|
||||||
for (i = 0; i < rows; i++) {
|
for (i = 0; i < rows; i++) {
|
||||||
for (j = 0; j < cols; j++) {
|
for (j = 0; j < cols; j++) {
|
||||||
|
/* Allocation de la structure de tuile */
|
||||||
tile_t *t = (tile_t *) malloc(sizeof(tile_t));
|
tile_t *t = (tile_t *) malloc(sizeof(tile_t));
|
||||||
if (t == NULL) {
|
if (t == NULL) {
|
||||||
images_free_tiles(tiles, id);
|
images_free_tiles(tiles, id);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remplissage des champs (coordonnées de découpe) */
|
||||||
strcpy(t->path, img_src.path);
|
strcpy(t->path, img_src.path);
|
||||||
t->width = tile_w;
|
t->width = tile_w;
|
||||||
t->height = tile_h;
|
t->height = tile_h;
|
||||||
@@ -63,6 +92,7 @@ tile_t **images_slice(image_source_t img_src, int rows, int cols)
|
|||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Libère la mémoire allouée par images_slice */
|
||||||
void images_free_tiles(tile_t **tiles, int num_tiles)
|
void images_free_tiles(tile_t **tiles, int num_tiles)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|||||||
+13
-2
@@ -1,24 +1,35 @@
|
|||||||
|
/* src/images.h
|
||||||
|
* Définition des structures pour la gestion et le découpage de l'image.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef IMAGES_H
|
#ifndef IMAGES_H
|
||||||
#define IMAGES_H
|
#define IMAGES_H
|
||||||
|
|
||||||
|
/* Une tuile est une partie de l'image source */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char path[30];
|
char path[30];
|
||||||
|
/* Coordonnées de la tuile DANS l'image source */
|
||||||
int src_x, src_y;
|
int src_x, src_y;
|
||||||
int width, height;
|
int width, height;
|
||||||
int id;
|
int id; /* ID unique, de 0 à (L*C - 2) */
|
||||||
} tile_t;
|
} tile_t;
|
||||||
|
|
||||||
|
/* Structure pour l'image source complète */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
char path[30];
|
char path[30];
|
||||||
} image_source_t;
|
} image_source_t;
|
||||||
|
|
||||||
|
/* Prototypes */
|
||||||
|
|
||||||
|
/* Obtient les informations de l'image choisie */
|
||||||
image_source_t get_image_source(int selected_index);
|
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);
|
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);
|
void images_free_tiles(tile_t **tiles, int num_tiles);
|
||||||
|
|
||||||
#endif
|
#endif /* IMAGES_H */
|
||||||
|
|||||||
+22
-7
@@ -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 "menu_view.h"
|
||||||
#include "game_logic.h"
|
#include "game_logic.h"
|
||||||
#include "images.h"
|
#include "images.h"
|
||||||
#include "game_view.h"
|
#include "game_view.h"
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include <graph.h>
|
#include <graph.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h> /* Pour exit() et srand() */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
menu_state_t menu_state;
|
menu_state_t menu_state;
|
||||||
unsigned long next_update;
|
unsigned long next_update;
|
||||||
|
|
||||||
|
/* 1. Initialisation de l'environnement IUT */
|
||||||
InitialiserGraphique();
|
InitialiserGraphique();
|
||||||
CreerFenetre(10, 10, WINDOW_WIDTH, WINDOW_HEIGHT);
|
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 {
|
do {
|
||||||
|
/* 2. Phase de Configuration (Menu) */
|
||||||
menu_init(&menu_state);
|
menu_init(&menu_state);
|
||||||
next_update = Microsecondes() + REFRESH_CYCLE;
|
next_update = Microsecondes() + REFRESH_CYCLE;
|
||||||
|
|
||||||
@@ -37,9 +44,11 @@ int main(void)
|
|||||||
}
|
}
|
||||||
menu_handle_key(&menu_state);
|
menu_handle_key(&menu_state);
|
||||||
|
|
||||||
|
/* Sortie anticipée du menu par ESC */
|
||||||
if (menu_state.ready_to_play == -1) goto end_program;
|
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) {
|
if (menu_state.ready_to_play == 1) {
|
||||||
|
|
||||||
image_source_t img_src;
|
image_source_t img_src;
|
||||||
@@ -47,12 +56,13 @@ int main(void)
|
|||||||
game_state_t game_state;
|
game_state_t game_state;
|
||||||
int num_tiles;
|
int num_tiles;
|
||||||
|
|
||||||
|
/* Préparation des données et initialisation du moteur de jeu */
|
||||||
img_src = get_image_source(menu_state.selected_image);
|
img_src = get_image_source(menu_state.selected_image);
|
||||||
tiles = images_slice(img_src, menu_state.rows, menu_state.cols);
|
tiles = images_slice(img_src, menu_state.rows, menu_state.cols);
|
||||||
|
|
||||||
if (tiles == NULL) {
|
if (tiles == NULL) {
|
||||||
fprintf(stderr, "Erreur: Allocation tuiles echouee. Retour au menu.\n");
|
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;
|
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",
|
printf("DEBUG: Jeu prêt. Grille %d x %d melangee. Lancement de la partie...\n",
|
||||||
game_state.rows, game_state.cols);
|
game_state.rows, game_state.cols);
|
||||||
|
|
||||||
|
/* Lancement de la BOUCLE DE JEU (bloque jusqu'à Victoire ou Retour Menu) */
|
||||||
game_loop(&game_state);
|
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);
|
images_free_tiles(tiles, num_tiles);
|
||||||
game_free(&game_state);
|
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();
|
FermerGraphique();
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-5
@@ -1,3 +1,7 @@
|
|||||||
|
/* src/menu_view.c
|
||||||
|
* Implémentation complète de l'interface de configuration (Menu)
|
||||||
|
*/
|
||||||
|
|
||||||
#include "menu_view.h"
|
#include "menu_view.h"
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include <graph.h>
|
#include <graph.h>
|
||||||
@@ -5,30 +9,37 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Constantes de positionnement */
|
||||||
#define IMG_PREVIEW_SIZE 150
|
#define IMG_PREVIEW_SIZE 150
|
||||||
#define IMG_START_X 50
|
#define IMG_START_X 250
|
||||||
#define IMG_START_Y 150
|
#define IMG_START_Y 150
|
||||||
#define IMG_SPACING 200
|
#define IMG_SPACING 200
|
||||||
#define BUTTON_SIZE 30
|
#define BUTTON_SIZE 30
|
||||||
#define DIM_START_Y 350
|
#define DIM_START_Y 350
|
||||||
|
|
||||||
|
/* Variables globales de la bibliothèque IUT pour la souris */
|
||||||
extern int _X;
|
extern int _X;
|
||||||
extern int _Y;
|
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 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_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);
|
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)
|
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->rows = MIN_DIMENSION;
|
||||||
state->cols = MIN_DIMENSION;
|
state->cols = MIN_DIMENSION;
|
||||||
state->ready_to_play = 0;
|
state->ready_to_play = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dessine l'écran de configuration du menu */
|
||||||
void menu_render(const menu_state_t *state)
|
void menu_render(const menu_state_t *state)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -40,22 +51,35 @@ void menu_render(const menu_state_t *state)
|
|||||||
char buffer[50];
|
char buffer[50];
|
||||||
|
|
||||||
char path_buffer[30];
|
char path_buffer[30];
|
||||||
const char *path_fixed = "assets/spiderman.jpg"; /* FIXE LE CHEMIN */
|
|
||||||
|
|
||||||
EffacerEcran(C_FOND);
|
EffacerEcran(C_FOND);
|
||||||
ChoisirCouleurDessin(C_TEXTE);
|
ChoisirCouleurDessin(C_TEXTE);
|
||||||
|
|
||||||
|
/* 1. Titre et Section Image */
|
||||||
EcrireTexte(IMG_START_X, 50, "CONFIGURATION DU JEU TAQUIN", 2);
|
EcrireTexte(IMG_START_X, 50, "CONFIGURATION DU JEU TAQUIN", 2);
|
||||||
EcrireTexte(IMG_START_X, 100, "1. Choisir l'image :", 1);
|
EcrireTexte(IMG_START_X, 100, "1. Choisir l'image :", 1);
|
||||||
|
|
||||||
|
/* BOUCLE : Affichage des 3 aperçus */
|
||||||
for (i = 0; i < NUM_IMAGES; i++) {
|
for (i = 0; i < NUM_IMAGES; i++) {
|
||||||
int x = IMG_START_X + i * IMG_SPACING;
|
int x = IMG_START_X + i * IMG_SPACING;
|
||||||
int y = IMG_START_Y;
|
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) {
|
if (state->selected_image == i) {
|
||||||
ChoisirCouleurDessin(C_SELECTION);
|
ChoisirCouleurDessin(C_SELECTION);
|
||||||
DessinerRectangle(x - 5, y - 5, IMG_PREVIEW_SIZE + 10, IMG_PREVIEW_SIZE + 10);
|
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);
|
ChoisirCouleurDessin(C_TEXTE);
|
||||||
|
|
||||||
|
/* 2. Section Dimensions */
|
||||||
int dim_section_x = IMG_START_X;
|
int dim_section_x = IMG_START_X;
|
||||||
int button_l_offset = 180;
|
int button_l_offset = 180;
|
||||||
int button_c_offset = 400;
|
int button_c_offset = 400;
|
||||||
|
|
||||||
EcrireTexte(dim_section_x, DIM_START_Y, "2. Choisir les dimensions (3x3 a 8x8) :", 1);
|
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);
|
EcrireTexte(dim_section_x, DIM_START_Y + 40, "Lignes :", 0);
|
||||||
sprintf(buffer, "%d", state->rows);
|
sprintf(buffer, "%d", state->rows);
|
||||||
EcrireTexte(dim_section_x + 100, DIM_START_Y + 40, buffer, 0);
|
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);
|
EcrireTexte(dim_section_x + 250, DIM_START_Y + 40, "Colonnes :", 0);
|
||||||
sprintf(buffer, "%d", state->cols);
|
sprintf(buffer, "%d", state->cols);
|
||||||
EcrireTexte(dim_section_x + 350, DIM_START_Y + 40, buffer, 0);
|
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, DIM_START_Y + 30, "+", C_BOUTON);
|
||||||
draw_button(dim_section_x + button_l_offset + 40, 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, DIM_START_Y + 30, "+", C_BOUTON);
|
||||||
draw_button(dim_section_x + button_c_offset + 40, 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);
|
ChoisirCouleurDessin(C_START);
|
||||||
RemplirRectangle(300, 500, 200, 50);
|
RemplirRectangle(300, 500, 200, 50);
|
||||||
ChoisirCouleurDessin(CouleurParNom("white"));
|
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)
|
void menu_handle_mouse_click(menu_state_t *state)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -101,6 +132,7 @@ void menu_handle_mouse_click(menu_state_t *state)
|
|||||||
int button_l_offset = 180;
|
int button_l_offset = 180;
|
||||||
int button_c_offset = 400;
|
int button_c_offset = 400;
|
||||||
|
|
||||||
|
/* 1. Clic sur les images */
|
||||||
for (i = 0; i < NUM_IMAGES; i++) {
|
for (i = 0; i < NUM_IMAGES; i++) {
|
||||||
int x_img = IMG_START_X + i * IMG_SPACING;
|
int x_img = IMG_START_X + i * IMG_SPACING;
|
||||||
int y_img = IMG_START_Y;
|
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 (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++;
|
if (state->rows < MAX_DIMENSION) state->rows++;
|
||||||
return;
|
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 (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--;
|
if (state->rows > MIN_DIMENSION) state->rows--;
|
||||||
return;
|
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 (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++;
|
if (state->cols < MAX_DIMENSION) state->cols++;
|
||||||
return;
|
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 (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--;
|
if (state->cols > MIN_DIMENSION) state->cols--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 4. Clic sur DÉMARRER */
|
||||||
if (is_in_rect(x_click, y_click, 300, 500, 200, 50)) {
|
if (is_in_rect(x_click, y_click, 300, 500, 200, 50)) {
|
||||||
state->ready_to_play = 1;
|
state->ready_to_play = 1;
|
||||||
return;
|
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)
|
void menu_handle_key(menu_state_t *state)
|
||||||
{
|
{
|
||||||
if (ToucheEnAttente()) {
|
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)
|
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);
|
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)
|
static void draw_button(int x, int y, const char *text, couleur C_BG)
|
||||||
{
|
{
|
||||||
ChoisirCouleurDessin(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);
|
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)
|
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_w = TailleChaineEcran((char*)text, size);
|
||||||
int text_h_sup = TailleSupPolice(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_x = x + (w - text_w) / 2;
|
||||||
int draw_y = y + (h + text_h_sup) / 2;
|
int draw_y = y + (h + text_h_sup) / 2;
|
||||||
|
|
||||||
|
|||||||
+12
-5
@@ -1,20 +1,27 @@
|
|||||||
|
/* src/menu_view.h
|
||||||
|
* Définition de l'état du menu et des constantes.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef MENU_VIEW_H
|
#ifndef MENU_VIEW_H
|
||||||
#define MENU_VIEW_H
|
#define MENU_VIEW_H
|
||||||
|
|
||||||
|
/* Constantes de dimension de la grille */
|
||||||
#define NUM_IMAGES 3
|
#define NUM_IMAGES 3
|
||||||
#define MIN_DIMENSION 3
|
#define MIN_DIMENSION 3
|
||||||
#define MAX_DIMENSION 8
|
#define MAX_DIMENSION 8
|
||||||
|
|
||||||
|
/* Structure de l'état du menu */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int selected_image;
|
int selected_image; /* Index de l'image (0, 1, ou 2) */
|
||||||
int rows;
|
int rows; /* Nombre de lignes (3 à 8) */
|
||||||
int cols;
|
int cols; /* Nombre de colonnes (3 à 8) */
|
||||||
int ready_to_play;
|
int ready_to_play; /* 1 si le joueur a validé, -1 si demande de quitter */
|
||||||
} menu_state_t;
|
} menu_state_t;
|
||||||
|
|
||||||
|
/* Prototypes des fonctions publiques */
|
||||||
void menu_init(menu_state_t *state);
|
void menu_init(menu_state_t *state);
|
||||||
void menu_render(const menu_state_t *state);
|
void menu_render(const menu_state_t *state);
|
||||||
void menu_handle_mouse_click(menu_state_t *state);
|
void menu_handle_mouse_click(menu_state_t *state);
|
||||||
void menu_handle_key(menu_state_t *state);
|
void menu_handle_key(menu_state_t *state);
|
||||||
|
|
||||||
#endif
|
#endif /* MENU_VIEW_H */
|
||||||
|
|||||||
+13
@@ -1,6 +1,14 @@
|
|||||||
|
/* src/utils.c
|
||||||
|
* Implémentation des fonctions utilitaires (valeur absolue et aléatoire).
|
||||||
|
*/
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <stdlib.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)
|
int abs_val(int n)
|
||||||
{
|
{
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
@@ -9,8 +17,13 @@ int abs_val(int n)
|
|||||||
return 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)
|
int random_int(int max)
|
||||||
{
|
{
|
||||||
if (max <= 0) return 0;
|
if (max <= 0) return 0;
|
||||||
|
/* La fonction srand() est appelée une seule fois dans main.c. */
|
||||||
return rand() % max;
|
return rand() % max;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -1,8 +1,14 @@
|
|||||||
|
/* src/utils.h
|
||||||
|
* Déclarations des fonctions utilitaires de base.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
|
/* Renvoie la valeur absolue d'un entier. */
|
||||||
int abs_val(int n);
|
int abs_val(int n);
|
||||||
|
|
||||||
|
/* Renvoie un entier pseudo-aléatoire entre 0 et max-1. */
|
||||||
int random_int(int max);
|
int random_int(int max);
|
||||||
|
|
||||||
#endif
|
#endif /* UTILS_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user