Implémentation en langage C du déminage (y compris l'extension, avec le code source)

Je crois que tous ceux qui le font ont déjà joué au démineur, donc je n'entrerai pas dans les détails du gameplay.


Allez droit au but : réfléchissez d'abord, codez ensuite ! !

Tout d'abord, j'ai divisé le dragueur de mines en deux tableaux, un pour les mines et l'autre pour que les joueurs puissent deviner.

C'est ce que certains élèves se sont demandés, la mise en place d'un échiquier n'est pas terminée, donc ça va compliquer ?

Pour répondre brièvement à la question de l'élève :

Parce que ma pensée est comme ça, j'utilise '1' pour représenter les mines et '0' pour ne représenter aucune mine. S'il y a plusieurs 1 dans une plaque, il est impossible de déterminer s'il s'agit d'une mine ou de l'un des 8 environnants. grilles Il y a un tonnerre.

Plus d'explications

  Référence de l'image :                      

Lorsque la mine est 1 et que le numéro d'enregistrement est également 1 , la position de la marque jaune suivante est un exemple :         

                                         

À ce stade, cliquez sur la position jaune, puis le nombre qu'il affiche est 2 au lieu de 1, et nous constaterons que le tonnerre '' est devenu plus ''.

Mais un autre camarade de classe veut demander, pourquoi devez-vous utiliser 1 pour représenter le tonnerre et 0 pour ne représenter aucun tonnerre ? J'utilise ¥ pour représenter le tonnerre et @ pour représenter non-lei, donc cette situation ne se produira pas.

En fait, il n'y a aucun problème avec cet arrangement.J'encourage également tout le monde à l'essayer, mais les avantages apportés par le double échiquier seront reflétés dans l'analyse du code plus tard. (Une version échiquier pourra être réalisée ultérieurement)

Il convient de noter que nous utilisons '1' et '0' , c'est-à-dire que le caractère 1 et le caractère 0 représentent des mines et pas de mines. Pourquoi est-il disposé de cette façon ? Cela nous aidera à concevoir des tableaux et des fonctions à l'avenir. Maintenant, nous allons l'expliquer temporairement, et nous vous donnerons une compréhension plus claire et plus systématique plus tard.

C'est la fin des idées de conception préliminaires ! Le train démarre maintenant, veuillez fermer les portes et les fenêtres et attacher vos ceintures de sécurité ! !


Tout d'abord, nous concevons le jeu dans des fichiers séparés, un test.c pour gérer le processus d'exécution du jeu, un game.c pour implémenter les fonctions personnalisées requises par le jeu, un game.h pour encapsuler la déclaration de fonction, la définition de constantes, l'inclusion de fichiers d'en-tête, etc. .

Pour la conception préliminaire du processus d'exécution du jeu de test.c, reportez-vous au code suivant :

void menu()
{
    printf("******************\n");
	printf("***   1.play   ***\n");
	printf("***   0.exit   ***\n");
	printf("******************\n");
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

Parce que le jeu peut avoir besoin d'être joué plusieurs fois, afin de ne pas redémarrer le jeu, la forme de boucle est utilisée ici, et parce que le jeu doit être exécuté au moins une fois, la boucle do while est utilisée ici, et c'est pas un gros problème pour utiliser la boucle while. Eh bien, la boucle do while exécute d'abord le menu d'impression, puis nous choisissons la branche à exécuter ensuite en fonction du menu. Lorsque nous choisissons 1, cela signifie que nous voulons jouer au jeu ensuite, et lorsque nous choisissons 0, nous voulons pour quitter le jeu, Ici, nous choisissons d'utiliser l'entrée variable entière pour accepter notre choix et basculer pour exécuter notre choix. En même temps, nous constaterons qu'il y a un avantage à faire en sorte que le numéro de sortie soit 0. Lorsque l'entrée est 0, la boucle peut être quittée directement et la boucle peut continuer à se poursuivre au lieu de 0, ce qui est parfaitement en ligne avec notre design de choix! ! !


Vient ensuite l'implémentation de la fonction game().

Nous venons de discuter du fait que nous avons besoin de deux échiquiers, un plateau de mine et un plateau de devinettes pour le joueur.

Nous créons donc deux tableaux 2D pour représenter les deux disques.

C'est-à-dire (thunder disk) char mine [ ] [ ] = { 0 } ; (player interface disk) char show [ ] [ ] = { 0 } ; Mais quelles sont les tailles des deux disques ? C'est ce que nous devons considérer Question, selon la difficulté du déminage primaire, on aménage provisoirement un échiquier de 9X9. Pour une considération à plus long terme, il nous est impossible d'écrire directement char mine [ 9 ] [ 9 ] = { 0 }; char show [ 9 ] [ 9 ] = { 0 }; Pour une maintenance future et une difficulté de contrôle à un petit coût, nous utilisons #define pour définir ROW comme 9 et COL comme 9. Mais sommes-nous vraiment assez complets ? Comment compter 8 mines autour de la position frontalière ? Cela se traduira sans aucun doute par un tableau hors limites. Nous devons donc étendre l'échiquier en un échiquier 11X11. Nous créons donc deux damiers comme celui-ci char mine[ROWS][COLS] = { 0 } ; char mine[ROWS ][ COLS ] = { 0 } ;

Ici, nous devons dire brièvement:

#define LIGNES LIGNE+2

#définir COLS COL+2.

Eh bien, nous avons enfin des objets que nous pouvons manipuler, maintenant nous devons les initialiser.

Prenons un nom de fonction selon la tradition, initboard(), c'est tout. Codage activé ! ! !

void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

Ici, nous initialisons le disque de la mine avec 0, ce qui signifie qu'il n'y a pas de mines à tous les endroits, et utilisons * pour garder le mystère de l'emplacement pour que les joueurs puissent le deviner.

即 initboard(mine, ROWS, COLS, '0');
            initboard(show, LIGNES, COLS, '*');

Bien que la fonction d'impression ne soit pas terminée depuis un moment, cela ne nous empêche pas d'admirer les résultats de l'initialisation (se référer à la figure ci-dessous)

    

En fait, cela montre pourquoi les caractères '1' et '0' sont utilisés pour représenter les mines et pas de mines, au lieu des chiffres 1 et 0, car nous n'avons besoin que d'une seule fonction pour initialiser les deux disques. Et si une carte est un tableau d'entiers et l'autre un tableau de caractères, vous aurez besoin de deux fonctions d'initialisation.


 Après avoir initialisé l'échiquier, nous devons terminer le reste de la fonction d'impression, car nous voulons vérifier si notre fonction d'initialisation implémente l'initialisation de l'échiquier selon nos idées. code afficher comme ci-dessous:

void displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= col; i++)
	{
		if (i == 0)
		{
			printf("  %d     ", i);
		}
		else
		{
			printf("%d     ", i);
		}
	}
	printf("\n------------------------------------------------------------\n");
	for (i = 1; i <= row; i++)
	{
		printf("  %d  |", i);
		for (j = 1; j <= col; j++)
		{
			printf("  %c  |", board[i][j]);
		}
		printf("\n------------------------------------------------------------\n");
	}
}

En raison des limitations d'espace, la fonction d'impression n'est pas décrite ici et l'apparence de l'échiquier est déterminée par vos préférences personnelles.


Chong Chong Chong, la prochaine étape consiste à définir le disque du tonnerre et à mettre le code ! ! !

void setmine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

Ici EASY_COUNT est une fonction constante définie par #define EASY_COUNT 9, ce qui signifie un simple nombre de mines. Ici, la fonction de bibliothèque rand() est utilisée pour concevoir respectivement des nombres aléatoires pour les coordonnées X et Y. %row+1 et %col+1 garantissent que X et Y sont dans la plage de 1 ~ row. Il convient de noter que mine[ x ] [ y ] Lors de l'attribution d'une valeur, assurez-vous que mine[ x ] [ y ] n'est pas enterré dans les mines. S'il n'y a pas de condition if dans l'image ci-dessus, la mine peut être inférieure, car des affectations répétées écraseront la dernière valeur.


Hahaha, la passionnante session de déminage est enfin là, il devrait y avoir des applaudissements ici ! ! ! Regardez d'abord la photo

Tout d'abord, nous devons saisir les coordonnées de la mine d'inspection, nous créons donc deux variables entières X et Y. Lorsque nous saisissons les valeurs de X et Y, les entrées X et Y peuvent ne pas répondre aux exigences. fois, l'instruction if apparaît. Lorsque la valeur que nous saisissons répond aux exigences, nous devons d'abord juger si le mien [ X ] [ Y ] == '1', si la condition est vraie, nous pouvons déclarer le jeu terminé, si le mien [ X ] [ Y ] = = ' 1', si cela ne tient pas, alors nous devons attribuer une valeur à la position des coordonnées (X, Y) et compter le nombre de mines qu'il y a dans 8 positions autour d'elle. Nous utilisons int get_mine_count() pour compter le nombre de mines en 8 positions autour de la position des coordonnées (X, Y) ; le code est le suivant :

void foundmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int iswin = 0;
	while (iswin < row * col - EASY_COUNT)
	{
		printf("请选择要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				displayboard(mine, row, col);
				break;
			}
			else
			{
				iswin++;
				expand(mine, show, row, col, x, y);
				displayboard(show, row, col);
			}
		}
		else
		{
			printf("输入坐标不合法,请重新输入\n");
		}
	}
	if (iswin == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
	}
}

Le processus de déminage est au cœur de tout le jeu. Les joueurs doivent connaître les positions de toutes les mines ou marcher sur les mines avant la fin du jeu. Par conséquent, la fonction de déminage doit être un processus cyclique, de sorte que l'effet du déminage multiple peut être obtenu. Comment juger de la victoire ? Nous adoptons une telle idée de conception : vérifiez tous les emplacements non miniers, et les autres emplacements sont naturellement des mines, afin d'obtenir un effet de déminage. Ici, j'utilise la variable entière iswin comme compteur. Lors de la saisie d'une position de coordonnées non mienne, iswin + = 1. Lorsque iswin est égal à ROW * COL-EASY_COUNT, le jeu peut être jugé gagnant.


Le point est ici, une analyse détaillée de la fonction expand ()

La fonction de la fonction get_mine_count() suivante est réitérée ici : Comptez le nombre de mines qu'il y a dans 8 positions autour de la position des coordonnées d'entrée (X,Y) .

expand() est une fonction d'expansion, la fonction est :

Lors de la vérification de la position des coordonnées get_mine_count() ! = 0, remplacez la valeur de cette position par la valeur de retour de get_mine_count().

Lorsqu'il y a 0 mines autour de la position de coordonnées cochée, définissez la position sur vide et vérifiez si les 8 positions environnantes sont également entourées de mines 0. Si la position de coordonnées environnante satisfait la condition get_mine_count == 0, ce sera L'emplacement est également défini sur null, si l'emplacement environnant environnant satisfait également la condition get_mine_count == 0, cela définit également l'emplacement sur 0, si l'emplacement environnant environnant satisfait également la condition et_mine_count == 0......... .

Parler avec des mots humains est : prenez la position que vous avez entrée comme point de départ, tant que la position get_mine_count == 0, videz-la, et en même temps videz l'environnement si get_mine_count == 0, et considérez également la position environnante comme le point de départ. Satisfaisant évidemment l'idée de récursivité, elle peut être résolue confortablement avec la récursivité.

Codage activé ! ! !

void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	if (get_mine_count(mine, x, y)==0)
	{
		show[x][y] = ' ';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*' && i > 0 && i <= row && j > 0 && j <= col)
				{
					expand(mine, show, row, col , i, j);
				}
			}
		}
	}
	else
	{
		show[x][y] = get_mine_count(mine, x, y) + '0';
	}
}

Hélas, je ne peux vraiment pas le lâcher, donc je ne peux que déranger les spectateurs pour qu'ils glissent et regardent.

Afin de représenter les 8 positions autour de la position d'entrée, nous utilisons les variables i,j pour représenter la boucle for, c'est-à-dire les 8 positions suivantes

        mien[x - 1][y - 1] mien[x - 1][ y ] mien[x - 1][y + 1]
        mien[ x ][y - 1] mien[x][y + 1]
        mien [x + 1][y - 1] mien[x + 1][ y ] mien[x + 1][y + 1]

Il convient de noter que lorsque la frontière get_mine_count == 0, la position environnante ne peut pas être vide, car si la position environnante est vide, il y aura des problèmes dans le calcul de get_mine_count.Pour plus de détails, consultez le principe d'implémentation de get_mine_count. Par conséquent, l'implémentation récursive ci-dessus nécessite une contrainte pour résoudre ce problème, voir le code ci-dessus pour plus de détails.

Ce qui suit complète le principe d'implémentation de get_mine_count :

static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
		+ mine[x][y - 1] + mine[x][y + 1]
		+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * 48;
}

Réitérez la fonction de la fonction get_mine_count() : calculez combien de mines il y a dans 8 positions autour de la position des coordonnées d'entrée (X,Y).

Parce qu'il y a des mines, je les mets avec le caractère '1', donc additionnez les valeurs de ces 8 positions pour connaître le nombre de mines. Mais il n'arrive pas que ce ne soit pas le chiffre 1, mais le caractère '1', car sa valeur ASCII est 49, donc il représente en fait le chiffre 49, mais cela ne nous empêche pas d'ajouter, car le caractère ' 1'== Le caractère '0'+1, nous pouvons donc les additionner et soustraire le caractère '0', car le caractère '0' représente en fait le nombre 48. Lorsqu'il est imprimé sous forme d'entier, il imprime le caractère '0' , qui affiche 0, identique au nombre 0. Lorsqu'il s'imprime sous forme d'entier, il imprime 48.), qui reflète également l'utilisation des caractères '1' et Le caractère '0' représente les avantages avec et sans mines.

partager l'intégralité du code source

test.c:

#include "game.h"
void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	initboard(mine, ROWS, COLS, '0');
	initboard(show, ROWS, COLS, '*');
	setmine(mine, ROW, COL);
	displayboard(show, ROW, COL);
	displayboard(mine, ROW, COL);
	foundmine(mine, show, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

jeu.c :

#include "game.h"
void menu()
{
	printf("******************\n");
	printf("***   1.play   ***\n");
	printf("***   0.exit   ***\n");
	printf("******************\n");
}
void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

void displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= col; i++)
	{
		if (i == 0)
		{
			printf("  %d     ", i);
		}
		else
		{
			printf("%d     ", i);
		}
	}
	printf("\n------------------------------------------------------------\n");
	for (i = 1; i <= row; i++)
	{
		printf("  %d  |", i);
		for (j = 1; j <= col; j++)
		{
			printf("  %c  |", board[i][j]);
		}
		printf("\n------------------------------------------------------------\n");
	}
}

void setmine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
		+ mine[x][y - 1] + mine[x][y + 1]
		+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * 48;
}

void foundmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int iswin = 0;
	while (iswin < row * col - EASY_COUNT)
	{
		printf("请选择要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				displayboard(mine, row, col);
				break;
			}
			else
			{
				iswin++;
				expand(mine, show, row, col, x, y);
				displayboard(show, row, col);
			}
		}
		else
		{
			printf("输入坐标不合法,请重新输入\n");
		}
	}
	if (iswin == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
	}
}

void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	if (get_mine_count(mine, x, y)==0)
	{
		show[x][y] = ' ';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*' && i > 0 && i <= row && j > 0 && j <= col)
				{
					expand(mine, show, row, col , i, j);
				}
			}
		}
	}
	else
	{
		show[x][y] = get_mine_count(mine, x, y) + '0';
	}
}

jeu.h :

#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void menu();
void initboard(char board[ROWS][COLS], int rows, int cols, char set);
void displayboard(char board[ROWS][COLS], int row, int col);
void setmine(char mine[ROWS][COLS], int row, int col);
void foundmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y);


Eh bien, ce qui précède est une mise en œuvre simple du déminage. Vous n'aimez pas ça quand vous le voyez, haha. Il y aura plus de séries à l'avenir, comme l'analyse et l'application de tous les mots-clés en langage C, ou plus d'implémentations de jeux. Ceci est mon premier blog dans le vrai sens du terme, et j'espère que vous en prendrez tous soin.

S'il y a des erreurs dans ce qui précède, je voudrais vous demander de me donner vos conseils, et je vous en serai reconnaissant.

J'espère progresser avec vous tous ! Dieu récompense le travail acharné ! ! La vie en vaut la peine et l'avenir est prometteur ! ! !

Je suppose que tu aimes

Origine blog.csdn.net/m0_62171658/article/details/121302830
conseillé
Classement