Untitled

 avatar
unknown
plain_text
15 days ago
11 kB
7
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;
}
#endif
Editor is loading...
Leave a Comment