Desenvolvimento de um Jogo da Velha Modular em Linguagem C

A criação de um jogo da velha (Tic-Tac-Toe) em C é um excelente exercício para consolidar conceitos de matrizes, funções, lógica condicional e modularização de código. Este projeto é estruturado em três arquivos distintos para manter a organização e facilitar a manutenção:

  • cabecalho.h: Contém as definições de constantes, inclusão de bibliotecas e protótipos de funções.
  • logica.c: Implementa as funcionalidades principais do jogo, como inicialização, desenho do tabuleiro e validação de vitórias.
  • main.c: Gerencia o fluxo principle do programa e o menu de interação com o usuário.

1. Definições e Protótipos (cabecalho.h)

Neste arquivo, definimos as dimensões do tabuleiro através de macros, o que permite alterar facilmente o tamanho do jogo para 4x4 ou 5x5 no futuro.

#ifndef CABECALHO_H
#define CABECALHO_H

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define LINHAS 3
#define COLUNAS 3

// Protótipos das funções
void limpar_tabuleiro(char matriz[LINHAS][COLUNAS]);
void renderizar_jogo(char matriz[LINHAS][COLUNAS]);
void lance_jogador(char matriz[LINHAS][COLUNAS]);
void lance_maquina(char matriz[LINHAS][COLUNAS]);
char verificar_estado(char matriz[LINHAS][COLUNAS]);

#endif

2. Implementação das Funcionalidades (logica.c)

Aqui, as funções são desenvolvidas. O tabuleiro é representado por uma matriz de caracteres preenchida inicialmente com espaços vazios.

#include "cabecalho.h"

void limpar_tabuleiro(char matriz[LINHAS][COLUNAS]) {
    for (int i = 0; i < LINHAS; i++) {
        for (int j = 0; j < COLUNAS; j++) {
            matriz[i][j] = ' ';
        }
    }
}

void renderizar_jogo(char matriz[LINHAS][COLUNAS]) {
    for (int i = 0; i < LINHAS; i++) {
        for (int j = 0; j < COLUNAS; j++) {
            printf(" %c ", matriz[i][j]);
            if (j < COLUNAS - 1) printf("|");
        }
        printf("\n");
        if (i < LINHAS - 1) {
            for (int j = 0; j < COLUNAS; j++) {
                printf("---");
                if (j < COLUNAS - 1) printf("+");
            }
            printf("\n");
        }
    }
}

void lance_jogador(char matriz[LINHAS][COLUNAS]) {
    int r, c;
    while (1) {
        printf("Sua vez. Digite Linha e Coluna (1-3): ");
        scanf("%d %d", &r, &c);
        if (r >= 1 && r <= LINHAS && c >= 1 && c <= COLUNAS) {
            if (matriz[r - 1][c - 1] == ' ') {
                matriz[r - 1][c - 1] = 'X';
                break;
            } else {
                printf("Posição ocupada!\n");
            }
        } else {
            printf("Coordenadas inválidas!\n");
        }
    }
}

void lance_maquina(char matriz[LINHAS][COLUNAS]) {
    printf("Turno do computador...\n");
    while (1) {
        int r = rand() % LINHAS;
        int c = rand() % COLUNAS;
        if (matriz[r][c] == ' ') {
            matriz[r][c] = 'O';
            break;
        }
    }
}

char verificar_estado(char matriz[LINHAS][COLUNAS]) {
    // Verificar linhas e colunas
    for (int i = 0; i < LINHAS; i++) {
        if (matriz[i][0] == matriz[i][1] && matriz[i][1] == matriz[i][2] && matriz[i][0] != ' ')
            return matriz[i][0];
        if (matriz[0][i] == matriz[1][i] && matriz[1][i] == matriz[2][i] && matriz[0][i] != ' ')
            return matriz[0][i];
    }
    // Diagonais
    if (matriz[0][0] == matriz[1][1] && matriz[1][1] == matriz[2][2] && matriz[1][1] != ' ')
        return matriz[1][1];
    if (matriz[0][2] == matriz[1][1] && matriz[1][1] == matriz[2][0] && matriz[1][1] != ' ')
        return matriz[1][1];

    // Empate ou Continua
    for (int i = 0; i < LINHAS; i++) {
        for (int j = 0; j < COLUNAS; j++) {
            if (matriz[i][j] == ' ') return 'C'; // Continua
        }
    }
    return 'E'; // Empate
}

3. Controle de Fluxo (main.c)

O arquivo principal contém o loop do jogo e a lógica de decisão para iniciar novass partidas ou encerrar o programa.

#include "cabecalho.h"

void iniciar_partida() {
    char tabuleiro[LINHAS][COLUNAS];
    char status = ' ';
    srand((unsigned int)time(NULL));
    
    limpar_tabuleiro(tabuleiro);
    renderizar_jogo(tabuleiro);

    while (1) {
        lance_jogador(tabuleiro);
        status = verificar_estado(tabuleiro);
        if (status != 'C') break;

        lance_maquina(tabuleiro);
        renderizar_jogo(tabuleiro);
        status = verificar_estado(tabuleiro);
        if (status != 'C') break;
    }

    renderizar_jogo(tabuleiro);
    if (status == 'X') printf("Parabéns! Você venceu!\n");
    else if (status == 'O') printf("A máquina venceu!\n");
    else printf("O jogo terminou em empate!\n");
}

int main() {
    int opcao;
    do {
        printf("\n--- JOGO DA VELHA ---\n");
        printf("1. Jogar\n0. Sair\nEscolha: ");
        scanf("%d", &opcao);
        switch (opcao) {
            case 1: iniciar_partida(); break;
            case 0: printf("Encerrando...\n"); break;
            default: printf("Opção inválida!\n");
        }
    } while (opcao != 0);
    return 0;
}

A lógica de verificação de vitória percorre a matriz buscando sequências idênticas de 'X' ou 'O'. Caso todas as células sejam preenchidas sem um vencedor, a função retorna 'E', sinalizando o empate. O uso de rand() em conjunto com time() garante que as jogadas do copmutador sejam imprevisíveis a cada execução.

Tags: C DesenvolvimentoDeJogos Algoritmos LinguagemC

Publicado em 7-19 18:23