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

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

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á.
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.
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).
Compilação e Execução
Execute o comando gcc para compilar:
cd ~/project
gcc -o gomoku gomoku.c
./gomoku

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!



