C - Как я могу напечатать случайное слово на игровом поле (массив 2d)? - PullRequest
1 голос
/ 17 апреля 2020

Я пишу игру с использованием языка C и терминала Cygwin.

Я прочитал из файла .txt 1000 слов, а затем напечатал случайное слово. Мне нужно напечатать это случайное слово внутри 2-мерного массива "gameboard"

Ссылка на: Изображение текущего вывода. Необходимо переместить слово из коробки вглубь коробки.

Как напечатать случайное слово внутри коробки?

Слово нужно показать вверх в верхнем ряду окна в произвольном горизонтальном положении.

Примечание: когда я говорю «ящик», я имею в виду ящик 20 (высота) на 80 (ширина), состоящий из тире и звездочек.

Любая помощь будет принята с благодарностью. Заранее большое спасибо.

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

void main(){

    int g, h; //index for use in for loop

        //creates box
    const int boxLength = 20;
    const int boxWidth = 75;
    char box[boxLength][boxWidth];
    for(int g = 0; g < boxLength; g++){
        for(int h = 0; h < boxWidth; h++){
            if(g == 0 || g == boxLength - 1)
                box[g][h] = '-';
            else if(h == 0 || h == boxWidth - 1)
                box[g][h] = '|';
            else
                box[g][h] = ' ';
            }
    }       

            FILE *myFilePointer2 = fopen("wordList.txt", "r");

            srand(time(0));
            int size = 1000;

            if(myFilePointer2 == NULL){
                printf("Unable to open file wordList.txt");
                exit(0);
            }

            char** words = (char**)malloc(sizeof(char**)*size); //2d pointer array, dynamically allocated, to store words from text file

            char wordBankArray[1050];//wordBankArray

            int wordQuantity = 0;

            while(fgets(wordBankArray, 1050, myFilePointer2) != NULL){// read data from myFilePointer line by line and store it into words array
                words[wordQuantity] = (char*)malloc(sizeof(char)*(strlen(wordBankArray)+1)); //dynamically allocates memory for words array
                strcpy(words[wordQuantity], wordBankArray); //copying words from text file to wordBankArray
                wordQuantity++;
            }

            printf("Randomly generated word from .txt file: ");
            int index = rand()%wordQuantity;   // psuedo randomly generates an index in range of 0 to wordQuantity)

            printf("%s\n", words[index]); //prints randomly generated word from index

            for(int g = 0; g < boxLength; g++){ //prints 2d box
                for(int h = 0; h < boxWidth; h++){
                    printf("%c", box[g][h]);
                }
                printf("\n");
                fclose(myFilePointer2); //close file for reading
            }
    }

Ответы [ 2 ]

0 голосов
/ 17 апреля 2020

Вы можете попробовать что-то вроде этого:

Получить массив слов и выбрать случайное слово, как вы уже делаете в своем коде. Здесь я предполагаю, что случайное слово имеет значение «randomWord».
Затем найдите случайное положение слова с помощью функции rand (), как показано в приведенном ниже коде.

char randomWord[20]="Program";
int wordLen = strlen(randomWord);
srand(time(0));
//Generates the random position of the word
//Subtract '2' because of the borders of the box and the word length so it can fit in the box
int wordPos = rand() % (boxWidth-wordLen-2);

for(int g = 0; g < boxLength; g++){
    for(int h = 0; h < boxWidth; h++){
        if(g == 1 && h == wordPos){ //Inserts the word at the top and at the random position
            for(int i = 0; i < wordLen; i++){
                box[g][h] = randomWord[i];
                h++;
            }
            box[g][h] = ' ';
        }
        if(g == 0 || g == boxLength - 1)
            box[g][h] = '-';
        else if(h == 0 || h == boxWidth - 1)
            box[g][h] = '|';
        else
            box[g][h] = ' ';
    }
}

Затем просто напечатайте коробку, как вы уже делаете.
Вы должны увидеть что-то вроде этого:
Результат коробки со случайным словом внутри

Надеюсь, это поможет вам!

0 голосов
/ 17 апреля 2020

У вас есть несколько мелких ошибок, и если вы читаете файл с 1000 словами, но выделяете только 100 указателей - вы вызываете неопределенное поведение, пытаясь записать указатели, которые не существуют.

Вы не можете Вам не нужно fclose() внутри вашего for(int g = 0; g < boxLength; g++){ l oop (который закрывает файл boxlength количество раз).

Если вы определяете const int ..., то вы создаете 2D VLA (массив переменной длины), что хорошо, но если границы известны до времени компиляции, вместо этого объявите константы с #define и избегайте VLA, который не существует в C89 / 90, был представлен в C99 и стал дополнительной функцией в C11.

Вы также должны добавить -Wshadow к вашей строке компиляции, вы скрываете переменные:

int g, h; //index for use in for loop

Когда вы объявляете переменные l oop:

for(int g = 0; g < boxLength; g++){
    for(int h = 0; h < boxWidth; h++){
    ...

(будьте осторожны с объявлениями переменных - здесь вы позже не будете использовать g или h, но в других теневые переменные могут иметь тяжелые последствия)

Имея это в виду, вы можете определить необходимые константы как:

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

#define BXLEN   20      /* if you need a constant, #define one (or more) */
#define BXWDT   75
#define WRDSZ  100
#define ARRSZ 1050

(вы можете выбрать любые имена, которые вам нравятся)

Если вы не работаете в автономной среде (без операционной системы), ваше объявление void main() неверно. В реализации, соответствующей стандартам, допустимыми декларациями для main для являются int main (void) и int main (int argc, char *argv[]) (которые вы увидите написанными с эквивалентом char **argv). См .: C11 Standard - §5.1.2.2.1 Запуск программы (p1) .

Аргументы int argc, and char **argv позволяют передавать информацию в вашу программу в командной строке. Не кодируйте имена файлов жестко. (если вы находитесь во встроенной системе - это исключение, поскольку у вас может не быть возможности передать имя файла в командной строке) В противном случае вы можете сделать:

int main (int argc, char **argv)
{
    int nptrs = WRDSZ, index, wordQuantity = 0;     /* declare/initialize vars */
    char box[BXLEN][BXWDT] =  {{0}},
        **words = NULL,
        wordBankArray[ARRSZ] = "";
    FILE *fp = NULL;

    if (argc < 2 ) {    /* validate 1 argument given for filename */
        fprintf (stderr, "error: insufficient input,\n"
                         "usage: %s filename\n", argv[0]);
        return 1;
    }

    /* open file/validate file open for reading */
    if ((fp = fopen (argv[1], "r")) == NULL) {
        perror ("fopen-argv[1]");
        return 1;
    }

( note: Я сократил myFilePointer2 до fp)

Когда вы динамически выделяете память с malloc, calloc или realloc в C, нет необходимости приводить к возврату malloc, это не нужно. Смотрите: Я разыгрываю результат Малло c? . Если вы используете разыменованный указатель для установки размера - вы никогда не ошибетесь. Вы можете назначить указатели для words с помощью:

    if (!(words = malloc (nptrs * sizeof *words))) {    /* allocate/validate */
        perror ("malloc-words");
        return 1;
    }

( примечание: вы должны проверить каждое выделение)

Примечание выше, вы только выделяете начальный WRDSZ (100) указатели. Если ваш файл содержит 1000 слов, вы должны отслеживать количество заполненных указателей (ваш wordQuantity) и отслеживать количество назначенных указателей (скажем, с nptrs). Когда wordQuantity == nptrs, вы должны realloc количество указателей, доступных через words, прежде чем пытаться использовать другой указатель (обычно удвоение текущего выделенного числа является разумной схемой роста). При добавлении дополнительного теста и перераспределения ваше чтение l oop станет:

    while (fgets (wordBankArray, ARRSZ, fp) != NULL) {  /* read each line in file */
        size_t len;     /* save length, then memcpy */
        if (wordQuantity == nptrs) {    /* check if all pointers used - realloc */
            /* always realloc using a temporary pointer -- not the pointer itself */
            void *tmp = realloc (words, 2 * nptrs * sizeof *words);
            if (!tmp) {     /* validate realloc succeeds */
                perror ("realloc-words");
                break;      /* don't exit, original words pointer still valid */
            }
            words = tmp;    /* assign reallocated block to original pointer */
            nptrs *= 2;     /* update number of pointers allocated */
        }
        if (!(words[wordQuantity] = malloc ((len = strlen (wordBankArray)) + 1))) {
            perror ("malloc-words[wordQuantity]");
            return 1;
        }
        memcpy (words[wordQuantity], wordBankArray, len + 1);
        wordQuantity++;
    }
    fclose (fp);

( note: , вам нужно всего лишь один раз вызвать strlen(), сохранить размер и затем скопировать строка с memcpy(). Если вы звоните strcpy(), вы просто сканируете конец строки, который у вас уже есть, от вашего звонка до strlen())

После того, как вы закончили чтение, затем позвоните fclose() в этот момент. Также обратите внимание, что sizeof (char) равно 1 и должно быть опущено при умножении вашего размера. Остальная часть того, что у вас есть, напечатает коробку и выведет случайную строку, но обратите внимание, что в выходных данных нет преобразования, нет необходимости вызывать printf. puts или fputs подойдет (хороший компилятор сделает это за вас за кулисами)

То, что вы упускаете, освобождает выделенную вами память. Для этого вы можете сделать:

    for (int i = 0; i < wordQuantity; i++)      /* free allocated strings */
        free (words[i]);
    free (words);                               /* free pointers */

Если вы поставите это в целом, вы можете сделать:

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

#define BXLEN   20      /* if you need a constant, #define one (or more) */
#define BXWDT   75
#define WRDSZ  100
#define ARRSZ 1050

int main (int argc, char **argv)
{
    int nptrs = WRDSZ, index, wordQuantity = 0;     /* declare/initialize vars */
    char box[BXLEN][BXWDT] =  {{0}},
        **words = NULL,
        wordBankArray[ARRSZ] = "";
    FILE *fp = NULL;

    if (argc < 2 ) {    /* validate 1 argument given for filename */
        fprintf (stderr, "error: insufficient input,\n"
                         "usage: %s filename\n", argv[0]);
        return 1;
    }

    for (int g = 0; g < BXLEN; g++) {                   /* initialize box */
        for (int h = 0; h < BXWDT; h++) {
            if (g == 0 || g == BXLEN - 1)
                box[g][h] = '-';
            else if (h == 0 || h == BXWDT - 1)
                box[g][h] = '|';
            else
                box[g][h] = ' ';
        }
    }

    /* open file/validate file open for reading */
    if ((fp = fopen (argv[1], "r")) == NULL) {
        perror ("fopen-argv[1]");
        return 1;
    }

    srand (time (NULL));                                /* seed rand generator */

    if (!(words = malloc (nptrs * sizeof *words))) {    /* allocate/validate */
        perror ("malloc-words");
        return 1;
    }

    while (fgets (wordBankArray, ARRSZ, fp) != NULL) {  /* read each line in file */
        size_t len;     /* save length, then memcpy */
        if (wordQuantity == nptrs) {    /* check if all pointers used - realloc */
            /* always realloc using a temporary pointer -- not the pointer itself */
            void *tmp = realloc (words, 2 * nptrs * sizeof *words);
            if (!tmp) {     /* validate realloc succeeds */
                perror ("realloc-words");
                break;      /* don't exit, original words pointer still valid */
            }
            words = tmp;    /* assign reallocated block to original pointer */
            nptrs *= 2;     /* update number of pointers allocated */
        }
        if (!(words[wordQuantity] = malloc ((len = strlen (wordBankArray)) + 1))) {
            perror ("malloc-words[wordQuantity]");
            return 1;
        }
        memcpy (words[wordQuantity], wordBankArray, len + 1);
        wordQuantity++;
    }
    fclose (fp);

    fputs ("Randomly generated string from list : ", stdout);
    index = rand() % wordQuantity;
    printf ("%s\n", words[index]);

    for (int g = 0; g < BXLEN; g++) {
        for (int h = 0; h < BXWDT; h++) {
            printf ("%c", box[g][h]);
        }
        putchar ('\n');
    }

    for (int i = 0; i < wordQuantity; i++)      /* free allocated strings */
        free (words[i]);
    free (words);                               /* free pointers */
}

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

Для файла данных dat/1kfnames.txt я просто перенаправил 1000 имен файлов в файл, чтобы они служили словами:

$ ./bin/wordinbox dat/1kfnames.txt
Randomly generated string from list : str_printf_null.c

---------------------------------------------------------------------------
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
---------------------------------------------------------------------------

Проверка использования памяти / ошибок

В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанностей относительно любого выделенного блока памяти: (1) всегда сохраняет указатель на начальный адрес для блока памяти, таким образом, (2) он может быть освобожден , когда он больше не нужен.

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

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

$ valgrind ./bin/wordinbox dat/1kfnames.txt
==28127== Memcheck, a memory error detector
==28127== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==28127== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==28127== Command: ./bin/wordinbox dat/1kfnames.txt
==28127==
Randomly generated string from list : tor.c

---------------------------------------------------------------------------
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
---------------------------------------------------------------------------
==28127==
==28127== HEAP SUMMARY:
==28127==     in use at exit: 0 bytes in 0 blocks
==28127==   total heap usage: 1,008 allocs, 1,008 frees, 45,566 bytes allocated
==28127==
==28127== All heap blocks were freed -- no leaks are possible
==28127==
==28127== For counts of detected and suppressed errors, rerun with: -v
==28127== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

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

...