Untitled
unknown
plain_text
8 months ago
11 kB
13
Indexable
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#define TMAX_MATRIZ 26
typedef struct estado {
char tabuleiro[TMAX_MATRIZ][TMAX_MATRIZ];
int linhas, colunas;
bool carregouTabuleiro;
bool looping;
struct estado *ultimoEstado; // ultimoEstado aponta pra um estado(que é o estado imediatamente anterior)
} Estado;
// Define um tipo de ponteiro chamado comando que aponta para uma função que retorna bool e recebe aqueles argumentos
// Comando já é o próprio ponteiro
typedef bool (*Comando)(char cmd, char *arg, Estado *e);
void guardarEstado(Estado *atual) {
Estado *estadoGuardar = malloc(sizeof(Estado)); // aloca a memoria pro estado que é pra guardar
if (estadoGuardar != NULL) {
memcpy(estadoGuardar, atual, sizeof(Estado)); // copia o estado atual para o o estadoGuardar
atual->ultimoEstado = estadoGuardar; // atualiza o ponteiro do estado atual para o estado que foi guardado
}
}
void mostrarTabuleiro(Estado *e) {
printf("\n");
for (int i = 0; i < e->linhas; i++) {
for (int j = 0; j < e->colunas; j++) {
printf("%c ", e->tabuleiro[i][j]);
}
printf("\n");
}
printf("\n");
}
bool cmdLer(char cmd, char *arg, Estado *e) {
if (cmd != 'l' || arg == NULL) return false;
// ficheiro é ponteiro para o tipo file (fopen retorna endereco)
FILE *ficheiro = fopen(arg, "r");
if (ficheiro == NULL) {
printf("Erro ao abrir %s\n", arg);
return true; // o comando foi reconhecido apesar de ter havido erro
}
// armazena no estado -> linhas
if (fscanf(ficheiro, "%d %d", &(e->linhas), &(e->colunas)) != 2) {
fclose(ficheiro);
printf("Formato inválido no ficheiro\n");
return true;
}
if (e->linhas <= 0 || e->colunas <= 0 || e->linhas > TMAX_MATRIZ || e->colunas > TMAX_MATRIZ) {
printf("As dimensões do tabuleiro são inválidas\n");
fclose(ficheiro);
return true;
}
while (fgetc(ficheiro) != '\n'); // retira os espaços e passa o "cursor" para depois do \n
char linha[TMAX_MATRIZ + 2]; // adiciona o \n e o \0
bool linhaValida = true;
for (int i = 0; i < e->linhas && linhaValida; i++) {
if (fgets(linha, sizeof(linha), ficheiro) == NULL) {
printf("Erro ao ler a linha %d\n", i+1);
linhaValida = false;
}
// remove o '\n' no final (strcspn retorna o indice do \n) p possibilitar futura comparacao
linha[strcspn(linha, "\n")] = '\0';
for (int j = 0; j < e->colunas && linhaValida; j++) {
if (linha[j] == '\0') {
printf("Aviso linha %d menor que o esperado\n", i); // se introduziu uma dimensao e depois a linha era dif
linhaValida = false;
}
e->tabuleiro[i][j] = linha[j];
}
}
fclose(ficheiro);
if (linhaValida == false) return true; // se a linha n era valida n carrega o tabuleiro mas identifica o comando
e->carregouTabuleiro = true;
printf("\nTabuleiro inicial %dx%d carregado:\n", e->linhas, e->colunas);
mostrarTabuleiro(e);
return true;
}
bool cmdGravar(char cmd, char *arg, Estado *e) {
if (cmd != 'g' || arg == NULL) return false;
// ficheiro é ponteiro para o tipo file (fopen retorna endereco)
FILE *ficheiro = fopen(arg, "w");
fprintf(ficheiro, "%d %d\n", e->linhas, e->colunas);
for (int i = 0; i < e->linhas; i++) {
for (int j = 0; j < e->colunas; j++) {
fprintf(ficheiro, "%c", e->tabuleiro[i][j]);
}
fprintf(ficheiro, "\n"); // a cada linha que escrever escreve o \n
}
fclose(ficheiro);
printf("Tabuleiro gravado em %s\n", arg);
return true;
}
bool cmdSair(char cmd, char *arg, Estado *e) {
if (cmd == 's' && arg == NULL) {
e->looping = false;
return true; // comando foi reconhecido
}
return false; // comando não foi reconhecido
}
bool cmdUndo(char cmd, char *arg, Estado *e) {
if (cmd == 'd' && arg == NULL) {
if (e->ultimoEstado == NULL) {
printf("Não há estados anteriores para desfazer.\n");
return true; // comando foi reconhecido apesar de n fzr nd
}
Estado *estadoAnterior = e->ultimoEstado; // pega o estado anterior
memcpy(e, estadoAnterior, sizeof(Estado)); // restaura o estado anterior
e->ultimoEstado = estadoAnterior->ultimoEstado; // digo que o estado anterior passa a ser o anterior do anterior
free(estadoAnterior); // libera a memória do estado anterior
mostrarTabuleiro(e);
return true;
}
return false;
}
bool cmdPintar(char cmd, char *arg, Estado *e) {
if (cmd != 'b' || e->carregouTabuleiro == false) return false;
int x;
char y;
if (sscanf(arg, "%c%d", &y, &x) != 2) return false;
int coluna = y - 'a'; // 'a' corresponde a 0, 'b' a 1 etc
if (x >= 0 && x < e->linhas && coluna >= 0 && coluna < e->colunas) {
guardarEstado(e); // guarda o estado atual (antes de fazer a alteração)
e->tabuleiro[x][coluna] = toupper(e->tabuleiro[x][coluna]);
mostrarTabuleiro(e);
return true;
}
return false;
}
bool cmdRiscar(char cmd, char *arg, Estado *e) {
if (cmd != 'r' || e->carregouTabuleiro == false) return false;
int x;
char y;
if (sscanf(arg, "%c%d", &y, &x) != 2) return false;
int coluna = y - 'a';
if (x >= 0 && x < e->linhas && coluna >= 0 && coluna < e->colunas) {
guardarEstado(e); // guarda o estado atual (antes de fazer a alteração)
e->tabuleiro[x][coluna] = '#';
mostrarTabuleiro(e);
return true;
}
return false;
}
bool cmdVerificarRestricoes(char cmd, char *arg, Estado *e) {
if (cmd != 'v' || arg != NULL || !e->carregouTabuleiro) return false;
bool violacoesEncontradas = false;
// 1. Verificar letras brancas repetidas na mesma linha/coluna
for (int i = 0; i < e->linhas; i++) {
for (int j = 0; j < e->colunas; j++) {
char celula = e->tabuleiro[i][j];
// Verificar apenas células brancas (letras maiúsculas)
if (isupper(celula)) {
// Verificar mesma linha
for (int k = 0; k < e->colunas; k++) {
if (k != j && e->tabuleiro[i][k] == celula) {
printf("Violação: Letra '%c' repetida na linha %d (colunas %c e %c)\n",
celula, i+1, 'a'+j, 'a'+k);
violacoesEncontradas = true;
}
}
// Verificar mesma coluna
for (int k = 0; k < e->linhas; k++) {
if (k != i && e->tabuleiro[k][j] == celula) {
printf("Violação: Letra '%c' repetida na coluna %c (linhas %d e %d)\n",
celula, 'a'+j, i+1, k+1);
violacoesEncontradas = true;
}
}
}
}
}
// 2. Verificar células riscadas vizinhas ortogonalmente
for (int i = 0; i < e->linhas; i++) {
for (int j = 0; j < e->colunas; j++) {
if (e->tabuleiro[i][j] == '#') {
// Verificar vizinhança ortogonal (cima, baixo, esquerda, direita)
if (i > 0 && e->tabuleiro[i-1][j] == '#') { // Cima
printf("Violação: Células riscadas vizinhas em (%c%d, %c%d)\n",
'a'+j, i+1, 'a'+j, i);
violacoesEncontradas = true;
}
if (i < e->linhas-1 && e->tabuleiro[i+1][j] == '#') { // Baixo
printf("Violação: Células riscadas vizinhas em (%c%d, %c%d)\n",
'a'+j, i+1, 'a'+j, i+2);
violacoesEncontradas = true;
}
if (j > 0 && e->tabuleiro[i][j-1] == '#') { // Esquerda
printf("Violação: Células riscadas vizinhas em (%c%d, %c%d)\n",
'a'+j, i+1, 'a'+j-1, i+1);
violacoesEncontradas = true;
}
if (j < e->colunas-1 && e->tabuleiro[i][j+1] == '#') { // Direita
printf("Violação: Células riscadas vizinhas em (%c%d, %c%d)\n",
'a'+j, i+1, 'a'+j+1, i+1);
violacoesEncontradas = true;
}
}
}
}
if (!violacoesEncontradas) {
printf("Nenhuma violação encontrada. Tabuleiro válido!\n");
}
return true;
}
#ifndef TESTING
int main() {
Estado estado = {0}; // inicializa todos os campos com zero
estado.looping = true;
// array de comandos (ponteiros para funções)
Comando comandos[] = {cmdLer,cmdGravar,cmdSair,cmdUndo,cmdPintar,cmdRiscar,cmdVerificarRestricoes,NULL};
char input[100];
printf("Escreva l [arquivo] para carregar o tabuleiro:\n");
while (estado.looping) {
// le o que foi introduzido (stdin) e armazena em input (para quando chega no \n ou ultrapassa o sizeof) e retorna o ponteiro
if (fgets(input, sizeof(input), stdin) == NULL) estado.looping = false;
// Quando escrevemos b a1 e damos enter o ultimo caractere é o \n, precisamos remove-lo.
int length = strlen(input);
if (length > 0 && input[length-1] == '\n') { // se for \n substitui.
input[length-1] = '\0';
}
char cmd = '\0';
char arg[100] = {0};
// vai ler da string input (%99s para usar no máx 99 caracteres (precisa de espaço pro \0 no fim))
int argsLido = sscanf(input, "%c %99s", &cmd, arg);
bool temArgumento = (cmd == 'l' || cmd == 'g' || cmd == 'b' || cmd == 'r');
// ver se leu o comando
if (argsLido < 1) {
printf("Comando inválido!\n");
}
// ver se era necessário argumento e não havia
else if (temArgumento && argsLido < 2) {
printf("Este comando requer um argumento!\n");
}
else {
bool reconhecido = false; // é uma flag (reconhecido só vira true quando um comando é reconhecido (é bem passado))
// enquanto não chegar ao fim da lista de comandos e não houver nenhum que foi reconhecido procura o comando
for (int i = 0; comandos[i] != NULL && !reconhecido; i++) {
if (argsLido > 1) {
reconhecido = comandos[i](cmd, arg, &estado);
}
else {
// se não tiver argumentos passa o ponteiro nulo
reconhecido = comandos[i](cmd, NULL, &estado);
}
}
if (!reconhecido) { // se não houve nenhum reconhecido então é inválido
printf("Comando inválido.\n");
}
}
}
printf("Jogo encerrado.\n");
return 0;
}
#endifEditor is loading...
Leave a Comment