Implementación en lenguaje C del barrido de minas (incluida la expansión, con código fuente)

Creo que todos los que lo hacen han jugado al buscaminas antes, así que no entraré en detalles sobre la jugabilidad.


Vaya directamente al tema: ¡piense primero, luego codifique! !

Primero, dividí el buscaminas en dos tableros, uno para las minas y otro para que los jugadores adivinaran.

Esto es lo que preguntaron algunos estudiantes, armar un tablero de ajedrez no ha terminado, ¿entonces lo hará complicado?

Para responder brevemente a la pregunta del estudiante:

Debido a que mi forma de pensar es así, uso '1' para representar minas y '0' para representar que no hay minas. Si hay varios 1 en un plato, es imposible determinar si es una mina o uno de los 8 que lo rodean. rejillas Hay un trueno.

Explicación adicional

  Referencia de la imagen:                      

Cuando la mina es 1 y el número de registro también es 1 , la siguiente posición de la marca amarilla es un ejemplo:         

                                         

En este punto, haga clic en la posición amarilla, luego el número que muestra es 2 en lugar de 1, y encontraremos que el trueno se ha vuelto más.

Pero otro compañero de clase quiere preguntar, ¿por qué tienes que usar 1 para representar truenos y 0 para representar no truenos? Yo uso ¥ para representar truenos y @ para representar non-lei, por lo que esta situación no ocurrirá.

De hecho, no hay ningún problema con este arreglo. También animo a todos a que lo prueben, pero los beneficios que trae el doble tablero de ajedrez se reflejarán en el análisis del código más adelante. (Una versión de tablero de ajedrez se puede hacer más adelante)

Cabe señalar que usamos '1' y '0' , es decir, el carácter 1 y el carácter 0 representan minas y no minas. ¿Por qué está dispuesto de esta manera? Esto nos ayudará a diseñar arreglos y funciones en el futuro. Ahora explicaremos esto temporalmente y le daremos una comprensión más clara y sistemática más adelante.

¡Este es el final de las ideas preliminares de diseño! El tren comienza ahora, ¡cierre las puertas y ventanas y abróchense los cinturones de seguridad! !


Primero, diseñamos el juego en archivos separados, un test.c para administrar el proceso de ejecución del juego, un game.c para implementar las funciones personalizadas requeridas por el juego, un game.h para encapsular la declaración de funciones, la definición de constantes, la inclusión de archivos de cabecera, etc.

Para el diseño preliminar del proceso de ejecución del juego de test.c, consulte el siguiente código:

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;
}

Debido a que es posible que sea necesario jugar el juego muchas veces, para no reiniciar el juego, aquí se usa la forma de bucle, y debido a que el juego debe ejecutarse al menos una vez, aquí se usa el bucle do while, y es no es un gran problema usar el ciclo while. Bueno, el bucle do while primero ejecuta el menú de impresión, y luego elegimos la rama que se ejecutará a continuación de acuerdo con el menú. Cuando elegimos 1, significa que queremos jugar el juego a continuación, y cuando elegimos 0, queremos para salir del juego, aquí elegimos usar la entrada de variable entera para aceptar nuestra elección y cambiar para ejecutar nuestra elección. Al mismo tiempo, encontraremos que hay una ventaja en arreglar el número de salida para que sea 0. Cuando la entrada es 0, el ciclo se puede salir directamente y el ciclo puede continuar en lugar de 0, lo cual es perfectamente en línea con nuestro diseño de elección! ! !


Lo siguiente es la implementación de la función game().

Acabamos de discutir que necesitamos dos tableros de ajedrez, un tablero de minas y un tablero de adivinanzas para el jugador.

Entonces creamos dos arreglos 2D para representar los dos discos.

Eso es (thunder disk) char mine [ ] [ ] = { 0 }; (player interface disk) char show [ ] [ ] = { 0 }; Pero, ¿cuáles son los tamaños de los dos discos? Esto es lo que debemos considerar Pregunta, según la dificultad del barrido de minas primario, organizamos temporalmente un tablero de ajedrez de 9X9. Para una consideración a más largo plazo, es imposible para nosotros escribir directamente char mine [ 9 ] [ 9 ] = { 0 }; char show [ 9 ] [ 9 ] = { 0 }; para futuras dificultades de mantenimiento y control en un pequeño cost, usamos #define para definir ROW como 9 y COL como 9. Pero, ¿realmente somos lo suficientemente completos? ¿Cómo contar 8 minas alrededor de la posición fronteriza? Sin duda, esto dará como resultado una matriz fuera de los límites. Entonces necesitamos expandir el tablero de ajedrez a un tablero de ajedrez de 11X11. Entonces creamos dos tableros de ajedrez como este char mine[ROWS][COLS] = { 0 }; char mine[ ROWS ][ COLS ] = { 0 };

Aquí tenemos que decir brevemente:

#define FILAS FILA+2

#define COLUMNAS COL+2.

Bueno, finalmente tenemos objetos que podemos manipular, ahora necesitamos inicializarlos.

Tomemos un nombre de función según la tradición, initboard(), eso es todo. ¡Código encendido! ! !

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;
		}
	}
}

Aquí, inicializamos el disco de la mina con 0, lo que significa que no hay minas en todas las ubicaciones, y usamos * para mantener el misterio de la ubicación para que los jugadores la adivinen.

即 initboard(mine, ROWS, COLS, '0');
            initboard(mostrar, FILAS, COLUMNAS, '*');

Aunque la función de impresión no se ha completado por un tiempo, no nos impide admirar los resultados de la inicialización (consulte la figura a continuación)

    

De hecho, esto muestra por qué los caracteres '1' y '0' se usan para representar minas y no minas, en lugar de los números 1 y 0, porque solo necesitamos una función para inicializar ambos discos. Y si un tablero es una matriz de números enteros y el otro es una matriz de caracteres, necesitará dos funciones de inicialización.


 Después de inicializar el tablero de ajedrez, necesitamos completar el resto de la función de impresión, porque queremos verificar si nuestra función de inicialización implementa la inicialización del tablero de ajedrez de acuerdo con nuestras ideas. el código se muestra a continuación:

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");
	}
}

Debido a limitaciones de espacio, la función de impresión no se describe aquí y la apariencia del tablero de ajedrez está determinada por las preferencias personales.


Chong Chong Chong, el siguiente paso es configurar el disco de trueno y poner el código. ! !

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--;
		}
	}
}

Aquí EASY_COUNT es una función constante definida por #define EASY_COUNT 9, lo que significa un número simple de minas. Aquí, la función de biblioteca rand() se usa para diseñar números aleatorios para las coordenadas X e Y respectivamente. %fila+1 y %col+1 aseguran que X e Y están dentro del rango de 1 fila. Cabe señalar que mine[ x ] [ y ] Cuando asigne un valor, asegúrese de que mine[ x ] [ y ] no esté enterrado en las minas. Si no hay una condición if en la imagen de arriba, la mina puede ser menor, porque las asignaciones repetidas sobrescribirán la último valor.


Jajaja, la emocionante sesión de desminado finalmente está aquí, ¡debería haber aplausos aquí! ! ! Mira la foto primero

En primer lugar, necesitamos ingresar las coordenadas de la mina de inspección, por lo que creamos dos variables enteras X e Y. Cuando ingresamos los valores de X e Y, es posible que la entrada X e Y no cumpla con los requisitos. tiempo, aparece la instrucción if. Cuando el valor que ingresamos cumple con los requisitos, primero debemos juzgar si el mío [ X ] [ Y ] == '1', si la condición es verdadera, podemos declarar el fin del juego, si el mío [ X ] [ Y ] = = '1', si no se mantiene, entonces debemos asignar un valor a la posición de las coordenadas (X, Y) y contar cuántas minas hay en 8 posiciones a su alrededor. Usamos int get_mine_count() para contar el número de minas en 8 posiciones alrededor de la posición de las coordenadas (X, Y); el código es el siguiente:

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");
	}
}

El proceso de desminado es el núcleo de todo el juego. Los jugadores deben averiguar las posiciones de todas las minas o pisar las minas antes de que termine el juego. Por lo tanto, la función de desminado debe ser un proceso cíclico, de modo que el efecto del desminado múltiple Puede ser obtenido. ¿Cómo juzgar la victoria? Adoptamos una idea de diseño de este tipo: verifique todas las ubicaciones que no sean minas, y otras ubicaciones son naturalmente minas, para lograr un efecto de barrido de minas. Aquí utilizo la variable entera iswin como contador. Al ingresar una posición de coordenadas que no es la mía, iswin+= 1. Cuando iswin es igual a ROW*COL-EASY_COUNT, se puede considerar que el juego gana.


El punto está aquí, un análisis detallado de la función expand()

La función de la siguiente función get_mine_count() se reitera aquí: Cuente cuántas minas hay en 8 posiciones alrededor de la posición de las coordenadas de entrada (X,Y).

expand() es una función de expansión, la función es:

Al comprobar la posición de las coordenadas get_mine_count()! = 0, cambia el valor de esta posición al valor de retorno de get_mine_count().

Cuando haya 0 minas alrededor de la posición de coordenadas marcada, establezca la posición en vacío y verifique si las 8 posiciones circundantes también están rodeadas por minas 0. Si la posición de las coordenadas circundantes cumple la condición get_mine_count == 0, esta será La ubicación es también establecido en nulo, si la ubicación circundante circundante también cumple la condición get_mine_count == 0, esto también establece la ubicación en 0, si la ubicación circundante circundante también cumple la condición et_mine_count == 0......... .

Hablando en palabras humanas es: tome la posición que ingresó como punto de partida, siempre que la posición get_mine_count == 0, vacíela y, al mismo tiempo, vacíe los alrededores si get_mine_count == 0, y también considere la posición circundante como el punto de partida. Obviamente satisfaciendo la idea de recursividad, se puede resolver cómodamente con recursividad.

¡Código encendido! ! !

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';
	}
}

Por desgracia, realmente no puedo dejarlo, así que solo puedo molestar a los espectadores para que deslicen y miren.

Para representar las 8 posiciones alrededor de la posición de entrada, usamos las variables i,j para representar el bucle for, es decir, las siguientes 8 posiciones

        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] mina[x + 1][ y ] mina[x + 1][y + 1]

Cabe señalar que cuando el límite get_mine_count == 0, la posición circundante no puede estar vacía, porque si la posición circundante está vacía, habrá problemas para calcular get_mine_count Para obtener más detalles, consulte el principio de implementación de get_mine_count. Por lo tanto, la implementación recursiva anterior requiere una restricción para resolver este problema, consulte el código anterior para obtener más detalles.

Lo siguiente complementa el principio de implementación 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;
}

Reitere la función de la función get_mine_count(): calcule cuántas minas hay en 8 posiciones alrededor de la posición de las coordenadas de entrada (X,Y).

Como hay minas, las configuro con el carácter '1', así que sume los valores de estas 8 posiciones para saber el número de minas. Pero no sucede que este no sea el número 1, sino el carácter '1', porque su valor ASCII es 49, entonces en realidad representa el número 49, pero eso no nos impide sumar, porque el carácter ' 1'== El carácter '0'+1, por lo que podemos sumarlos y restar el carácter '0', porque el carácter '0' en realidad representa el número 48. Cuando se imprime como un número entero, imprime el carácter '0' , que muestra 0, lo mismo que el número 0. Cuando se imprime como un número entero, se imprime 48.), que también refleja el uso de los caracteres '1' y El carácter '0' representa los beneficios con y sin minas.

compartir el código fuente completo

prueba.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;
}

juego.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';
	}
}

juego.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);


Bueno, lo de arriba es una implementación simple del barrido de minas, no te gusta cuando lo ves, jaja. Habrá más series en el futuro, como el análisis y aplicación de todas las palabras clave en lenguaje C, o más implementaciones de juegos. Este es mi primer blog en el verdadero sentido de la palabra, y espero que todos lo cuiden.

Si hay algún error en lo anterior, me gustaría pedirle que me dé su consejo, y se lo agradeceré.

¡Espero progresar junto con todos ustedes! ¡Dios recompensa el trabajo duro! ! ¡La vida vale la pena y el futuro es prometedor! ! !

Supongo que te gusta

Origin blog.csdn.net/m0_62171658/article/details/121302830
Recomendado
Clasificación