Enno Mühlbauer
Enno Mühlbauer

Reputation: 23

C: writing content of array to txt file

im trying to implement the number guessing game. The game itself works fine. But now I want to add a function which safes the score of the last game (Number of trys and guessed number) in the leaderboard.txt file. I haven't finished the saveScore method yet, I don't need help with implementing the rest. I wan't to read the data from the file, add the new line and sort it from least to most trys. But I only wan't to save the top 10 of all time. I need some help to get the following code running. One problem is the code doesn´t even terminate.


I think all the problems are within the saveScore method. (line 18 -25)

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

#define max_length 300


void saveScore(int guess, int randNumber) {

    FILE *datei;
    datei = fopen("leaderboard.txt", "wb");

    
    char lines[max_length];
    char leaderboard[10][max_length];
    int line = 0;

    while (fgets(leaderboard[line], sizeof(lines), datei) != NULL){
            line++;  
    }

    leaderboard[line][max_length] = ("%d, %d", guess, randNumber);

    fwrite(leaderboard, sizeof(char), sizeof(leaderboard), datei);
    fclose(datei);
}

void startGame(){

    int guess = 1;
    int randNumber;
    int uInput;

    randNumber = (rand()%100)+1;
    printf("%d", randNumber);

        do {
        
        printf("Guess the number between 1-100: \n");
        scanf("%d", &uInput);

        if (uInput < randNumber){
            printf("the number you are looking for is higher.\n");
        }
        else if (uInput > randNumber){
            printf("the number you are looking for is lower.\n");
        }
        else {
            printf("Jackpot it was your %d. try.", guess);
        }
        guess++;
    } while (randNumber != uInput);

    saveScore(guess, randNumber);

    char playAgain = 'j';
    printf("Try Again (j/n): \n");
    scanf(" %c", &playAgain);
    
    if (playAgain == 'j') {
        startGame();
    }
    printf("Thank you for playing.");

}

int main() {

    srand(time(NULL));
      
    startGame();

    return 0;
}

I appreciate any help. Best Enno

Upvotes: 0

Views: 69

Answers (1)

Oka
Oka

Reputation: 26345

This loop has the problem of being able to overflow leaderboard if line ever reaches 10.

while (fgets(leaderboard[line], sizeof(lines), datei) != NULL){
    line++;  
}

This line

leaderboard[line][max_length] = ("%d, %d", guess, randNumber);

has a few problems. [max_length] would be one past the end of the buffer, but with that said, it is not needed and the assignment as a whole is incorrect. To perform string interpolation, use a function such as sprintf.

Aside from the fact that you only open the file for writing, the primary problem is that

fwrite(leaderboard, sizeof(char), sizeof(leaderboard), datei);

will write the entire contents of leaderboard to the file. This includes the garbage values that exist towards the end of each array, after each string. fgets will then read those garbage values later.

You should stick to reading and writing binary or text, but do not mix them. If you use fgets, use fputs (or similar) to write the text. Conversely, if you use fwrite, use fread to read the binary data.


Here is a basic, cursory snippet using text functions, where we:

  • open the file for reading
  • read our lines into the array
  • close the file
  • add our new score to the array
  • sort our array using qsort
  • open the file for writing
  • write our lines
  • close the file

The trick here is leaderboard has an additional slot, so that there is always room for our newest score. After sorting, we only write at most MAX_ENTRIES entries to the file, meaning if the array is full we ignore the worst score.

#define MAX_ENTRIES 10
#define MAX_LENGTH 300
#define SAVE_FILE "leaderboard.txt"

int compare(const void *ap, const void *bp) {
    const char (*a)[MAX_LENGTH] = ap;
    const char (*b)[MAX_LENGTH] = bp;

    int av, bv;
    sscanf(*a, "%d", &av);
    sscanf(*b, "%d", &bv);

    return (av > bv) - (av < bv);
}

void saveScore(int guess, int randNumber) {
    char leaderboard[MAX_ENTRIES + 1][MAX_LENGTH] = { 0 };
    size_t entries = 0;

    FILE *file = fopen(SAVE_FILE, "r");

    if (file) {
        while (entries < MAX_ENTRIES &&
                fgets(leaderboard[entries], sizeof *leaderboard, file))
            entries++;

        fclose(file);
    }

    sprintf(leaderboard[entries], "%d %d\n", guess, randNumber);
    entries++;
    qsort(leaderboard, entries, sizeof *leaderboard, compare);

    file = fopen(SAVE_FILE, "w");

    if (file) {
        for (size_t i = 0; i < entries && i < MAX_ENTRIES; i++)
            fputs(leaderboard[i], file);

        fclose(file);
    }
}

Upvotes: 1

Related Questions