Search code examples
cconways-game-of-life

How to initialize a 2D grid from a file for a Game Of Life in C?


For a school project, I am trying to build a Game Of Life in C to play with in the terminal.

The program should do the following sequence of actions:

  • Take a file, open it and get the dimensions (working)
  • Initialize a grid corresponding to the file that looks good (wip - help)
  • display that grid (should work)
  • compute all next generation (wip) and print them too

So far, with my teammate, we built some helper functions to parse the file and get the dimensions of the grid we need to allocate for the game to work, print the function, do the necessary stuff in order to compute the next generation... However I can't seem to get my function to initialize my grid by mapping characters in the file to characters in the terminal.

Can anyone help me ? We are trying to do it the easy way since we have not really seen any advanced concepts, so the solution must not use any advanced concepts too... If someone finds something, it would really help us a lot!

Here is the code with the relevant parts (everything else should not interfere, and sorry if the comments are in French) (if anyone wants the full code, I'll post it):

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define INVALID_CHAR_READING 101
#define CANT_OPEN_FILE 201
#define FAIL_UPDATE 102

/// @brief Obtient les dimensions de la grille du jeu de la vie
/// @param filename le nom du fichier
/// @param dimensions un tableau de dimensions non instancié
/// @return un tableau d'entier contenant dans l'ordre lignes, colonnes
void get_dimensions(char filename[], int *dimensions)
{
    errno = 0;
    FILE *content = fopen(filename, "r");
    if (content == NULL)
    {
        fprintf(stderr, "Une erreur s'est produite a l'ouverture du fichier %s : %s\n", filename, strerror(errno));
        exit(CANT_OPEN_FILE);
    }
    int lines = 0;
    int columns = 0;
    char buffer;
    for (buffer = getc(content); buffer != EOF; buffer = getc(content))
    {
        if (buffer == '\n')
        {
            lines = lines + 1;
        }
        columns += 1;
    }

    columns /= lines;
    // columns /= 2; // on a compté les espaces

    fclose(content);
    dimensions[0] = lines;
    dimensions[1] = columns;
}

void init_grid(char filename[], char **grid, int *dimensions)
{
    errno = 0;
    FILE *content = fopen(filename, "r");
    if (content == NULL)

    {
        fprintf(stderr, "Une erreur s'est produite a l'ouverture du fichier %s : %s\n", filename, strerror(errno));
        exit(CANT_OPEN_FILE);
    }
    int line = 0;
    int column = 0;
    char ch;
    while ((ch = fgetc(content)) != EOF)
    {
        while (column < dimensions[1] && line < dimensions[0])
        {
            switch (ch)
            {
            case 'o':
                grid[line][column] = ' ';

            case 'x':
                grid[line][column] = ' ';

            case '\n':
                grid[line][column] = ch;

            default:
                fprintf(stderr, "Une erreur s'est produite lors de la lecture du caractère à l'initialisation de la grille :/ Votre fichier est peut-être incompatible avec le parseur...\n");
                exit(INVALID_CHAR_READING);
            }
            column++;
        };
        line++;
    }
}

void print_grid(char **grid, int *dimensions)
{
    for (int i = 0; i < dimensions[0]; i++)
    {
        for (int j = 0; j < dimensions[1]; j++)
        {
            printf("%c", grid[i][j]);
        }
    };
}

void gol()
{
    char filename[256];
    printf("Quel est le nom du fichier que vous cherchez à ouvrir ? (Merci d'entrer le chemin complet du fichier)\n> ");
    scanf("%s", &filename);
    // printf("%s", filename);
    int dimensions[2];

    get_dimensions(filename, dimensions);
    printf("%d %d\n", dimensions[0], dimensions[1]);
    char grid[dimensions[1]][dimensions[0]];
    // printf("%d", sizeof(grid));

    init_grid(filename, &grid, dimensions);
    print_grid(grid, dimensions);
}

int main()
{
    printf("GOL\n===\nQue voulez-vous faire ? (h: aide, f: chemin de fichier, r: random)\n> ");
    char option;
    scanf("%c", &option);

    switch (option)
    {
    case 'h':
        help();
        break;

    case 'f':
        gol(); // Nécéssaire sinon bypass du contrôle d'initialisation??
        break;

    case 'r':
        // TODO implémentation de la grille random
        break;

    default:
        printf("\nVous n'avez pas rentré d'option valable :( Merci de relancer le programme pour recommencer ;)\n\n");
        exit(INVALID_CHAR_READING);
    }

    return 0;
}

Solution

  • I would recommend not using the multi-dimensional array, but instead do something like:

    char grid[ dimensions[1] * dimensions[0] ];
    

    and:

    void
    init_grid(const char *filename, char *grid, int *dimensions)
    {
        errno = 0;
        FILE *content = fopen(filename, "r");
        if( content == NULL ){
            fprintf(stderr, "Une erreur s'est produite a l'ouverture du fichier %s : %s\n", filename, strerror(errno));
            exit(CANT_OPEN_FILE);
        }
        int ch;
        char *end = grid + dimensions[0] * dimensions[1];
        while ((ch = fgetc(content)) != EOF && grid < end ){
            *grid++ = ch == '\n' ? '\n' : ' ';
        }
        if( fclose(content) ){
            perror(filename);
        }
    }
    

    If you do want to keep the multi-dimensional array, you can do things like:

    char grid[dimensions[1]][dimensions[0]];                                    
    char *g = &grid[0][0];
    

    and then pass g to helper functions. Be aware of any compiler warnings you get about mismatched types in parameters.