Pour continuer, voici un petit topic d'exercice : le démineur !
Ce jeu va générer un terrain miné (>0 : sur, -1 : mine) et va demander au joueur de le déminer.
Lorsqu'un joueur découvre une case non piégée, si aucune mine n'est à côté, les cases autour sont découvertes, sinon, la case indique le nombre de mines à côté (diagonales et côtés)
On joue tant que l'on a pas marqué toutes les mines et tant que nous avons pas découvert une case minée.
Les niveaux du topic vous proposent de créer le jeu par étape
Niveau 1 : Generation et affichage d'une grille
Notion mise à l'épreuve : Les tableaux et l'aléatoire en C
Vous allez fixer le nombre de cases maximales en hauteur et largeur.
Le programme va devoir générer la grille en demandant à l'utilisateur :
La hauteur de la grille
La largeur de la grille
Le nombre de mines
Ici : on ne joue pas, on va juste afficher la grille.
Hint : Vous pouvez inscrire directement dans le tableau le nombre de mines aux alentours.
le plus : Vous pouvez allouer dynamiquement le tableau (et donc enlever le maximum)
(Notion + : l'allocation dynamique)
le plus plus : Vous pouvez ajouter de la couleur, pour rendre plus classe
(Notion ++ : la gestion de la console et du terminal en C, et les macros pour rendre votre code portable)
Ce niveau vous propose de créer le jeu en soi :
Le plateau est caché, le jeu vous propose d'entrer des coordonées et demande si vous voulez découvrir ou marquer la case correspondante.
Hint : Vous pouvez tricher pour débugger !
Le plus : Vous pouvez gérer le temps de choix
(Notion + : La gestion du temps en C)
Le plus plus : Au lieu de demander, vous pouvez gerer le clavier afin de se déplacer sur la grille et choisir
(Notion ++ : La gestion de la console et du terminal; et les macros pour rendre portable votre code)
Le plus : Vous pouvez, au lieu d'y inscrire le contenu de deux tableau, une sorte de format compressé n'indiquant que le strict minimum.
(Objet + : Votre cerveau)
Exemple :
6 8 4 1 1 4 2 1 4 5 1 2 5 8 6 5 4
Le plus plus : ne pas compresser mais gérer le fichier en binaire
(Notion ++ : Les fichiers, en mode binaire)
Vous pouvez utiliser la SDL pour rendre le jeu un peu plus beau.
(Vous pourrez alors vous amuser à faire des animations, si vous le voulez;
Jouer des sons, et inscrire le score (temps))
Des liste ?
J'avais utiliser les matrice, mais ca avait la facheuse habitude de defoncer celle utiliser par le jeu sans retour possible des valeurs (enfin, a moins de faire encore plein de developpement).
Par contre, c'était en philosophie/Français que je codais :x (les p'tit jeune, prenez pas exemple de ça !)
Bon, on va finir le petit HS, sinon j'ai pas finit d'en parler. En tous cas, c'est largement faisable et assez fendar a faire
Des listes ?
J'avais utilisér les matrices, mais ça avait la fâcheuse habitude de défoncer celle utilisée par le jeu sans retour possible des valeurs (enfin, à moins de faire encore plein de développements).
Par contre, c'était en philosophie/Français que je codais :x (les p'tits jeunes, prenez pas exemple de ça !)
Bon, on va finir le petit HS, sinon j'ai pas finit d'en parler. En tous cas, c'est largement faisable et assez fendar à faire
J’espère que cet exo aura vent en poupe.
Bon courage a tous.
Bon, pour ma part : niveau 1 fini
Je vais ajouter la couleur
J'aime pas trop les « plus plus » en général. On n'arrivera jamais à un truc 100% portable avec des compilations conditionnelles.
Citation
J'avais utiliser les matrice, mais ca avait la facheuse habitude de defoncer celle utiliser par le jeu sans retour possible des valeurs (enfin, a moins de faire encore plein de developpement).
Par contre, c'était en philosophie/Français que je codais :x (les p'tit jeune, prenez pas exemple de ça !)
Tiens, je ne me sens plus seul.
Bon, vu que personne n'a posté de code pour le moment, je poste le mien pour le niveau 1 (sans le « plus plus ») :
grid.c :
#include <stdio.h>
#include <stdlib.h>
#include "grid.h"
/* ----- Gère les voisins ----- */
static void countNeighbours (grid_s * p_grid, int x, int y)
{
if (y > 0 && p_grid->pp_grid[y - 1][x].value != -1)
++p_grid->pp_grid[y - 1][x].value;
if (y > 0 && x > 0 && p_grid->pp_grid[y - 1][x - 1].value != -1)
++p_grid->pp_grid[y - 1][x - 1].value;
if (y > 0 && x < p_grid->width - 1
&& p_grid->pp_grid[y - 1][x + 1].value != -1)
++p_grid->pp_grid[y - 1][x + 1].value;
if (x > 0 && p_grid->pp_grid[y][x - 1].value != -1)
++p_grid->pp_grid[y][x - 1].value;
if (x > 0 && y < p_grid->hight - 1
&& p_grid->pp_grid[y + 1][x - 1].value != -1)
++p_grid->pp_grid[y + 1][x - 1].value;
if (x < p_grid->width - 1 && p_grid->pp_grid[y][x + 1].value != -1)
++p_grid->pp_grid[y][x + 1].value;
if (x < p_grid->width - 1 && y < p_grid->hight - 1
&& p_grid->pp_grid[y + 1][x + 1].value != -1)
++p_grid->pp_grid[y + 1][x + 1].value;
if (y < p_grid->hight - 1 && p_grid->pp_grid[y + 1][x].value != -1)
++p_grid->pp_grid[y + 1][x].value;
}
/* ----- Affiche une ligne pour la grille ----- */
static void printLine (unsigned int width)
{
printf ("\n ");
for (unsigned int i = 0; i < width; ++i)
printf ("+---");
putchar ('+');
}
/* ----- Génère une grille ----- */
grid_s *generateGrid (void)
{
grid_s *p_grid = malloc (sizeof *p_grid);
if (p_grid == NULL)
goto error;
printf ("Paramètres :\n\t- lignes : ");
scanf ("%u", &p_grid->hight);
printf ("\t- colonnes : ");
scanf ("%u", &p_grid->width);
printf ("\t- mines : ");
scanf ("%u", &p_grid->mines);
p_grid->pp_grid = malloc (sizeof *p_grid->pp_grid * p_grid->hight);
if (p_grid->pp_grid == NULL)
goto error;
for (unsigned int i = 0; i < p_grid->hight; ++i)
{
p_grid->pp_grid[i] =
malloc (sizeof *p_grid->pp_grid[i] * p_grid->width);
if (p_grid->pp_grid[i] == NULL)
goto error;
for (unsigned int j = 0; j < p_grid->width; ++j)
{
#ifdef DEBUG
p_grid->pp_grid[i][j].isVisible = VISIBLE;
#else
p_grid->pp_grid[i][j].isVisible = HIDE;
#endif
p_grid->pp_grid[i][j].value = 0;
}
}
for (unsigned int i = 0; i < p_grid->mines; ++i)
{
int x = rand () % p_grid->width;
int y = rand () % p_grid->hight;
if (p_grid->pp_grid[y][x].value != -1)
{
p_grid->pp_grid[y][x].value = -1;
countNeighbours (p_grid, x, y);
}
else
--i;
}
return p_grid;
error:
deleteGrid (&p_grid);
return NULL;
}
/* ----- Détruit une grille ----- */
void deleteGrid (grid_s ** pp_grid)
{
if (pp_grid != NULL)
{
for (unsigned int i = 0; i < (*pp_grid)->hight; ++i)
free ((*pp_grid)->pp_grid[i]);
free ((*pp_grid)->pp_grid);
free (*pp_grid);
}
}
/* ----- Affiche une grille ----- */
void printGrid (grid_s * p_grid)
{
if (p_grid != NULL && p_grid->pp_grid != NULL)
{
printf ("\n ");
for (unsigned int i = 1; i <= p_grid->width; ++i)
printf (" %d", i);
printLine (p_grid->width);
for (unsigned int i = 0; i < p_grid->hight; ++i)
{
printf (i < 9 ? "\n %d | " : "\n %d | ", i + 1);
for (unsigned int j = 0; j < p_grid->width; ++j)
{
if (p_grid->pp_grid[i][j].isVisible == VISIBLE)
switch (p_grid->pp_grid[i][j].value)
{
case -1:
putchar ('x');
break;
case 0:
putchar (' ');
break;
default:
printf ("%d", p_grid->pp_grid[i][j].value);
break;
}
else
putchar ('?');
printf (" | ");
}
printLine (p_grid->width);
}
}
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define VIDE 0
#define BOMBE -1
typedef struct
{
int **tab;
int larg;
int haut;
int nb_mine;
}Dem; /*Structure du Demineur*/
void init(Dem *dem);
void options(Dem *dem);
int get_param(const char *s);
void affiche(Dem *dem);
void generer_tab(Dem *dem);
void cases(Dem *dem, int x, int y);
void liberer(Dem *dem);
int main(void)
{
Dem dem;
srand((unsigned int)time(NULL));
printf("** zDemineur (niveau 1) **\n\n");
printf("Ce programme genere une grille de demineur !\n\n");
init(&dem);
options(&dem);
generer_tab(&dem);
affiche(&dem);
liberer(&dem);
return EXIT_SUCCESS;
}
void init(Dem *dem)
{
dem->larg = 0;
dem->haut = 0;
dem->nb_mine = 0;
dem->tab = NULL;
}
void liberer(Dem *dem)
{
int i;
/*Liberation du tablea 2D*/
for (i = 0; i < dem->larg; i++)
{
free(dem->tab[i]);
dem->tab[i] = NULL;
}
free(dem->tab);
dem->tab = NULL;
}
void options(Dem *dem)
{
int i;
printf("Parametres :\n");
dem->larg = get_param("lignes");
dem->haut = get_param("colonnes");
do
{
dem->nb_mine = get_param("mine");
}while(dem->nb_mine > dem->larg * dem->haut);
/*Allocation dynamique d'un tableau 2D*/
dem->tab = malloc((size_t)dem->larg * sizeof(*dem->tab));
if(dem->tab == NULL)
{
printf("Erreur malloc !");
exit(EXIT_FAILURE);
}
for(i = 0; i < dem->larg; i++)
{
dem->tab[i] = malloc((size_t)dem->haut * sizeof(*dem->tab[i]));
if(dem->tab[i] == NULL)
{
printf("Erreur malloc !");
exit(EXIT_FAILURE);
}
}
}
/*Economise quelques lignes en verifiant vite-fait l'entree*/
int get_param(const char *s)
{
int n = 0;
do
{
printf("\t --> %s : ", s);
scanf("%d", &n);
}while(n <= 0);
return n;
}
void generer_tab(Dem *dem)
{
int x, y;
int booleen = 1;
int i, j;
/*On le replit de "VIDE"*/
for(i = 0; i < dem->larg; i++)
{
for(j = 0; j < dem->haut; j++)
{
dem->tab[i][j] = VIDE;
}
}
/*On ajoute les bombes*/
for(i = 0; i < dem->nb_mine; i++)
{
do
{
booleen = 1;
x = rand() % dem->larg;
y = rand() % dem->haut;
if(dem->tab[x][y] != VIDE)
{
booleen = 0;
}
else
{
dem->tab[x][y] = BOMBE;
}
}while(!booleen);
/*Le tout en comptant les cases voisines*/
cases(dem, x, y);
}
}
/*Compte les cases voisines*/
void cases(Dem *dem, int x, int y)
{
if(x + 1 < dem->larg && dem->tab[x + 1][y] != BOMBE)
dem->tab[x + 1][y]++;
if(x - 1 >= 0 && dem->tab[x - 1][y] != BOMBE)
dem->tab[x - 1][y]++;
if(y + 1 < dem->haut && dem->tab[x][y + 1] != BOMBE)
dem->tab[x][y + 1]++;
if(y - 1 >= 0 && dem->tab[x][y - 1] != BOMBE)
dem->tab[x][y - 1]++;
if(x + 1 < dem->larg && y + 1 < dem->haut &&
dem->tab[x + 1][y + 1] != BOMBE)
dem->tab[x + 1][y + 1]++;
if(x - 1 >= 0 && y - 1 >= 0 && dem->tab[x - 1][y - 1] != BOMBE)
dem->tab[x - 1][y - 1]++;
if(x + 1 < dem->larg && y - 1 >= 0 && dem->tab[x + 1][y - 1] != BOMBE)
dem->tab[x + 1][y - 1]++;
if(x - 1 >= 0 && y + 1 < dem->haut && dem->tab[x - 1][y + 1] != BOMBE)
dem->tab[x - 1][y + 1]++;
}
void affiche(Dem *dem)
{
int i, j;
for(i = 0; i < dem->larg; i++)
{
for(j = 0; j < dem->haut; j++)
{
if(dem->tab[i][j] == VIDE)
printf("- ");
else if(dem->tab[i][j] == BOMBE)
printf("X ");
else
printf("%d ", dem->tab[i][j]);
}
printf("\n\n");
}
}
@lucas-84 : Ah le C99...
grid.c: In function 'countNeighbours':
grid.c:12:19: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
grid.c:17:19: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
grid.c:20:10: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
grid.c:22:10: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
grid.c:22:35: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
grid.c:25:10: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
grid.c: In function 'generateGrid':
grid.c:79:20: warning: conversion to 'unsigned int' from 'int' may change the sign of the result [-Wsign-conversion]
grid.c:79:23: warning: conversion to 'int' from 'unsigned int' may change the sign of the result [-Wsign-conversion]
grid.c:80:20: warning: conversion to 'unsigned int' from 'int' may change the sign of the result [-Wsign-conversion]
grid.c:80:23: warning: conversion to 'int' from 'unsigned int' may change the sign of the result [-Wsign-conversion]
main.c: In function 'main':
main.c:8:16: warning: conversion to 'unsigned int' from 'time_t' may alter its value [-Wconversion]
Sinon c'est un très belle présentation que tu nous a fait là.
Attend t'as pas encore vu avec les couleurs. (si il y a un windowsien parmi nous, pourrait-t-il confirmer que cela marche chez lui, vu que j'ai fait au feeling là...)
Bon j'ai corrigé tes warnings normalement.
main.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "grid.h"
#include "color.h"
int main (void)
{
grid_s *p;
srand ((unsigned int) time (NULL));
clear ();
puts ("** zDemineur (niveau 2) **\nRegles : Vous voila en face d'un terrain"
" qu'il vous faut déminer. Pour ce faire, vous allez devoir poser des"
" drapeaux sur chaque mine. Chaque case découverte vous indiquant le "
"nombre de mines adjacentes. Si vous tentez de découvrir une case min"
"ée, vous perdez.");
p = generateGrid ();
printGrid (p);
deleteGrid (&p);
return 0;
}
grid.c
#include <stdio.h>
#include <stdlib.h>
#include "color.h"
#include "grid.h"
/* ----- Gère les voisins ----- */
static void countNeighbours (grid_s * p_grid, int x, int y)
{
if (y > 0 && p_grid->pp_grid[y - 1][x].value != -1)
++p_grid->pp_grid[y - 1][x].value;
if (y > 0 && x > 0 && p_grid->pp_grid[y - 1][x - 1].value != -1)
++p_grid->pp_grid[y - 1][x - 1].value;
if (y > 0 && x < p_grid->width - 1
&& p_grid->pp_grid[y - 1][x + 1].value != -1)
++p_grid->pp_grid[y - 1][x + 1].value;
if (x > 0 && p_grid->pp_grid[y][x - 1].value != -1)
++p_grid->pp_grid[y][x - 1].value;
if (x > 0 && y < p_grid->hight - 1
&& p_grid->pp_grid[y + 1][x - 1].value != -1)
++p_grid->pp_grid[y + 1][x - 1].value;
if (x < p_grid->width - 1 && p_grid->pp_grid[y][x + 1].value != -1)
++p_grid->pp_grid[y][x + 1].value;
if (x < p_grid->width - 1 && y < p_grid->hight - 1
&& p_grid->pp_grid[y + 1][x + 1].value != -1)
++p_grid->pp_grid[y + 1][x + 1].value;
if (y < p_grid->hight - 1 && p_grid->pp_grid[y + 1][x].value != -1)
++p_grid->pp_grid[y + 1][x].value;
}
/* ----- Affiche une ligne pour la grille ----- */
static void printLine (int width)
{
printf ("\n ");
for (int i = 0; i < width; ++i)
printf ("+---");
putchar ('+');
}
/* ----- Génère une grille ----- */
grid_s *generateGrid (void)
{
grid_s *p_grid = malloc (sizeof *p_grid);
if (p_grid == NULL)
goto error;
printf ("Paramètres :\n\t- lignes : ");
scanf ("%u", &p_grid->hight);
printf ("\t- colonnes : ");
scanf ("%u", &p_grid->width);
printf ("\t- mines : ");
scanf ("%u", &p_grid->mines);
p_grid->pp_grid = malloc (sizeof *p_grid->pp_grid * (size_t) p_grid->hight);
if (p_grid->pp_grid == NULL)
goto error;
for (int i = 0; i < p_grid->hight; ++i)
{
p_grid->pp_grid[i] =
malloc (sizeof *p_grid->pp_grid[i] * (size_t) p_grid->width);
if (p_grid->pp_grid[i] == NULL)
goto error;
for (int j = 0; j < p_grid->width; ++j)
{
#ifdef DEBUG
p_grid->pp_grid[i][j].isVisible = VISIBLE;
#else
p_grid->pp_grid[i][j].isVisible = HIDE;
#endif
p_grid->pp_grid[i][j].value = 0;
}
}
for (int i = 0; i < p_grid->mines; ++i)
{
int x = rand () % p_grid->width;
int y = rand () % p_grid->hight;
if (p_grid->pp_grid[y][x].value != -1)
{
p_grid->pp_grid[y][x].value = -1;
countNeighbours (p_grid, x, y);
}
else
--i;
}
return p_grid;
error:
deleteGrid (&p_grid);
return NULL;
}
/* ----- Détruit une grille ----- */
void deleteGrid (grid_s ** pp_grid)
{
if (pp_grid != NULL)
{
for (int i = 0; i < (*pp_grid)->hight; ++i)
free ((*pp_grid)->pp_grid[i]);
free ((*pp_grid)->pp_grid);
free (*pp_grid);
}
}
/* ----- Affiche une grille ----- */
void printGrid (grid_s * p_grid)
{
if (p_grid != NULL && p_grid->pp_grid != NULL)
{
putchar('\n');
color (GREEN_FG, WHITE_BG);
printf (" ");
for (int i = 1; i <= p_grid->width; ++i)
printf (" %d ", i);
color (BLACK_FG, WHITE_BG);
printLine (p_grid->width);
for (int i = 0; i < p_grid->hight; ++i)
{
color (GREEN_FG, WHITE_BG);
printf (i < 9 ? "\n %d" : "\n %d", i + 1);
color (BLACK_FG, WHITE_BG);
printf (" |");
for (int j = 0; j < p_grid->width; ++j)
{
if (p_grid->pp_grid[i][j].isVisible == VISIBLE)
switch (p_grid->pp_grid[i][j].value)
{
case -1:
color (BLACK_FG, RED_BG);
printf (" x ");
break;
case 0:
printf (" ");
break;
default:
if (p_grid->pp_grid[i][j].value == 1)
color (YELLOW_FG, WHITE_BG);
else if (p_grid->pp_grid[i][j].value == 2)
color (CYAN_FG, WHITE_BG);
else
color (BLUE_FG, WHITE_BG);
printf (" %d ", p_grid->pp_grid[i][j].value);
break;
}
else
{
color (WHITE_FG, WHITE_BG);
printf (" ? ");
}
color (BLACK_FG, WHITE_BG);
printf ("|");
}
printLine (p_grid->width);
}
}
putchar('\n');
color (DEFAULT, DEFAULT);
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "game.h"
#include "grid.h"
#include "color.h"
int main (void)
{
grid_s *p_grid;
stats_s *p_stats;
srand ((unsigned int) time (NULL));
clear ();
puts
("** zDemineur (niveau 2) **\n\nRègles : Vous voilà en face d'un terra"
"in qu'il vous faut déminer. Pour ce faire, vous allez devoir poser d"
"es drapeaux sur chaque mine. Chaque case découverte vous indiquant l"
"e nombre de mines adjacentes. Si vous tentez de découvrir une case m"
"inée, vous perdez.\n\n");
p_grid = generateGrid ();
p_stats = play (p_grid);
if (p_stats->isWinner == WIN)
printf ("Bravo - Vous avez mis %.0f secondes.\n", p_stats->t);
else
printf ("*BOUM* Dommage - Une prochaine fois peut-être ?\n");
deleteGrid (&p_grid);
return EXIT_SUCCESS;;
}
grid.c
#include <stdio.h>
#include <stdlib.h>
#include "color.h"
#include "game.h"
#include "grid.h"
/* ----- Gère les voisins ----- */
static void countNeighbours (grid_s * p_grid, int x, int y)
{
if (y > 0 && p_grid->pp_grid[y - 1][x].value != -1)
++p_grid->pp_grid[y - 1][x].value;
if (y > 0 && x > 0 && p_grid->pp_grid[y - 1][x - 1].value != -1)
++p_grid->pp_grid[y - 1][x - 1].value;
if (y > 0 && x < p_grid->width - 1
&& p_grid->pp_grid[y - 1][x + 1].value != -1)
++p_grid->pp_grid[y - 1][x + 1].value;
if (x > 0 && p_grid->pp_grid[y][x - 1].value != -1)
++p_grid->pp_grid[y][x - 1].value;
if (x > 0 && y < p_grid->hight - 1
&& p_grid->pp_grid[y + 1][x - 1].value != -1)
++p_grid->pp_grid[y + 1][x - 1].value;
if (x < p_grid->width - 1 && p_grid->pp_grid[y][x + 1].value != -1)
++p_grid->pp_grid[y][x + 1].value;
if (x < p_grid->width - 1 && y < p_grid->hight - 1
&& p_grid->pp_grid[y + 1][x + 1].value != -1)
++p_grid->pp_grid[y + 1][x + 1].value;
if (y < p_grid->hight - 1 && p_grid->pp_grid[y + 1][x].value != -1)
++p_grid->pp_grid[y + 1][x].value;
}
/* ----- Affiche une ligne pour la grille ----- */
static void printLine (int width)
{
printf ("\n ");
for (int i = 0; i < width; ++i)
printf ("+---");
putchar ('+');
}
/* ----- Génère une grille ----- */
grid_s *generateGrid (void)
{
grid_s *p_grid = malloc (sizeof *p_grid);
if (p_grid == NULL)
goto error;
printf ("Paramètres :\n\t- lignes : ");
while (scanf ("%u", &p_grid->hight) != 1)
cleanStdin ();
printf ("\t- colonnes : ");
while (scanf ("%u", &p_grid->width) != 1)
cleanStdin ();
printf ("\t- mines : ");
while (scanf ("%u", &p_grid->mines) != 1)
cleanStdin ();
p_grid->pp_grid =
malloc (sizeof *p_grid->pp_grid * (size_t) p_grid->hight);
if (p_grid->pp_grid == NULL)
goto error;
for (int i = 0; i < p_grid->hight; ++i)
{
p_grid->pp_grid[i] =
malloc (sizeof *p_grid->pp_grid[i] * (size_t) p_grid->width);
if (p_grid->pp_grid[i] == NULL)
goto error;
for (int j = 0; j < p_grid->width; ++j)
{
#ifdef DEBUG
p_grid->pp_grid[i][j].isVisible = VISIBLE;
#else
p_grid->pp_grid[i][j].isVisible = HIDE;
#endif
p_grid->pp_grid[i][j].value = 0;
}
}
for (int i = 0; i < p_grid->mines; ++i)
{
int x = rand () % p_grid->width;
int y = rand () % p_grid->hight;
if (p_grid->pp_grid[y][x].value != -1)
{
p_grid->pp_grid[y][x].value = -1;
countNeighbours (p_grid, x, y);
}
else
--i;
}
return p_grid;
error:
deleteGrid (&p_grid);
return NULL;
}
/* ----- Détruit une grille ----- */
void deleteGrid (grid_s ** pp_grid)
{
if (pp_grid != NULL)
{
for (int i = 0; i < (*pp_grid)->hight; ++i)
free ((*pp_grid)->pp_grid[i]);
free ((*pp_grid)->pp_grid);
free (*pp_grid);
}
}
/* ----- Affiche une grille ----- */
void printGrid (grid_s * p_grid)
{
if (p_grid != NULL && p_grid->pp_grid != NULL)
{
putchar ('\n');
color (GREEN_FG);
printf (" ");
for (int i = 1; i <= p_grid->width; ++i)
printf (" %d ", i);
color (BLACK_FG);
printLine (p_grid->width);
for (int i = 0; i < p_grid->hight; ++i)
{
color (GREEN_FG);
printf (i < 9 ? "\n %d" : "\n %d", i + 1);
color (BLACK_FG);
printf (" |");
for (int j = 0; j < p_grid->width; ++j)
{
if (p_grid->pp_grid[i][j].isVisible == VISIBLE)
switch (p_grid->pp_grid[i][j].value)
{
case -1:
color (RED_FG);
printf (" x ");
break;
case 0:
printf (" ");
break;
default:
if (p_grid->pp_grid[i][j].value == 1)
color (YELLOW_FG);
else if (p_grid->pp_grid[i][j].value == 2)
color (CYAN_FG);
else
color (BLUE_FG);
printf (" %d ", p_grid->pp_grid[i][j].value);
break;
}
else if (p_grid->pp_grid[i][j].isVisible == FLAG)
printf (" P ");
else
{
color (WHITE_FG);
printf (" ? ");
}
color (BLACK_FG);
printf ("|");
}
printLine (p_grid->width);
}
}
putchar ('\n');
color (DEFAULT);
}
grid.h
#ifndef H_LP_GRID_20120314134737
#define H_LP_GRID_20120314134737
typedef enum
{
VISIBLE,
HIDE,
FLAG
} visible_e;
typedef struct
{
visible_e isVisible;
int value;
} case_s;
typedef struct
{
int hight;
int width;
int mines;
case_s **pp_grid;
} grid_s;
grid_s *generateGrid (void);
void deleteGrid (grid_s ** pp_grid);
void printGrid (grid_s * p_grid);
#endif
game.c
#include <stdio.h>
#include <time.h>
#include "color.h"
#include "game.h"
#include "grid.h"
/* ----- Demande les coordonnées à l'utilisateur ----- */
static void askCoor (grid_s * p_grid, int *x, int *y)
{
puts ("Quelle case ?");
do
{
printf ("\t- y : ");
while (scanf ("%d", y) != 1)
cleanStdin ();
}
while (*y < 0 && *y > p_grid->hight);
do
{
printf ("\t- x : ");
while (scanf ("%d", x) != 1)
cleanStdin ();
}
while (*x < 0 && *x > p_grid->width);
}
/* ----- Teste la fin du jeu ----- */
static userState_e testWin (grid_s * p_grid, stats_s * p_stats, int mines)
{
userState_e ret = WIN;
if (p_stats->isWinner == LOST)
{
ret = -1;
goto end;
}
for (int i = 0; i < p_grid->hight; ++i)
{
for (int j = 0; j < p_grid->width; ++j)
{
if (p_grid->pp_grid[i][j].value == -1
&& p_grid->pp_grid[i][j].isVisible != FLAG)
{
ret = NONE;
goto end;
}
}
}
end:
return ret;
}
/* ----- Vide le flux d'entrée standard ----- */
void cleanStdin (void)
{
int c;
while ((c = getchar ()) != '\n' && c != EOF);
}
/* ----- Fonction de jeu ----- */
stats_s *play (grid_s * p_grid)
{
stats_s *p_stats = malloc (sizeof *p_stats);
if (p_grid != NULL && p_grid->pp_grid != NULL && p_stats != NULL)
{
time_t t;
int mines = p_grid->mines;
int choice;
p_stats->isWinner = NONE;
time (&t);
do
{
clear ();
printGrid (p_grid);
printf
("Il devrait rester %d/%d mines.\n\nQue faire :\n\t- (0) Abandonne"
"r\n\t- (1) Poser un drapeau\n\t- (2) Découvrir une case\nChoix :"
" ", mines, p_grid->mines);
while (scanf ("%d", &choice) != 1)
cleanStdin ();
if (choice == 1 || choice == 2)
{
int x, y;
askCoor (p_grid, &x, &y);
if (choice == 1)
{
p_grid->pp_grid[y - 1][x - 1].isVisible = FLAG;
--mines;
}
else
{
if (p_grid->pp_grid[y - 1][x - 1].value == -1)
p_stats->isWinner = LOST;
p_grid->pp_grid[y - 1][x - 1].isVisible = VISIBLE;
}
}
p_stats->isWinner = testWin (p_grid, p_stats, mines);
}
while (choice != 0 && p_stats->isWinner == NONE);
clear ();
printGrid (p_grid);
p_stats->t = difftime (time (NULL), t);
}
return p_stats;
error:
free (p_stats);
return NULL;
}
Testé uniquement sous Windows xp pour le moment. Je colle un lien vers le tiles.bmp dès que possible.
edit:
Bon on ne peut pas uploader sur le sdz en .bmp.
Il vous faudra convertir en .bmp. Ou, je vais reposter une version qui utilise SDL_Image.
edit2:
modification pour n'afficher que lorsque nécessaire...
Code proposé pour « feu » le concours SDL (pour participation uniquement) :
http://www.siteduzero.com/forum-83-610 [...] html#r5928594 [Linuxiens : ne téléchargez que les sources les plus récentes sur GitHub, elles corrigent un bug assez bizarre lié à l'appel de SDL_WM_SetCaption() depuis un callback. Et pour les autres aussi, c'est recommandé (correction d'un bug de performance).]
Sinon, ce serait sympa de parler un peu algo, genre quel algo utilisez vous pour le comptage de cases voisines (il y en a 2 typiquement), ou autres trucs.
@ GurneyH : je crois que je vais te piquer tes tiles, ils sont plus jolis que les miens.
Sinon, ce serait sympa de parler un peu algo, genre quel algo utilisez vous pour le comptage de cases voisines (il y en a 2 typiquement), ou autres trucs.
Hum, tu parles du comptage des cases voisines au moment de la pose des bombes?
Pour mon code, à chaque fois que je pose une bombe et pour chacune des 8 cases adjacentes(s'il ne s'agit pas d'une bombe), j'incrémente un compteur.
Je vois pas la seconde solution!
Niveau algo, c'est plus le dfs pour découvrir les cases que je trouve sympa!
Citation : Yoch
@ GurneyH : je crois que je vais te piquer tes tiles, ils sont plus jolis que les miens.
Pas de soucis, moi je les trouve laids!(ce sont les tiles du démineur de WIndows Xp!).
Pour mon code, à chaque fois que je pose une bombe et pour chacune des 8 cases adjacentes(s'il ne s'agit pas d'une bombe), j'incrémente un compteur.
Je vois pas la seconde solution!
C'est normal, tu as la bonne (l'autre solution est moins bonne).
Citation : GurneyH
Niveau algo, c'est plus le dfs pour découvrir les cases que je trouve sympa!
Ouais, c'est sympa aussi, mais il n'y a pas de piège là dessus. Enfin bon, le démineur, ce n'est pas non plus le gros truc d'algo de la mort, bref...
Niveau fonctionnalités :
Une fonction intéressante à implémenter serait la fonction "double-clic" du démineur Windows : effectue les découvrements "immédiats" automatiquement. Perso, je l'ai mise. En revanche, je n'ai pas ajouté de possibilité de sauvegarde.
Citation : GurneyH
Citation : Yoch
@ GurneyH : je crois que je vais te piquer tes tiles, ils sont plus jolis que les miens.
Pas de soucis, moi je les trouve laids!(ce sont les tiles du démineur de WIndows Xp!).
Moi, je les ai fait à la main, donc forcément c'est pire...
J'ai fait le niveau 2 (pas encore géré la gestion du clavier)
Main
main.c
#include "header.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int main(void) {
Grille grille;
Choix choix;
position_t x, y;
int nbMines;
srand(time(NULL));
printf("** zDemineur (niveau 2) **\n\n");
afficherRegles();
printf("Paramètres :\n");
printf(" - lignes : "); scanf("%d", &y);
while(y < 0 || y > MAX_Y) {
fprintf(stderr, "Erreur : le nombre de lignes doit être positif et inférieur à %d\n", MAX_Y);
printf(" - lignes : "); scanf("%d", &y);
}
printf(" - colonnes : "); scanf("%d", &x);
while(y < 0 || y > MAX_Y) {
fprintf(stderr, "Erreur : le nombre de colonnes doit être positif et inférieur à %d\n", MAX_X);
printf(" - colonnes : "); scanf("%d", &x);
}
printf(" - mines : "); scanf("%d", &nbMines);
while(nbMines > x*y) {
fprintf(stderr, "Erreur : le nombre de mines doit être inférieur (ou égal) au nombre de cases (%d)\n", x*y);
printf(" - mines : "); scanf("%d", &nbMines);
}
printf("\n");
initGrille(&grille, x, y, nbMines);
#ifdef DEBUG
printf("Grille (debug) :\n\n");
afficherGrilleClaire(&grille);
#endif
bool aGagne = false, aPerdu = false;
do
{
afficherSeparateur();
printf("Grille :\n\n");
afficherGrille(&grille);
if(grille.nbDrapeaux < grille.nbMines) {
printf("\nIl devrait rester %d/%d mines\n\n",
grille.nbMines - grille.nbDrapeaux,
grille.nbMines);
}
else if(grille.nbDrapeaux == grille.nbMines) {
printf("\nHum.. le compte de drapeaux est bon, mais sont-ils au bon endroit ?\n\n");
}
else {
printf("\nIl y a %d drapeaux en trop (%d/%d)\n\n",
grille.nbDrapeaux - grille.nbMines,
grille.nbDrapeaux,
grille.nbMines);
}
printf("Que faire :\n");
printf(" - (%d) Abandonner\n", ABANDONNER);
printf(" - (%d) Poser/Enlever un drapeau\n", MARQUER);
printf(" - (%d) Découvrir une case \n", DECOUVRIR);
printf("\nChoix : "); scanf("%d", (int*)&choix);
switch(choix) {
case ABANDONNER:
break;
case MARQUER:
printf("Quelle case :\n");
printf(" - x : "); scanf("%d", (int*)&x);
printf(" - y : "); scanf("%d", (int*)&y);
printf("\n");
x--;
y = grille.nbLignes - (y-1) -1;
if(x > 0 && x < grille.nbColonnes && y > 0 && y < grille.nbLignes) {
marquer(&grille, x, y);
aGagne = gagne(&grille);
}
break;
case DECOUVRIR:
printf("Quelle case :\n");
printf(" - x : "); scanf("%d", (int*)&x);
printf(" - y : "); scanf("%d", (int*)&y);
printf("\n");
x--;
y = grille.nbLignes - (y-1) - 1;
if(x > 0 && x < grille.nbColonnes && y > 0 && y < grille.nbLignes) {
decouvrir(&grille, x, y);
aPerdu = (grille.grille[x][y].indication == MINE);
}
break;
}
} while(!aGagne && !aPerdu && choix != ABANDONNER);
afficherSeparateur();
if(aPerdu || choix == ABANDONNER) {
printf("Perdu !\n\n");
}
else {
printf("Gagné !\n\n");
}
afficherGrilleClaire(&grille);
return 0;
}
Header
header.h
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
// #define DEBUG
// La valeur qui désigne une mine (/!\ ne doit pas valoir entre 1 et 8)
#define MINE -1
typedef int position_t; //< type d'une position
typedef int indication_t; //< type d'un indice
/** Etat d'une case (jeu) */
typedef enum eEtat {
INCONNUE, DECOUVERTE, DRAPEAU
} Etat ;
/** Une case */
typedef struct sCase {
indication_t indication; // MINE si minée, 0 - 8 sinon.
Etat etat; // Etat au cours de la partie
} Case ;
// Taille maximale d'un tableau
#define MAX_X 9
#define MAX_Y 9
/** Une grille */
typedef struct sGrille {
Case grille[MAX_X][MAX_Y]; // Notre grille
position_t nbLignes; // hauteur
position_t nbColonnes; // largeur
char nbMines; // (Calculable)
char nbDrapeaux; // (Calculable)
} Grille ;
/** Couleur */
typedef enum eColor {
NOIR, ROUGE, VERT, ORANGE, BLEU, MAJENTA, CYAN, BLANC
} Color ;
/** Choix (menu de jeu) */
typedef enum eChoix {
ABANDONNER, MARQUER, DECOUVRIR
} Choix ;
/** booléen */
typedef enum eBool {
false = 0, true = 1
} bool;
// --------------------------------------------------------- //
/** Ecrit l'indice passé en paramètre (en couleur) */
void ecrireIndice(indication_t c);
/** Change la couleur du terminal */
void setColor(Color c);
/** Remet la couleur du terminal à zéro */
void resetColor(void);
// --------------------------------------------------------- //
/** Tire un nombre aléatoire entre min et max */
int aleatoire(int min, int max);
// --------------------------------------------------------- //
/**
Initialise notre grille
@param g
La grille
@param nbC
Le nombre de colonnes
@param nbL
Le nombre de lignes
@param nbMines
Le nombre de mines
*/
void initGrille(Grille * g, position_t nbC, position_t nbL, int nbMines);
/** Ajoute une mine en (x, y) */
void ajouterMine(Grille *, position_t x, position_t y);
/** Ajoute nbMines mines aléatoirement */
void ajouterMines(Grille *, int nbMines);
// --------------------------------------------------------- //
/** Affiche les règles du jeu */
void afficherRegles(void);
/** Affiche un séparateur (ou efface l'ecran) */
void afficherSeparateur(void);
/** Affiche la grille decouverte */
void afficherGrilleClaire(const Grille *);
/** Affiche la grille */
void afficherGrille(const Grille *);
// --------------------------------------------------------- //
/** Indique si les drapeaux sont ou il faut */
bool gagne(const Grille *);
/** Plante un drapeau */
void marquer(Grille *, position_t x, position_t y);
/** Decouvrons une case */
void decouvrir(Grille *, position_t x, position_t y);
#endif//HEADER_H_INCLUDED
Fonctions
fonctions.c
#include "header.h"
#include <stdio.h>
#include <stdlib.h>
int aleatoire(int min, int max) {
return ((int)((double)rand()/RAND_MAX*(max-min)))+min;
// return (rand()%(max-min+1))+min;
}
void setColor(Color c) {
// Si on est sous linux
#ifdef __linux__
printf("\033[3%dm", c);
if(c == NOIR) {
printf("\033[7%dm", BLANC);
}
if(c == BLANC) {
printf("\033[7%dm", NOIR);
}
#endif
// Si on est sous windows
#ifdef __WIN32__
switch(c) {
case NOIR: system("COLOR 0F"); break;
case ROUGE: system("COLOR C0"); break;
case VERT: system("COLOR 20"); break;
case ORANGE: system("COLOR E0"); break;
case BLEU: system("COLOR 90"); break;
case MAJENTA: system("COLOR D0"); break;
case CYAN: system("COLOR B0"); break;
case BLANC: system("COLOR F0"); break;
}
#endif
}
void resetColor(void) {
// Si on est sous linux
#ifdef __linux__
printf("\033[0m");
#endif
// Si on est sous windows
#ifdef __WIN32__
setColor(BLANC);
#endif
}
grille.c
#include "header.h"
void initGrille(Grille * g, position_t nbC, position_t nbL, int nbMines) {
g->nbLignes = nbL;
g->nbColonnes = nbC;
g->nbMines = 0;
g->nbDrapeaux = 0;
int x, y;
for(x = 0; x < g->nbColonnes; x++) {
for(y = 0; y < g->nbLignes; y++) {
g->grille[x][y].indication = 0;
g->grille[x][y].etat = INCONNUE;
}
}
ajouterMines(g, nbMines);
}
void ajouterMines(Grille * g, int N) {
int n;
const int nbCases = g->nbLignes * g->nbColonnes;
for(n = 0; n < N; n++) {
int c = aleatoire(0, nbCases-1 - g->nbMines)+1;
int i = 0;
char x = 0;
char y = 0;
if(g->grille[x][y].indication != MINE)
i++;
while(i < c) {
x++;
if(x >= g->nbColonnes) {
x = 0;
y++;
if(y >= g->nbLignes) {
y = 0;
}
}
if(g->grille[x][y].indication != MINE)
i++;
}
ajouterMine(g, x, y);
}
}
void ajouterMine(Grille * g, position_t x, position_t y) {
// Ajoute la mine
g->grille[x][y].indication = MINE;
g->nbMines++;
// Incrémente les indices des cases alentours (si ce ne sont pas des mines, ou du vide)
// A gauche
if(x > 0) {
// En haut
if(y > 0) {
if(g->grille[x-1][y-1].indication != MINE) {
g->grille[x-1][y-1].indication++;
}
}
// Au milieu
if(g->grille[x-1][y].indication != MINE) {
g->grille[x-1][y].indication++;
}
// En bas
if(y < g->nbLignes - 1) {
if(g->grille[x-1][y+1].indication != MINE) {
g->grille[x-1][y+1].indication++;
}
}
}
// Au milieu
// En haut
if(y > 0) {
if(g->grille[x][y-1].indication != MINE) {
g->grille[x][y-1].indication++;
}
}
// En bas
if(y < g->nbLignes - 1) {
if(g->grille[x][y+1].indication != MINE) {
g->grille[x][y+1].indication++;
}
}
// A droite
if(x < g->nbColonnes - 1) {
// En haut
if(y > 0) {
if(g->grille[x+1][y-1].indication != MINE) {
g->grille[x+1][y-1].indication++;
}
}
// Au milieu
if(g->grille[x+1][y].indication != MINE) {
g->grille[x+1][y].indication++;
}
// En bas
if(y < g->nbLignes - 1) {
if(g->grille[x+1][y+1].indication != MINE) {
g->grille[x+1][y+1].indication++;
}
}
}
}
affichage.c
#include "header.h"
#include <stdio.h>
void afficherRegles(void) {
printf("Regles : Vous voila en face d'un terrain qu'il vous faut déminer.\n");
printf(" Pour ce faire, vous allez devoir poser des drapeaux sur chaque mine.\n");
printf(" Chaque case découverte vous indiquant le nombre de mines adjacentes.\n");
printf(" Si vous tentez de découvrir une case minée, vous perdez.\n\n");
}
void afficherSeparateur(void) {
printf("____________________________________\n\n");
}
void afficherGrilleClaire(const Grille * g) {
char x, y;
for(y = 0; y < g->nbLignes; y++) {
// Affiche une ligne
printf(" +");
for(x = 0; x < g->nbColonnes; x++) {
printf("---+");
}
printf("\n |");
// Affiche une liste de cases
for(x = 0; x < g->nbColonnes; x++) {
printf(" ");
if(g->grille[x][y].indication == MINE) {
printf("x");
}
else if(g->grille[x][y].indication == 0) {
printf(" ");
}
else {
ecrireIndice(g->grille[x][y].indication);
}
printf(" |");
}
printf("\n");
}
// Affiche une ligne
printf(" +");
for(x = 0; x < g->nbColonnes; x++) {
printf("---+");
}
printf("\n");
}
void afficherGrille(const Grille * g) {
char x, y;
for(y = 0; y < g->nbLignes; y++) {
// Affiche une ligne
printf(" +");
for(x = 0; x < g->nbColonnes; x++) {
printf("---+");
}
printf("\n |");
// Affiche une liste de cases
for(x = 0; x < g->nbColonnes; x++) {
printf(" ");
if(g->grille[x][y].etat == INCONNUE) {
printf("?");
}
else if(g->grille[x][y].etat == DRAPEAU) {
printf("P");
}
else if(g->grille[x][y].indication == MINE) {
printf("x");
}
else if(g->grille[x][y].indication == 0) {
printf(" ");
}
else {
ecrireIndice(g->grille[x][y].indication);
}
printf(" |");
}
printf("\n");
}
// Affiche une ligne
printf(" +");
for(x = 0; x < g->nbColonnes; x++) {
printf("---+");
}
printf("\n");
}
void ecrireIndice(indication_t c) {
switch(c) {
case 1: setColor(BLEU); break;
case 2: setColor(ROUGE); break;
case 3: setColor(VERT); break;
case 4: setColor(ORANGE); break;
case 5: setColor(MAJENTA); break;
case 6: setColor(CYAN); break;
case 7: setColor(BLANC); break;
case 8: setColor(NOIR); break;
}
printf("%d", (int)c);
resetColor();
}
jeu.c
#include "header.h"
bool gagne(const Grille * g) {
if(g->nbDrapeaux != g->nbMines)
return false;
int x, y;
for(x = 0; x < g->nbColonnes; x++) {
for(y = 0; y < g->nbLignes; y++) {
if(g->grille[x][y].indication == MINE && g->grille[x][y].etat != DRAPEAU) {
return false;
}
if(g->grille[x][y].indication != MINE && g->grille[x][y].etat == DRAPEAU) {
return false;
}
}
}
return true;
}
void marquer(Grille * g, position_t x, position_t y) {
if(g->grille[x][y].etat == DECOUVERTE)
return;
if(g->grille[x][y].etat == DRAPEAU) {
g->grille[x][y].etat = INCONNUE;
g->nbDrapeaux--;
}
else {
g->grille[x][y].etat = DRAPEAU;
g->nbDrapeaux++;
}
}
void decouvrir(Grille * g, position_t x, position_t y) {
if(g->grille[x][y].etat == DECOUVERTE) {
return;
}
if(g->grille[x][y].etat == DRAPEAU) {
g->nbDrapeaux--;
}
g->grille[x][y].etat = DECOUVERTE;
if(g->grille[x][y].indication == 0) {
// A gauche
if(x > 0) {
// En haut
if(y > 0) {
decouvrir(g, x-1, y-1);
}
// Au milieu
decouvrir(g, x-1, y);
// En bas
if(y < g->nbLignes - 1) {
decouvrir(g, x-1, y+1);
}
}
// Au milieu
// En haut
if(y > 0) {
decouvrir(g, x, y-1);
}
// En bas
if(y < g->nbLignes - 1) {
decouvrir(g, x, y+1);
}
// A droite
if(x < g->nbColonnes - 1) {
// En haut
if(y > 0) {
decouvrir(g, x+1, y-1);
}
// Au milieu
decouvrir(g, x+1, y);
// En bas
if(y < g->nbLignes - 1) {
decouvrir(g, x+1, y+1);
}
}
}
}
C'est une version naïve de la chose, commencée from scratch hier soir.
Je suis bien conscient que le réaffichage de la totalité à chaque frame n'est pas super adapté à un démineur, que c'est la version lente du compte de mines, tout ça, mais bon, c'était pour me détendre un peu (révisions intensives et pas de code).
Contrôles :
F5 pour relancer une partie.
LMB : Démine.
RMB : Flag / interrogation / unflag.
(Et un jour, je ferai peut-être ce qui manque ! )
Clément.
Edit :
Je viens de jeter un oeil au code de GurneyH (qui est plus propre que le mien d'ailleurs), et je précise que la similitude sur les noms de fonctions, les enums, et autres choses ressemblantes est vraiment une coïncidence !
Edit 2 :
Et une petite upgrade pour voir les bombes quand on perd. (+ suppression de valeurs en dur, mais je ne suis pas sûr que ça aide à la compréhension).
Niveau fonctionnalités :
Une fonction intéressante à implémenter serait la fonction "double-clic" du démineur Windows : effectue les découvrements "immédiats" automatiquement. Perso, je l'ai mise.
Heuu, je suis pas sûr d'avoir saisi le double clic.
Tu pourrais être un peu plus explicite ?
Heuu, je suis pas sûr d'avoir saisi le double clic.
Tu pourrais être un peu plus explicite ?
Suppose que j'ai marqué 2 cases d'un drapeau comme ci-dessous.
Il est immédiat que certaines cases peuvent être déminées sans risque (et sans "calcul"), puisque la case marquée de [2] est déjà en contact avec 2 mines. Si je double clic sur la case [2], j'obtiens :
Ça permet d'accélérer un peu le jeu. Et si mes drapeaux étaient mauvais, je peux perdre en faisant cela.
Il me semble que certains jeux sous Linux implémentent aussi cette fonction (chercher dans les options).
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.