Criando um Jogo Gomoku em C

CBeginner
Pratique Agora

Introdução

Neste projeto, criaremos um simples jogo de Gomoku baseado em texto usando a linguagem de programação C. Gomoku é um jogo de tabuleiro de estratégia para dois jogadores, onde o objetivo é ser o primeiro a obter cinco peças consecutivas em uma linha, seja horizontalmente, verticalmente ou diagonalmente. Desenvolveremos este jogo usando um tabuleiro de jogo 15x15.

👀 Pré-visualização

Gomoku Game

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como projetar e implementar um tabuleiro de xadrez usando um array bidimensional em C.
  • Como criar uma função principal (main function) para controlar o fluxo do jogo.
  • Como implementar funções para inicializar o jogo, imprimir o tabuleiro de xadrez e permitir que os jogadores joguem suas rodadas.
  • Como desenvolver uma função para verificar a condição de vitória no jogo.
  • Como compilar e executar o programa.

🏆 Conquistas

Após concluir este projeto, você será capaz de:

  • Trabalhar com arrays bidimensionais em C.
  • Projetar e implementar o fluxo de um jogo usando funções.
  • Verificar as condições de vitória em um jogo.
  • Compilar e executar um programa em C.
Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível intermediário com uma taxa de conclusão de 75%. Recebeu uma taxa de avaliações positivas de 80% dos estudantes.

Criar Arquivos do Projeto

Primeiramente, crie um novo arquivo chamado gomoku.c e abra-o no seu editor de código preferido.

cd ~/project
touch gomoku.c
✨ Verificar Solução e Praticar

Projetando um Tabuleiro de Xadrez

Usando 'O' e 'X' para representar as peças de xadrez

Primeiramente, precisamos de um tabuleiro de xadrez (15 * 15) para registrar a "situação" de cada posição no tabuleiro. Portanto, podemos definir um array chessboard[16][16]. Por que não [15][15]? É porque, desta forma, as coordenadas do array podem corresponder exatamente às linhas e colunas do tabuleiro de xadrez, tornando mais fácil escrever o código subsequente.

#include <stdio.h>

#define N 15

// Define um array e atribui 0 como o valor inicial a cada elemento.
int chessboard[N + 1][N + 1] = { 0 };
✨ Verificar Solução e Praticar

Função main

Antes de começar a escrever a função main, vamos considerar brevemente o fluxo típico de um jogo. Primeiro, entramos na interface principal do jogo, depois clicamos no botão iniciar para entrar no jogo, em seguida, exibimos a tela do jogo, determinamos a vitória ou a derrota e encerramos o jogo. Então, qual é o fluxo de um jogo como Go?

Primeiramente, entramos na tela de boas-vindas do jogo, depois inserimos Y para iniciar o jogo (não Y para sair do jogo), exibimos o tabuleiro do jogo e fazemos com que dois jogadores se revezem para colocar suas peças, depois determinamos a vitória ou a derrota (se houver 5 peças em linha).

// É usado para acompanhar se é a vez do jogador 1 ou do jogador 2, com um número ímpar representando a vez do jogador 1 e um número par representando a vez do jogador 2.
int whoseTurn = 0;

int main(void)
{
 // Uma função personalizada que inicializa o jogo, ou seja, exibe a tela de boas-vindas e entra no tabuleiro de exibição do jogo.
 initGame();

 // Este loop é para que dois jogadores se revezem.
 while (1)
 {
  // Cada ciclo incrementa em 1, para que duas pessoas possam se revezar.
  whoseTurn++;

  // Uma função personalizada que realiza uma operação de colocação de peça.
  playChess();
 }

 return 0;
}
✨ Verificar Solução e Praticar

Função initGame

Nesta função, as funcionalidades que queremos implementar são:

  • Exibir uma tela de boas-vindas simples.
  • Solicitar a entrada 'Y' e exibir o tabuleiro de xadrez após a entrada.
void initGame(void)
{
 char c;

 printf("Please input 'y' to enter the game:");
 c = getchar();
 if ('y' != c && 'Y' != c)
  exit(0);

 //Clear
 system("clear");

 //Here again we call a custom function whose function is to print out the chessboard.
 printChessboard();
}

Na função initGame, usamos as funções exit e system, então precisamos incluir o arquivo de cabeçalho stdlib.h no topo do programa.

#include <stdlib.h>
✨ Verificar Solução e Praticar

Função printChessboard

Nesta função, nosso objetivo é:

  • Imprimir os números das linhas e colunas e imprimir o tabuleiro de xadrez.
  • Se o valor do elemento da matriz for 0, imprimir um asterisco (*) para indicar que a posição está vazia.
  • Se o valor do elemento da matriz for 1, imprimir um círculo sólido (X), representando a peça do jogador 1.
  • Se o valor do elemento da matriz for 2, imprimir um círculo vazio (O), representando a peça do jogador 2.
void printChessboard(void)
{
 int i, j;

 for (i = 0; i <= N; i++)
 {
  for (j = 0; j <= N; j++)
  {
   if (0 == i)  //This prints out the column number.
    printf("%3d", j);
   else if (j == 0) //Print row number.
    printf("%3d", i);
   else if (1 == chessboard[i][j])
    printf("  X");
   else if (2 == chessboard[i][j])
    printf("  O");
   else
    printf("  *");
  }
  printf("\n");
 }
}
✨ Verificar Solução e Praticar

Função playChess

Nesta função, queremos alcançar o seguinte:

  • Solicitar ao jogador que insira a posição para colocar a peça de xadrez.
  • Se for a vez do jogador 1, atribuir o valor 1 ao elemento correspondente na matriz.
  • Se for a vez do jogador 2, atribuir o valor 2 ao elemento correspondente na matriz.
  • Após cada jogada, determinar se o jogador atual venceu.
void playChess(void)
{
 int i, j, winner;

 //Determine if it is player 1's turn or player 2's turn, and then assign the value to the corresponding element in the array.
 if (1 == whoseTurn % 2)
 {
  printf("Turn to player 1, please input the position, the format is line number + space number + column number: ");
  scanf("%d %d", &i, &j);
  chessboard[i][j] = 1;
 }
 else
 {
  printf("Turn to player 2, please input the position, the format is line number + space number + column number: ");
  scanf("%d %d", &i, &j);
  chessboard[i][j] = 2;
 }

 //Print the board again.
 system("clear");
 printChessboard(); //This function is called again.

 /*
 *The following section calls the custom function (the judge function).
 *It is used to determine whether the current player has won the move or not.
 */
 if (judge(i, j, whoseTurn))
 {
  if (1 == whoseTurn % 2)
   printf("Winner is player 1!\n");
  else
   printf("Winner is player 2!\n");
 }
}
✨ Verificar Solução e Praticar

Função judge

Parâmetros da função:

  • x: o número da linha da jogada atual
  • y: o número da coluna da jogada atual

Valor de retorno:

  • 1 ou 0. 1 significa que o jogador atual venceu após fazer a jogada.
int judge(int x, int y)
{
    int i, j;
    int t = 2 - whoseTurn % 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 &&
                    chessboard[x][y]==chessboard[row][col])
                    count+=1;
                else
                    break;
            }
        if(count>=5)
            return 1;
    }
    return 0;
}

Na função judge, existem 3 loops for aninhados, e seu propósito é determinar se existe uma linha de cinco peças consecutivas.

Uma linha de cinco peças pode estar em uma direção de linha, coluna ou diagonal. Aqui, usaremos uma abordagem de tentativa e erro, procurando por peças consecutivas nas direções horizontal, vertical e diagonal. Vamos pegar um exemplo:

Gomoku Game

No tabuleiro de xadrez acima, explicaremos o algoritmo para determinar se existe uma linha de cinco peças com base nas coordenadas (9, 10).

Primeiro, verificamos se existe uma linha diagonal de cinco peças começando em (9, 10). O processo é o seguinte:

  • Começando em (9, 10), procuramos na direção superior esquerda. As coordenadas que satisfazem a condição são (8, 9), (7, 8) e (6, 7). Como (5, 6) não satisfaz a condição, passamos para a próxima etapa.
  • Em seguida, procuramos na direção inferior direita e encontramos (10, 11), que é a única coordenada que satisfaz a condição.
  • Encontramos cinco pontos em linha reta, então o jogador 2 vence.

Se não houver uma linha de cinco peças na direção diagonal, verificamos as direções vertical e horizontal. Se nenhuma delas satisfizer a condição de vitória, significa que o jogador atual não pode vencer, e o jogo continuará.

✨ Verificar Solução e Praticar

Minha Peça de Xadrez foi 'Comida'

Não sei se alguém notou, mas em nosso jogo Cinco em Linha, mesmo que uma posição já esteja ocupada, ainda podemos "comer" a peça de xadrez original ao colocar a nossa.

Isso ocorre porque, ao escrever a função playChess, não verificamos a posição onde colocamos a peça de xadrez. Podemos modificar nosso código assim:

void playChess(void)
{
 int i, j, winner;
 if (1 == whoseTurn % 2)
 {
  printf("Turn to player 1, please input the position, the format is line number + space number + column number: ");
  scanf("%d %d", &i, &j);
  //debug
  while(chessboard[i][j] != 0)
  {
   printf("This position has been occupied, please input the position again: ");
   scanf("%d %d",&i, &j);
  }
  //debug
  chessboard[i][j] = 1;
 }
 else
 {
  printf("Turn to player 2, please input the position, the format is line number + space number + column number: ");
  scanf("%d %d", &i, &j);
  //debug
  while(chessboard[i][j] != 0)
  {
   printf("This position has been occupied, please input the position again: ");
   scanf("%d %d",&i, &j);
  }
  //debug
  chessboard[i][j] = 2;
 }

 system("clear");
 printChessboard();

 if (judge(i, j))
 {
  if (1 == whoseTurn % 2)
   printf("The winner is player 1!\n");
  else
   printf("The winner is player 2!\n");
 }
}

Adicionamos um loop na função, então, quando a posição já está ocupada, ela exibe uma mensagem e solicita uma nova entrada.

✨ Verificar Solução e Praticar

Por que não consigo Vencer para Sempre?

Quando há uma linha de cinco peças, e ele exibe "Jogador 1" ou "Jogador 2" vence, ele então exibe "É a vez do jogador *, por favor, insira a posição da sua peça...". Não é frustrante? Na verdade, tudo o que precisamos fazer é adicionar uma linha de código!

 if (judge(i, j))
 {
  if (1 == whoseTurn % 2)
  {
   printf("Winner is player 1!\n");
   exit(0); //debug
  }
  else
  {
   printf("Winner is player 2!\n");
   exit(0); //debug
  }
 }
}

Além de exibir o jogador que venceu após a vitória, saia do jogo com exit(0).

✨ Verificar Solução e Praticar

Compilação e Execução

Execute o comando gcc para compilar:

cd ~/project
gcc -o gomoku gomoku.c
./gomoku
Gomoku Game
✨ Verificar Solução e Praticar

Resumo

Parabéns! Você criou um jogo Gomoku simples usando a linguagem C. Os jogadores podem se revezar colocando suas peças em um tabuleiro 15x15, e quando um jogador tiver cinco peças consecutivas, o programa o declarará o vencedor. Divirta-se jogando este jogo baseado em texto com seus amigos!