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.