Creando un juego del Gomoku en C

CCBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este proyecto, crearemos un sencillo juego de Go-Moku basado en texto utilizando el lenguaje de programación C. El Go-Moku es un juego de tablero de estrategia para dos jugadores donde el objetivo es ser el primero en obtener cinco piezas consecutivas en línea, ya sea horizontal, vertical o diagonalmente. Desarrollaremos este juego utilizando un tablero de juego de 15x15.

👀 Vista previa

Juego de Go-Moku

🎯 Tareas

En este proyecto, aprenderás:

  • Cómo diseñar e implementar un tablero de ajedrez utilizando una matriz bidimensional en C.
  • Cómo crear una función principal para controlar el flujo del juego.
  • Cómo implementar funciones para inicializar el juego, imprimir el tablero de juego y permitir que los jugadores tomen sus turnos.
  • Cómo desarrollar una función para comprobar si se ha alcanzado una condición de victoria en el juego.
  • Cómo compilar y ejecutar el programa.

🏆 Logros

Después de completar este proyecto, podrás:

  • Trabajar con matrices bidimensionales en C.
  • Diseñar e implementar un flujo de juego utilizando funciones.
  • Comprobar las condiciones de victoria en un juego.
  • Compilar y ejecutar un programa en C.

Crear archivos del proyecto

Primero, crea un nuevo archivo llamado gomoku.c y dile que se abra en tu editor de código preferido.

cd ~/proyecto
touch gomoku.c
✨ Revisar Solución y Practicar

Diseñando un tablero de ajedrez

Utilizando 'O' y 'X' para representar las piezas

En primer lugar, necesitamos un tablero de ajedrez (15 * 15) para registrar la "situación" de cada posición en el tablero. Entonces podemos definir una matriz tableroAjedrez[16][16]. ¿Por qué no [15][15]? Es porque de esta manera, las coordenadas de la matriz pueden coincidir exactamente con las filas y columnas del tablero de ajedrez, lo que facilita escribir el código subsiguiente.

#include <stdio.h>

#define N 15

// Definir una matriz y asignar 0 como valor inicial a cada elemento.
int tableroAjedrez[N + 1][N + 1] = { 0 };
✨ Revisar Solución y Practicar

Función principal

Antes de comenzar a escribir la función principal, consideremos brevemente el flujo típico de un juego. Primero, ingresamos a la interfaz principal del juego, luego clicamos en el botón de inicio para entrar al juego, luego se muestra la pantalla del juego, se determina la victoria o derrota y se termina el juego. Entonces, ¿cómo es el flujo de un juego como el Go?

En primer lugar, ingresamos a la pantalla de bienvenida del juego, luego ingresamos Y para comenzar el juego (no Y para salir del juego), se muestra el tablero de juego y los dos jugadores toman turnos para colocar sus piezas, luego se determina la victoria o derrota (si hay 5 piezas en línea).

// Se utiliza para llevar un registro de si es el turno del jugador 1 o del jugador 2, con un número impar representando el turno del jugador 1 y un número par representando el turno del jugador 2.
int turnoDeQuién = 0;

int main(void)
{
	// Una función personalizada que inicializa el juego, es decir, muestra la pantalla de bienvenida y entra al tablero de juego.
	inicializarJuego();

	// Este bucle es para que los dos jugadores tomen turnos.
	while (1)
	{
		// En cada ciclo se incrementa en 1, para que dos personas puedan tomar turnos.
		turnoDeQuién++;

		// Una función personalizada que realiza una operación de colocar piezas.
		jugarAjedrez();
	}

	return 0;
}
✨ Revisar Solución y Practicar

Función initGame

En esta función, las características que queremos implementar son:

  • Mostrar una pantalla de bienvenida simple.
  • Solicitar la entrada 'Y' y mostrar el tablero de ajedrez después de la entrada.
void inicializarJuego(void)
{
	char c;

	printf("Por favor ingrese 'y' para entrar al juego:");
	c = getchar();
	if ('y'!= c && 'Y'!= c)
		exit(0);

	//Limpiar
	system("clear");

	//Aquí nuevamente llamamos a una función personalizada cuya función es imprimir el tablero de ajedrez.
	imprimirTableroAjedrez();
}

En la función inicializarJuego, utilizamos las funciones exit y system, por lo que necesitamos incluir el archivo de encabezado stdlib.h al principio del programa.

#include <stdlib.h>
✨ Revisar Solución y Practicar

Función imprimirTableroAjedrez

En esta función, nuestro objetivo es:

  • Imprimir los números de fila y columna, y luego imprimir el tablero de ajedrez.
  • Si el valor del elemento del array es 0, imprimir un asterisco (*) para indicar que la posición está vacía.
  • Si el valor del elemento del array es 1, imprimir un círculo sólido (X), que representa la pieza del jugador 1.
  • Si el valor del elemento del array es 2, imprimir un círculo vacío (O), que representa la pieza del jugador 2.
void imprimirTableroAjedrez(void)
{
	int i, j;

	for (i = 0; i <= N; i++)
	{
		for (j = 0; j <= N; j++)
		{
			if (0 == i)		// Esto imprime el número de columna.
				printf("%3d", j);
			else if (j == 0)	// Imprime el número de fila.
				printf("%3d", i);
			else if (1 == tableroAjedrez[i][j])
				printf("  X");
			else if (2 == tableroAjedrez[i][j])
				printf("  O");
			else
				printf("  *");
		}
		printf("\n");
	}
}
✨ Revisar Solución y Practicar

Función jugarAjedrez

En esta función, queremos lograr lo siguiente:

  • Pedir al jugador que ingrese la posición para colocar la pieza de ajedrez.
  • Si es el turno actual del jugador 1, asignar el valor de 1 al elemento correspondiente en la matriz.
  • Si es el turno actual del jugador 2, asignar el valor de 2 al elemento correspondiente en la matriz.
  • Después de cada movimiento, determinar si el jugador actual ha ganado.
void jugarAjedrez(void)
{
	int i, j, ganador;

	// Determinar si es el turno del jugador 1 o del jugador 2, y luego asignar el valor al elemento correspondiente en la matriz.
	if (1 == turnoDeQuién % 2)
	{
		printf("Turno del jugador 1, por favor ingrese la posición, el formato es número de línea + espacio + número de columna: ");
		scanf("%d %d", &i, &j);
		tableroAjedrez[i][j] = 1;
	}
	else
	{
		printf("Turno del jugador 2, por favor ingrese la posición, el formato es número de línea + espacio + número de columna: ");
		scanf("%d %d", &i, &j);
		tableroAjedrez[i][j] = 2;
	}

	// Imprimir el tablero nuevamente.
	system("clear");
	imprimirTableroAjedrez();	// Esta función se llama nuevamente.

	/*
	* La siguiente sección llama a la función personalizada (la función de juez).
	* Se utiliza para determinar si el jugador actual ha ganado el movimiento o no.
	*/
	if (juzgar(i, j, turnoDeQuién))
	{
		if (1 == turnoDeQuién % 2)
			printf("El ganador es el jugador 1!\n");
		else
			printf("El ganador es el jugador 2!\n");
	}
}
✨ Revisar Solución y Practicar

Función juzgar

Parámetros de la función:

  • x: el número de fila del movimiento actual
  • y: el número de columna del movimiento actual

Valor de retorno:

  • 1 o 0. 1 significa que el jugador actual ha ganado después de realizar el movimiento.
int juzgar(int x, int y)
{
    int i, j;
    int t = 2 - turnoDeQuién % 2;
    const int step[4][2]={{-1,0},{0,-1},{1,1},{1,0}};
    for(int i=0;i<4;++i)
    {
        const int d[2]={-1,1};
        int count=1;
        for(int j=0;j<2;++j)
            for(int k=1;k<=4;++k){
                int row=x+k*d[j]*step[i][0];
                int col=y+k*d[j]*step[i][1];
                if( row>=1 && row<=N && col>=1 && col<=N &&
                    tableroAjedrez[x][y]==tableroAjedrez[row][col])
                    count+=1;
                else
                    break;
            }
        if(count>=5)
            return 1;
    }
    return 0;
}

En la función juzgar, hay 3 bucles for anidados, y su propósito es determinar si hay una línea de cinco piezas consecutivas.

Una línea de cinco piezas puede estar en una fila, columna o dirección diagonal. Aquí, usaremos un enfoque de prueba y error, buscando piezas consecutivas en las direcciones horizontal, vertical y diagonal. Tomemos un ejemplo:

Juego del Gomoku

En el tablero de ajedrez anterior, explicaremos el algoritmo para determinar si hay una línea de cinco piezas basado en las coordenadas (9, 10).

Primero, comprobamos si hay una línea diagonal de cinco piezas que empiece en (9, 10). El proceso es el siguiente:

  • A partir de (9, 10), buscamos en la dirección superior izquierda. Las coordenadas que cumplen con la condición son (8, 9), (7, 8) y (6, 7). Dado que (5, 6) no cumple con la condición, pasamos al siguiente paso.
  • Luego, buscamos en la dirección inferior derecha y encontramos (10, 11), que es la única coordenada que cumple con la condición.
  • Hemos encontrado cinco puntos en línea recta, por lo que el jugador 2 gana.

Si no hay una línea de cinco piezas en la dirección diagonal, luego comprobamos las direcciones vertical y horizontal. Si ninguna de ellas cumple con la condición de victoria, significa que el jugador actual no puede ganar y el juego continuará.

✨ Revisar Solución y Practicar

Mi Pieza de Ajedrez Fue "Comida"

No sé si alguien se dio cuenta, pero en nuestro juego del Gato, incluso si una posición ya está ocupada, todavía podemos "comer" la pieza de ajedrez original al colocar la nuestra.

Esto se debe a que cuando escribimos la función jugarAjedrez, no comprobamos la posición donde colocamos la pieza. Podemos modificar nuestro código de la siguiente manera:

void jugarAjedrez(void)
{
	int i, j, ganador;
	if (1 == turnoDeQuién % 2)
	{
		printf("Turno del jugador 1, por favor ingrese la posición, el formato es número de línea + espacio + número de columna: ");
		scanf("%d %d", &i, &j);
		//debug
		while(tableroAjedrez[i][j]!= 0)
		{
			printf("Esta posición ya está ocupada, por favor ingrese la posición nuevamente: ");
			scanf("%d %d",&i, &j);
		}
		//debug
		tableroAjedrez[i][j] = 1;
	}
	else
	{
		printf("Turno del jugador 2, por favor ingrese la posición, el formato es número de línea + espacio + número de columna: ");
		scanf("%d %d", &i, &j);
		//debug
		while(tableroAjedrez[i][j]!= 0)
		{
			printf("Esta posición ya está ocupada, por favor ingrese la posición nuevamente: ");
			scanf("%d %d",&i, &j);
		}
		//debug
		tableroAjedrez[i][j] = 2;
	}

	system("clear");
	imprimirTableroAjedrez();

	if (juzgar(i, j))
	{
		if (1 == turnoDeQuién % 2)
			printf("El ganador es el jugador 1!\n");
		else
			printf("El ganador es el jugador 2!\n");
	}
}

Agregamos un bucle en la función, por lo que cuando la posición ya está ocupada, se muestra un mensaje y se solicita una nueva entrada.

✨ Revisar Solución y Practicar

¿Por qué nunca puedo ganar?

Cuando hay una línea de cinco piezas y se muestra el mensaje "Gana el jugador 1" o "Gana el jugador 2", luego se muestra el mensaje "Es el turno del jugador *, por favor ingrese la posición de su pieza...". ¿No es frustrante? En realidad, todo lo que tenemos que hacer es agregar una línea de código!

	if (juzgar(i, j))
	{
		if (1 == turnoDeQuién % 2)
		{
			printf("El ganador es el jugador 1!\n");
			exit(0);	//debug
		}
		else
		{
			printf("El ganador es el jugador 2!\n");
			exit(0);	//debug
		}
	}
}

Además de mostrar al jugador que gana después de ganar, salir del juego con exit(0).

✨ Revisar Solución y Practicar

Compilación y ejecución

Ejecute el comando gcc para compilar:

cd ~/proyecto
gcc -o gomoku gomoku.c
./gomoku
Juego del Gomoku
✨ Revisar Solución y Practicar

Resumen

¡Felicitaciones! Has creado un sencillo juego del Gomoku utilizando el lenguaje de programación C. Los jugadores pueden tomar turnos para colocar sus piezas en un tablero de 15x15, y cuando un jugador tiene cinco piezas consecutivas, el programa los declarará ganador. ¡Disfruta jugando este juego basado en texto con tus amigos!