Чтение в массив для файла лабиринта в C с неравномерным размером куба и дополнительными символами - PullRequest
0 голосов
/ 23 сентября 2018

Я пытаюсь понять, как правильно читать и хранить лабиринт из документа .txt на C. Максимальный размер этого лабиринта будет 40x40 "кубов".После прочтения, мне нужно будет решить ее, поместив путь из левого угла в нижний правый с помощью *.Я привык использовать двумерные массивы, но эта проблема постоянно сбивает меня с толку, так как я не понимаю, как отслеживать строки и столбцы, если они не совсем ровные, или как я даже точно напечатал бы * в серединекаждого "куба" после того, как я его решил.Я читал другие примеры лабиринтов, которые состоят из 1 и 0 или даже четных # для стен, что позволяет легко читать и отслеживать, но не вводить, как это.В том же текстовом файле будут другие лабиринты, которые мне нужно будет прочитать после решения первого лабиринта, который будет разделен двойным пробелом.Ниже приведен пример одного из лабиринтов:

+---+---+---+---+---+  
|       |           |  
+---+   +   +   +   +  
|   |   |   |   |   |  
+   +   +   +   +---+  
|   |   |   |       |  
+   +   +   +---+   +  
|       |   |       |  
+   +---+---+   +   +  
|               |   |  
+---+---+---+---+---+ 

Вот мой код, который я делаю до сих пор для проверки ошибок и чтения символов.В нем я пытаюсь инициализировать массив размером 120x120, прочитать текущий символ и преобразовать эти символы в -1 или 0, чтобы соответствовать стене или пустому пространству.:

/* Read in a grid as parameter of max 120x120 to account for '+' and'-' */
int readLine(int grid2D[120][120])
{
    int row = 0;
    int col = 0;

    int isNewLine = TRUE;

    /* Loop through while getchar does not equal EOF */
    while ((c = getchar()) != EOF)
    {

        /* Check for foreign characters, return FALSE if found */
        if ((c != '+') || (c != '-') || (c != '|') || (c != ' '))
        {
            /* If c = \n , avoid tripping error, and do nothing */
            if(c == '\n'){}
            else
              errorFree = FALSE;
        }

        /* If character is a '+' '-' or '|', it is a wall, set to -1 to 
        use int's for wall tracking */
        else if (row%2 == 0)
        {
           if(c == '|')
           {
              grid2D[row][col] = -1;
              col++;
           }
        }
        else if((c == '+') || (c == '-'))
        {
           grid2D[row][col] = -1;
           col++;
        }
        else
        {
            if(c == '\n')
            {
               col = 0;
               row++;
            }
        }

    isNewLine = TRUE;

    return isNewLine;
}

Буду признателен за любые указания, я не уверен, что подход, который я делаю, является правильным.Я полагаю, что в настоящее время я правильно проверяю ошибки, однако я изо всех сил пытаюсь понять, как я должен отслеживать каждый «куб», так как символы для каждого «куба» не являются четными, они больше измеряются как кубы 5x1 (a +---+ для одной стороны и | для другой)

1 Ответ

0 голосов
/ 23 сентября 2018

В ответ на ваш вопрос и вопрос в комментарии определить размер строки и столбца довольно просто.Когда вы читаете строку массива из файла с fgets, вы можете использовать strlen() для определения количества символов (, но обратите внимание , он также содержит символ '\n' - так что вы будетенужно вычесть одну - что вы можете сделать в сочетании с обрезкой '\n' от конца)

После того, как вы прочитали первую строку и учли '\n', установите переменную, которая содержит числосимволы (столбцы) в вашем массиве.Поскольку вы знаете, что ваш массив является кубом, вы можете сравнить длину первой строки с длиной каждой прочитанной строки, чтобы проверить, что все строки имеют одинаковое количество столбцов.

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

Существует два способа обработки хранилища для вашего массива.Вы можете либо объявить массив, достаточно большой, чтобы вместить самый большой ожидаемый лабиринт (сохраняя его достаточно маленьким для размещения в стеке 256x512 безопасен как для Linux, так и для Windoze), или вы можете динамически распределять хранилище для ваших столбцов и строк, используя realloc() для выделения дополнительного хранилища по мере необходимости.(Там вы можете обрабатывать размеры лабиринта вплоть до предела памяти вашего компьютера - но это действительно добавляет сложности)

Ваша «путаница» с моим массивом должна быть, например, 11x21 понятна.Все это связано с тем, что символы на терминале примерно в два раза выше , чем в ширину .Таким образом, чтобы напечатать «куб» символов, вам нужно примерно вдвое больше столбцов, чем строк.Это не проблема вообще.Если вы правильно закодировали чтение столбцов и строк и у вас есть переменные, отслеживающие количество столбцов и строк - тогда разница становится не более чем числами, которые ваш код отслеживает в паре переменных.

Следующееэто короткий пример, чтобы обратиться к вашим камням преткновения при чтении неизвестного числа или строк и столбцов до фиксированного максимума.(вместо того, чтобы динамически распределять и перераспределять - что мы можем оставить на потом).Для этого мы #define константа для максимального числа столбцов, а затем зная, что нам нужно 1/2 этого числа строк, #define константа для этого количества строк, например

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

#define MAXC 512    /* declare consts for max NxN you can handle */
#define MAXR (MAXC + 1) / 2

Тогда достаточно просто объявить переменные для отслеживания текущих row и col и общего количества строк и столбцов (nrow, ncol) вместе с объявлением массива a[MAXR][MAXC] = {""}; для хранения лабиринта.Затем вы можете либо открыть свой файл, если в качестве 1-го аргумента указано имя файла (либо прочитать из stdin по умолчанию, если аргумент не указан).В любом случае вы можете проверить, есть ли у вас открытый поток для чтения, например,

    size_t row = 0, col = 0, nrow = 0, ncol = 0;
    char a[MAXR][MAXC+1] = {""}; /* delcare and initialize array */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

Если ваш файловый поток открыт для чтения, теперь это просто вопрос чтения всех строк данных в файле.Поскольку вы ожидаете, что все строки будут иметь четное количество символов, так что ваш лабиринт на самом деле является кубом, вы можете сохранить количество символов в первой строке (после обрезки '\n') и использовать его для сравнения с количеством символовв каждой строке для подтверждения у вас есть куб.Когда вы читаете строки, вам также нужно защищать границы вашего массива, чтобы вы не пытались хранить больше строк в вашем массиве, чем у вас есть строк, которые нужно хранить, поэтому простая проверка row < MAXR в сочетании с вашим fgets (a[row], MAXC, fp) налагает, чтопредел, например,

    while (row < MAXR && fgets (a[row], MAXC, fp)) {
        size_t len = strlen (a[row]);       /* get length of row */
        if (len && a[row][len-1] == '\n')   /* validate it fits in array */
            a[row][--len] = 0;  /* remove trailing '\n' char from end */
        else if (len == MAXC) {
            fprintf (stderr, "error: row exceeds %d chars.\n", MAXC);
            return 1;
        }
        if (!row)       /* if 1st row - set expected ncol for each row */
            ncol = len;
        if (ncol != len) {  /* validate all other rows against 1st */
            fprintf (stderr, "error: unequal columns (%lu) on row (%lu)\n",
                    len, row);
            return 1;
        }
        /* your code goes here - example just outputs array */
        for (col = 0; col < ncol; col++)
            putchar (a[row][col]);
        putchar ('\n');

        row++;  /* advance row counter when done processing row */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */
    nrow = row;                     /* save the total number of rows */

Теперь у вас есть все сохраненные строки и столбцы, и у вас есть установленные значения nrow и ncol, предоставляющие массив nrow x ncol.Я оставлю вам логику пути, но я хотел бы привести пример замены ' ' на '*' в вашем пути.Следующее делает это для каждого возможного символа пути, накладывая ограничение на то, что каждый '*' имеет соседний пробел (вы можете отрегулировать по мере необходимости).Здесь мы просто зациклим 0 -> nrow-1 и вложим цикл из 0 -> ncol-1, чтобы зациклить каждый символ в массиве.

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

Вы обрабатываете проверки краев как простые добавленияк условному инсайдеру вашего оператора if (...), например

    /* you can make multiple passes over the array to determine your path.
     * below is just an example of replacing the spaces in the path with
     * asterisks.
     */
    puts ("\nreplacing path spaces with asterisks\n");
    for (row = 0; row < nrow; row++) {
        for (col = 0; col < ncol; col++) {
            /* if adjacents and current ' ', replace with '*' */
            if (col && col < ncol - 1 &&    /* col > 0 && col < ncol-1 */
                    /* next checks adjacent and current all ' ' */
                    a[row][col-1] == ' ' && a[row][col] == ' ' && 
                    a[row][col+1] == ' ')
                a[row][col] = '*';  /* if conditions met, set element '*' */
            putchar (a[row][col]);
        }
        putchar ('\n');
    }

Собрав все части вместе в коротком примере, чтобы прочитать любой лабиринт шириной до 512 символов, вы можете сделать что-то вроде следующего:

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

#define MAXC 512    /* declare consts for max NxN you can handle */
#define MAXR (MAXC + 1) / 2

int main (int argc, char **argv) {

    size_t row = 0, col = 0, nrow = 0, ncol = 0;
    char a[MAXR][MAXC+1] = {""}; /* delcare and initialize array */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (row < MAXR && fgets (a[row], MAXC, fp)) {
        size_t len = strlen (a[row]);       /* get length of row */
        if (len && a[row][len-1] == '\n')   /* validate it fits in array */
            a[row][--len] = 0;  /* remove trailing '\n' char from end */
        else if (len == MAXC) {
            fprintf (stderr, "error: row exceeds %d chars.\n", MAXC);
            return 1;
        }
        if (!row)       /* if 1st row - set expected ncol for each row */
            ncol = len;
        if (ncol != len) {  /* validate all other rows against 1st */
            fprintf (stderr, "error: unequal columns (%lu) on row (%lu)\n",
                    len, row);
            return 1;
        }
        /* your code goes here - example just outputs array */
        for (col = 0; col < ncol; col++)
            putchar (a[row][col]);
        putchar ('\n');

        row++;  /* advance row counter when done processing row */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */
    nrow = row;                     /* save the total number of rows */

    /* you can make multiple passes over the array to determine your path.
     * below is just an example of replacing the spaces in the path with
     * asterisks.
     */
    puts ("\nreplacing path spaces with asterisks\n");
    for (row = 0; row < nrow; row++) {
        for (col = 0; col < ncol; col++) {
            /* if adjacents and current ' ', replace with '*' */
            if (col && col < ncol - 1 &&    /* col > 0 && col < ncol-1 */
                    /* next checks adjacent and current all ' ' */
                    a[row][col-1] == ' ' && a[row][col] == ' ' && 
                    a[row][col+1] == ' ')
                a[row][col] = '*';  /* if conditions met, set element '*' */
            putchar (a[row][col]);
        }
        putchar ('\n');
    }

    return 0;
}

Пример использования / вывода

Как указано, код просто считывает и выводит оригинальный лабиринт, а затем делает второй проход по лабиринту, выводя его вместе с путемзаполнено '*'

$ ./bin/array2dread <dat/arrmaze.txt
+---+---+---+---+---+
|       |           |
+---+   +   +   +   +
|   |   |   |   |   |
+   +   +   +   +---+
|   |   |   |       |
+   +   +   +---+   +
|       |   |       |
+   +---+---+   +   +
|               |   |
+---+---+---+---+---+

replacing path spaces with asterisks

+---+---+---+---+---+
| * * * | * * * * * |
+---+ * + * + * + * +
| * | * | * | * | * |
+ * + * + * + * +---+
| * | * | * | * * * |
+ * + * + * +---+ * +
| * * * | * | * * * |
+ * +---+---+ * + * +
| * * * * * * * | * |
+---+---+---+---+---+

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

...