Criando um Jogo da Velha Simples em C

CBeginner
Pratique Agora

Introdução

Neste projeto, você aprenderá como criar um jogo simples de Jogo da Velha (Tic-Tac-Toe) em C. O jogo será jogado entre dois jogadores que se revezam para marcar os espaços em uma grade 3x3. O primeiro jogador a ter três de suas marcas em uma linha, coluna ou diagonal é o vencedor. Se todos os espaços forem preenchidos e nenhum jogador tiver três marcas em uma linha, o jogo é um empate.

👀 Pré-visualização

Pré-visualização do jogo Jogo da Velha

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como criar um tabuleiro de jogo e inicializá-lo com espaços vazios.
  • Como implementar funções para limpar a tela, exibir o tabuleiro de jogo e verificar se o jogo acabou.
  • Como determinar o vencedor do jogo.
  • Como implementar o loop principal do jogo para permitir que os jogadores se revezem e interajam com o jogo.

🏆 Conquistas

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

  • Criar e manipular arrays em C.
  • Usar loops e condicionais para implementar a lógica do jogo.
  • Interagir com o usuário através da interface de linha de comando.
  • Organizar o código em funções para melhor modularidade e legibilidade.

Criar os Arquivos do Projeto

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

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

Definir Constantes

Agora, precisamos escrever o código C. O primeiro passo é incluir os arquivos de cabeçalho:

#include <stdio.h>
#include <stdbool.h>

Declarar o tamanho do tabuleiro:

char board[3][3];
✨ Verificar Solução e Praticar

Inicializar o Tabuleiro do Jogo

Inicialize o tabuleiro do jogo com espaços vazios.

void initializeBoard() {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            board[i][j] = ' ';
        }
    }
}

O primeiro for é um loop externo que usa a variável i para iterar de 0 a 2, representando o número de linhas no tabuleiro do jogo. O segundo for é um loop interno que usa a variável j para iterar de 0 a 2, representando o número de colunas no tabuleiro do jogo, por sua vez.

board[i][j] = ' ' No loop interno, define o conteúdo das casas na linha i e coluna j do tabuleiro do jogo para o caractere espaço (' '). Isso significa que todo o tabuleiro do jogo é inicializado para um estado vazio, sem jogadores jogando no tabuleiro.

✨ Verificar Solução e Praticar

Limpar a Tela Após Cada Rodada

Implemente uma função para limpar a tela após cada jogada.

void clearScreen() {
    printf("\033[H\033[J");
}

A função clearScreen é usada principalmente para limpar o conteúdo da tela do terminal ou console, a fim de limpar o estado anterior do jogo e a saída no final de cada jogada. Isso é feito imprimindo uma sequência de escape especial para um fluxo de saída padrão (geralmente uma janela de terminal) que limpa o conteúdo de texto na tela.

printf("\033[H\033[J") usa sequências de escape ANSI:

  • \033 é a representação octal do caractere de escape ASCII, que representa o início da sequência.
  • [H indica mover o cursor para o canto superior esquerdo da tela, equivalente a posicionar o cursor na primeira linha e na primeira coluna da tela.
  • [J significa limpar a tela. Ele limpará todo o conteúdo de texto após a posição do cursor, incluindo o estado anterior do jogo e a saída.

Isso significa que cada vez que a função clearScreen é chamada, ela limpará o conteúdo da tela atual do terminal ou console para exibir o novo estado do jogo antes da próxima sessão de jogo, resultando em uma interface mais limpa.

✨ Verificar Solução e Praticar

Exibir o Tabuleiro do Jogo

Crie a função printBoard para exibir visualmente o estado do tabuleiro do jogo atual na tela, permitindo que o jogador entenda a posição das peças no tabuleiro. Esta é uma parte do jogo usada em jogos de jogo da velha para mostrar o estado do jogo.

void printBoard() {
    printf("\n");
    printf("  1   2   3\n");
    printf("1 %c | %c | %c\n", board[0][0], board[0][1], board[0][2]);
    printf("  ---------\n");
    printf("2 %c | %c | %c\n", board[1][0], board[1][1], board[1][2]);
    printf("  ---------\n");
    printf("3 %c | %c | %c\n", board[2][0], board[2][1], board[2][2]);
}
✨ Verificar Solução e Praticar

Verificar se o Jogo Terminou

Implemente uma função chamada isGameOver para determinar se o jogo da velha terminou e retornar um valor booleano (true ou false).

bool isGameOver() {
    // Check rows
    for (int i = 0; i < 3; i++) {
        if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
            return true;
        }
    }

    // Check columns
    for (int j = 0; j < 3; j++) {
        if (board[0][j] != ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
            return true;
        }
    }

    // Check diagonals
    if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
        return true;
    }
    if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
        return true;
    }

    // Check for a draw
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[i][j] == ' ') {
                return false;
            }
        }
    }

    return true;
}

Verificar se uma linha tem um vencedor: Se todas as três células em uma linha forem ocupadas pelo mesmo jogador, há um vencedor e a função retorna true.

Verificar se uma coluna tem um vencedor: Se todas as três células em uma coluna forem ocupadas pelo mesmo jogador, há um vencedor e a função retorna true.

Verificar se há um vencedor na diagonal: Se todos os três quadrados em qualquer diagonal forem ocupados pelo mesmo jogador, há um vencedor e a função retorna true.

Verificar se houve empate: Finalmente, a função verifica se todas as células foram ocupadas, mas não há vencedor. Se este for o caso, o jogo é um empate e a função retorna true.

✨ Verificar Solução e Praticar

Determinar o Vencedor

Uma função chamada getWinner é definida com o propósito de determinar se há um vencedor em um jogo da velha e retornar a bandeira do vencedor (X ou O), ou um espaço (' ') se não houver vencedor.

char getWinner() {
    // Check rows
    for (int i = 0; i < 3; i++) {
        if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
            return board[i][0];
        }
    }

    // Check columns
    for (int j = 0; j < 3; j++) {
        if (board[0][j] != ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
            return board[0][j];
        }
    }

    // Check diagonals
    if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
        return board[0][0];
    }
    if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
        return board[0][2];
    }

    return ' '; // No winner
}
✨ Verificar Solução e Praticar

Implementar o Loop Principal do Jogo

Escreva o loop principal do jogo para permitir que os jogadores se revezem e interajam com o jogo.

int main() {
    initializeBoard();
    int currentPlayer = 1;

    while (1) {
        clearScreen();
        printf("Current board state:\n");
        printBoard();

        int row, col;
        printf("Player %d, please enter a row and column (e.g., 1 2):", currentPlayer);

        while (scanf("%d %d", &row, &col) != 2) {
            printf("Invalid input, please try again: ");
            while (getchar() != '\n');
        }

        if (row < 1 || row > 3 || col < 1 || col > 3 || board[row - 1][col - 1] != ' ') {
            printf("Invalid move, please try again.\n");
        } else {
            if (currentPlayer == 1) {
                board[row - 1][col - 1] = 'X';
                currentPlayer = 2;
            } else {
                board[row - 1][col - 1] = 'O';
                currentPlayer = 1;
            }
        }

        if (isGameOver()) {
            clearScreen();
            printf("Game over!\n");
            printBoard();
            char winner = getWinner();
            if (winner != ' ') {
                printf("Player %c wins!\n", winner);
            } else {
                printf("It's a draw!\n");
            }
            break;
        }
    }

    return 0;
}

Chame a função initializeBoard para inicializar o tabuleiro do jogo, definindo todos os quadrados como espaços.

Crie uma variável inteira currentPlayer para controlar de quem é a vez. A configuração inicial é 1, indicando o jogador 1.

No loop principal while (1):

  • Chame a função clearScreen para limpar a tela para que a exibição seja atualizada após cada turno.
  • Chame a função printBoard para imprimir o tabuleiro atual do jogo.

Entrada do jogador: Solicita ao jogador atual que insira as coordenadas da linha e da coluna por meio da função scanf, por exemplo, Player 1, please enter a row and column (e.g., 1 2):. Se a entrada for inválida (não dois inteiros), a mensagem Invalid input, please try again: é exibida e o buffer de entrada é limpo até que uma entrada válida seja obtida.

Validação da entrada: Em seguida, verifique se as coordenadas da linha e da coluna inseridas são válidas (em uma escala de 1 a 3) e se a posição selecionada é um espaço. Se a entrada for inválida ou o local selecionado já estiver ocupado, a mensagem Invalid move, please try again. é exibida e o jogador é solicitado a reinserir.

Jogada: Se a entrada for válida, o código coloca um X ou um O na posição correspondente no tabuleiro do jogo com base no jogador atual (1 ou 2).

Detecção do fim do jogo: O código então chama a função isGameOver() para verificar se o jogo acabou. Se o jogo terminar, a tela limpa e uma mensagem de que o jogo acabou é exibida, incluindo um vencedor (se houver) ou um empate.

Resultado do jogo: Se houver um vencedor, o código dirá Player X wins! ou Player O wins!, dependendo de qual jogador é o vencedor. Se não houver vencedor, o código dirá It's a draw! indicando um empate.

✨ Verificar Solução e Praticar

Compilar e Executar o Projeto

Execute o comando gcc para compilar:

cd ~/project
gcc -o tictactoe tictactoe.c
./tictactoe
Demonstração do jogo da velha
✨ Verificar Solução e Praticar

Resumo

Parabéns! Você criou com sucesso um jogo da velha simples em C. Os jogadores podem se revezar e marcar suas posições na grade 3x3. O jogo verifica se há um vencedor ou um empate após cada turno e exibe o resultado de acordo. Este projeto simples demonstra os conceitos básicos da programação em C e pode servir como um ponto de partida para o desenvolvimento de jogos mais complexos.