Невозможно прочитать лабиринт символов и поместить его в двумерный массив - PullRequest
0 голосов
/ 06 января 2019

Я играю в лабиринт. В середине игры есть функция, которая сохраняет прогресс и печатает все в файл. Но теперь я хочу прочитать лабиринт, символ и т. Д. Из файла и поместить его в двумерный массив, но я не могу это сделать.

Массив объявлен глобально (из-за его использования в нескольких функциях) и является лабиринтом [30] [30]. N также объявляется глобально как переменный размер, но в этот момент оно должно быть равно 10. filePath также и дает имя файла.

Это то, как они объявлены глобально.

int N = 10;
char* filePath = "./save.txt";
char maze[30][30];

Это функция чтения:

void ler()                   
{
    int i, j;
    ex=1; ey=0;
    sx=N-2; sy=N-1;

    int aux;
    FILE *fp = fopen(filePath, "r");
    if (fp == NULL)
    {
        printf("Unable to perform.");
    return;
    }
    system("cls");

    for(i=0 ; i<N ; i++)
    {
        for(j=0 ; j<N ; j++)
        {
        fscanf(fp, "%c", maze[j][i]);
        }
    }

    for (i = 0; i < N; i++) 
    {
    for (j = 0; j < N; j++) 
    {
        printf("%c", maze[j][i]);   //Double print just for visuals
        printf("%c", maze[j][i]);
    }
    printf("\n");
    }
    fclose(fp);
}

Это функция сохранения:

void save(char maze[30][30]){
int i,j;
FILE *fp = fopen(filePath, "w");
if(fp==NULL){
    printf("Unable to perform.");
    return; 
    }

for(i=0 ; i<N ; i++){
    for(j=0 ; j<N ; j++){
        fprintf(fp, "%c", maze[j][i]);
        fprintf(fp, "%c", maze[j][i]);
    }
    fprintf(fp, "\n", maze[j][i]);
}
fclose(fp);}

В этот момент должна быть возможность напечатать лабиринт, но он этого не делает.

Как выглядит файл save.txt после сохранения Странный II - это кахарктер, а другой похож на рекорд.

1 Ответ

0 голосов
/ 06 января 2019

Использование многомерных массивов в C на самом деле больше боли, чем они того стоят. Гораздо лучшим вариантом является использование структуры с динамически распределенным массивом, описывающим лабиринт, и функций доступа для изучения и изменения ячеек лабиринта. Вместо того, чтобы помещать маркеры в данные лабиринта, вы можете поместить координаты начала / конца / текущего местоположения в структуру.

(я понимаю, что это не отвечает на поставленный ОП вопрос, но является ответом на основную проблему, которую ОП пытается решить.)

Рассмотрим следующий пример. Он ограничивает размер лабиринта 255 × 255, но поскольку каждая координата и ячейка лабиринта всегда составляют всего один байт, файлы сохранения переносимы между архитектурами, так как нет порядка байтов (порядка байтов), о котором следует беспокоиться. (Вам, как программисту, все же нужно выбрать использовать только коды 0..255 в лабиринте, чтобы сохранить переносимость данных; приведенные ниже функции не будут обеспечивать это.)

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

typedef struct {
    unsigned char  row;
    unsigned char  col;
} mazepoint;

typedef struct {
    unsigned char  rows;
    unsigned char  cols;
    unsigned char *cell;
    mazepoint      player;
} maze;

#define  OUTSIDE    0

static inline unsigned char  maze_get(maze *const  m,
                                      const int    row,
                                      const int    col)
{
    if (m &&
        row >= 0 && row < (int)(m->rows) &&
        col >= 0 && col < (int)(m->cols))
        return m->cell[ (size_t)col + (size_t)(m->cols) * (size_t)row ];
    else
        return OUTSIDE;
}

static inline unsigned char  maze_set(maze *const          m,
                                      const int            row,
                                      const int            col,
                                      const unsigned char  val)
{
    if (m &&
        row >= 0 && row < (int)(m->rows) &&
        col >= 0 && col < (int)(m->cols))
        return m->cell[ (size_t)col + (size_t)(m->cols) * (size_t)row ] = val;
    else
        return OUTSIDE;
}

static inline void maze_free(maze *const m)
{
    if (m) {
        free(m->cell);
        m->rows = 0;
        m->cols = 0;
        m->cell = NULL;
    }
}

int maze_create(maze *const    m,
                const int      rows,
                const int      cols)
{
    size_t          cells = (size_t)rows * (size_t)cols;
    unsigned char  *cell;

    if (!m)
        return -1;  /* NULL reference to a maze variable! */

    if (rows < 1 || rows > 255 ||
        cols < 1 || cols > 255)
        return -1;  /* Invalid number of rows or columns! */

    cell = malloc(cells); /* sizeof (unsigned char) == 1. */
    if (!cell)
        return -1;

    /* Initialize all maze cells to OUTSIDE. */
    memset(cell, OUTSIDE, cells);

    m->rows = rows;
    m->cols = cols;
    m->cell = cell;

    /* Let's initialize player location to upper left corner. */
    m->player.row = 0;
    m->player.col = 0;

    return 0; /* Success. */
}

int maze_save(maze *const m, const char *filename)
{
    size_t  cells;
    FILE   *out;

    if (!m || m->rows < 1 || m->cols < 1)
        return -1; /* No maze to save! */
    if (!filename || !filename[0])
        return -1; /* NULL or empty filename! */

    cells = (size_t)(m->rows) * (size_t)(m->cols);

    out = fopen(filename, "wb");
    if (!out)
        return -1; /* Cannot open file for writing! */

    do {

        /* First byte is the number of rows. */
        if (fputc(m->rows, out) == EOF)
             break;

        /* Second byte is the number of columns. */
        if (fputc(m->cols, out) == EOF)
             break;

        /* rows*cols bytes of maze data follows. */
        if (fwrite(m->cell, 1, cells, out) != cells)
            break;

        /* Player location follows. */
        if (fputc(m->player.row, out) == EOF)
            break;
        if (fputc(m->player.col, out) == EOF)
            break;

        /* You can save additional data at this point. */

        /* That completes the save file. Ensure it is correctly saved. */
        if (fflush(out))
            break;
        if (fclose(out))
            break;

        /* Maze successfully saved. */
        return 0;

    } while (0);

    /* Save failed. */
    fclose(out);
    remove(filename);
    return -1;
}

int maze_load(maze *const m, const char *filename)
{
    size_t         cells;
    unsigned char *cell;
    int            rows, cols, r, c;
    FILE          *in;

    if (!m)
        return -1; /* No reference to a maze variable to load into! */

    /* Just in case, we clear the maze first. Might help finding bugs! */
    m->rows = 0;
    m->cols = 0;
    m->cell = NULL;

    if (!filename || !filename[0])
        return -1; /* NULL or empty filename! */

    in = fopen(filename, "rb");
    if (!in)
        return -1; /* Cannot open file for reading. */

    rows = fgetc(in);
    cols = fgetc(in);
    if (rows == EOF || rows < 1 || rows > 255 ||
        cols == EOF || cols < 1 || cols > 255) {
        fclose(in);
        return -1; /* Not a saved maze! */
    }

    cells = (size_t)(rows) * (size_t)(cols);
    cell = malloc(cells);
    if (!cell) {
        fclose(in);
        return -1; /* Not enough memory available! */
    }

    do {
        /* Read maze cell data. */
        if (fread(cell, 1, cells, in) != cells)
            break;

        /* Player location. */
        r = fgetc(in);
        c = fgetc(in);
        if (r == EOF || r < 0 || r > 255 ||
            c == EOF || c < 0 || c > 255)
            break;
        m->player.row = r;
        m->player.col = c;

        /* Load other saved data here. */

        /* All data read successfully. */
        fclose(in);

        m->rows = rows;
        m->cols = cols;
        m->cell = cell;

        return 0;
    } while (0);

    /* Read error. */
    fclose(in);
    free(cell);
    return -1;
}

В вашей собственной программе вы бы создали лабиринт следующим образом:

    maze  m;

    /* Create a 20-row, 30-column maze. */
    if (maze_create(&m, 20, 30)) {
        /* Failed to create maze! Show an error message. */
        exit(EXIT_FAILURE);
    }

Чтобы сохранить лабиринт, чтобы сказать maze.dat, вы используете

    m.player.row = /* row where the player is */
    m.player.col = /* column where the player is */
    if (maze_save(&m, "maze.dat")) {
        /* Failed! Show an error message. */
        exit(EXIT_FAILURE);
    }

Если вы посмотрите на пример кода, вы можете добавить дополнительные данные, особенно точки, такие как player, для сохранения и загрузки вместе с самими ячейками лабиринта.

Чтобы уничтожить лабиринт, когда он больше не нужен, используйте

    maze_free(&m);

Чтобы загрузить сохраненный лабиринт, скажем, из maze.dat, используйте

    if (maze_load(&m, "maze.dat")) {
        /* Failed! Show an error message. */
        exit(EXIT_FAILURE);
    }
    /* Restore player place from m.player.row and m.player.col */

Функция доступа maze_get() не ограничена действительными координатами (от 0 до rows-1 или cols-1 включительно). Если вы изучите за пределами самого лабиринта, он просто вернет значение макроса OUTSIDE. Например,

    if (maze_get(&m, row, col) == 5) {
        /* That cell has value 5 */
    } else {
        /* Either the cell has a different value,
           or row,col is outside the maze. */
    }

Точно так же вы можете попытаться установить любое значение ячейки безопасно. Однако он будет «прилипать» только в том случае, если он находится в допустимом диапазоне координат лабиринта; в другом месте он вернет OUTSIDE:

    if (maze_set(&m, row, col, 5) == 5) {
        /* Changed cell value to 5 */
    } else {
        /* row,col is outside the maze. */
    }

Причина, по которой я так написал макросы доступа, заключается в том, что он делает рендеринг только части лабиринта очень простым. Если размер представления viewrows на viewcols с центром в row и col, вы можете визуализировать представление с помощью простого цикла:

    const int  top = row - viewrows / 2;
    const int  left = col - viewcols / 2;
    int        vr, vc;
    for (vr = 0; vr < viewrows; vr++) {
        for (vc = 0; vc < viewcols; vc++) {
            const unsigned char  v = maze_get(&m, top+vr, left+vc);
            /* Draw v at row vr, col vc */
        }
    }

и ячейки отображаются даже в том же порядке, в каком вы читали этот текст; сверху вниз, слева направо.

Обратите внимание, что вместо использования значений ячеек лабиринта для кодов символов, вы должны использовать таблицу поиска. Например,

int cell_char[256];

Вместо прямой печати значений ячеек вы напечатаете соответствующий cell_char, например

fputc(cell_char[maze_get(&m, row, col)], stdout);

Таким образом, вы можете группировать, например, различные символы стены в последовательном диапазоне, или даже использовать отдельные биты в 8-битном значении ячейки в качестве идентификаторов. Затем ячейки лабиринта описывают логическое содержимое в этой ячейке лабиринта, а не ее визуальное представление, с логически-визуальным отображением в отдельном массиве.

Если бы вы использовали Gtk +, вы могли бы иметь массив указателей GtkImage,

GtkImage *cell_image[256] = {0}; /* All NULL by default */

или используя SDL, вы можете использовать лабиринт в качестве текстур, которые вы можете рендерить,

SDL_Texture *cell_texture[256] = {0}; /* All NULL by default */

и в обоих случаях считывайте их либо с одного большого изображения (скажем, разделенного на прямоугольники с одинаковым размером 16 × 16), либо из отдельных файлов изображений.

Например, вы можете решить, что четыре младших значащих бита в значении ячейки указывают, является ли перемещение из этой ячейки вверх (предыдущая строка), вниз (следующая строка), влево (предыдущий столбец) или вправо (следующий столбец) возможно:

#define  CAN_GO_UP(value)    ((value) & (1 << 0))   /* == 1 */
#define  CAN_GO_DOWN(value)  ((value) & (1 << 1))   /* == 2 */
#define  CAN_GO_LEFT(value)  ((value) & (1 << 2))   /* == 4 */
#define  CAN_GO_RIGHT(value) ((value) & (1 << 3))   /* == 8 */

Обратите внимание, что это позволяет вам делать «ловушки»: проходы, которые работают только в одном направлении. Значения ячеек лабиринта, кратные 16 (0, 16, 32, 48, 64, 80, 96, ..., 208, 224 и 240), представляют собой полностью заблокированные ячейки: выхода нет. +1 позволяет пройти вверх; +2 позволяет проход вниз; +3 позволяет проход вверх и вниз; +4 разрешает проход слева; +5 позволяет проход влево и вверх; +6 позволяет проход влево и вниз; +7 позволяет проход вверх, влево и вниз; +8 позволяет проход вправо; +9 позволяет проход вверх и вправо; +10 позволяет проход вниз и вправо; +11 позволяет проход вверх, вниз и вправо; +12 позволяет проход влево и вправо; +13 позволяет проход вверх, влево и вправо; +14 позволяет проход вниз, влево и вправо; +15 позволяет проход вверх, вниз, влево и вправо.

Я бы лично порекомендовал использовать широкую версию библиотеки ncurses (ncursesw). (Я не использую Windows, поэтому я не совсем уверен, как вы устанавливаете и используете его в Windows, но домашняя страница ncurses имеет загрузки при использовании mingw .)

Тогда у вас будет гораздо более широкий выбор глифов, которые вы можете использовать. (При использовании локалей UTF-8 потенциально весь набор символов Unicode - блок Box Drawing особенно полезен для рисования лабиринта, и большинство из этих символов также доступны в старом CP437 * Кодовая страница 1080 *, что означает, что они должны хорошо работать как в Windows, так и в Windows-терминалах.)

В этом случае вы, вероятно, будете использовать

cchar_t  cell_char[256];

Как я упоминал выше, вы даже можете создать графическую версию (возможно, позже, расширив версию терминала) на C, используя SDL или GTK + . (Обратите внимание, что приведенное выше разделение между значением содержимого логической ячейки лабиринта и визуальным описанием ячейки также означает, что вы можете во время выполнения выбирать между «темами», имея более одного набора изображений ячеек. Это позволяет вам начать с грубой информационной версии, для отладки, а затем добавить визуальное совершенство.)

Подход, показанный в этих ответах, позволяет начать с простой игры на основе терминала, и, если вы решите, что хотите, добавьте поддержку графического интерфейса пользователя с лабиринтными ячейками на основе изображений без необходимости переписывать ядро. код лабиринта.

...