Storing Player Name

How could we modify the high score example to also store the player's name along with the score?

To store the player's name along with the score, you'll need to define a structure that holds both the name and the score. You'll also need to decide on a format for storing this data in the file.

Let's see an example. First, we'll define a structure:

#include <SDL.h>
#include <iostream>
#include <algorithm>
#include <string>

struct ScoreEntry {
  int32_t Score;
  
  // Assuming a maximum name length
  // of 49 characters + null terminator
  char Name[50];
};

Next, let's modify UpdateHighScore():

void UpdateHighScore(
  const char* Path, const ScoreEntry& NewEntry
) {
  SDL_RWops* File{SDL_RWFromFile(Path, "w+b")};
  if (!File) {
    std::cerr << "Error opening file: "
      << SDL_GetError() << "\n";
    return;
  }

  const int NumScores{5};
  ScoreEntry HighScores[NumScores];

  size_t EntriesRead{SDL_RWread(
    File, HighScores,
    sizeof(ScoreEntry),
    NumScores
  )};

  if (EntriesRead < NumScores) {
    std::cout << "Initializing new high scores\n";
    for (size_t i{EntriesRead}; i<NumScores; ++i) {
      HighScores[i].Score = 0;

      // Initialize name to an empty string
      HighScores[i].Name[0] = '\0';
    }
  }

  bool IsHighScore{false};
  for (int i{0}; i < NumScores; ++i) {
    if (NewEntry.Score > HighScores[i].Score) {
      IsHighScore = true;
      break;
    }
  }

  if (IsHighScore) {
    std::cout << NewEntry.Name
      << " achieved a new high score of "
      << NewEntry.Score
      << "! Updating file\n";

    HighScores[NumScores - 1] = NewEntry;
    std::sort(HighScores, HighScores + NumScores,
      [](const ScoreEntry& a, const ScoreEntry& b) {
        return a.Score > b.Score;
      });

    SDL_RWseek(File, 0, RW_SEEK_SET);
    SDL_RWwrite(
      File, HighScores,
      sizeof(ScoreEntry), NumScores
    );
  } else {
    std::cout << "Not a high score - "
      "file not updated\n";
  }

  SDL_RWclose(File);
}

int main() {
  ScoreEntry ScoreOne{500, "Noob"};
  UpdateHighScore("highscores.dat", ScoreOne);
  ScoreEntry ScoreTwo{5000, "Alice"};
  UpdateHighScore("highscores.dat", ScoreTwo);
  ScoreEntry ScoreThree{3000, "Bob"};
  UpdateHighScore("highscores.dat", ScoreThree);
  ScoreEntry ScoreFour{6000, "Charlie"};
  UpdateHighScore("highscores.dat", ScoreFour);
  ScoreEntry ScoreFive{4000, "David"};
  UpdateHighScore("highscores.dat", ScoreFive);
  ScoreEntry ScoreSix{5500, "Eve"};
  UpdateHighScore("highscores.dat", ScoreSix);
  return 0;
}
Initializing new high scores
Noob achieved a new high score of 500! Updating file
Alice achieved a new high score of 5000! Updating file
Bob achieved a new high score of 3000! Updating file
Charlie achieved a new high score of 6000! Updating file
David achieved a new high score of 4000! Updating file
Eve achieved a new high score of 5500! Updating file

Explanation

  • We define a ScoreEntry struct to hold the player's name and score.
  • We use a fixed-size character array (char Name[50]) for the name, assuming a maximum length of 49 characters (plus the null terminator).
  • In UpdateHighScore(), we now read and write an array of ScoreEntry structs.
  • When initializing new entries, we set the Name field to an empty string.
  • When a new high score is achieved, we add the NewEntry to the HighScores array and sort the array based on the Score member using a lambda function for comparison.
  • We then write the entire sorted HighScores array back to the file.

Considerations

  • Fixed-Length Names: Using a fixed-size character array for the name simplifies the code but limits the length of names. You might consider using a more flexible approach, like storing the length of the name followed by the characters, but that would add complexity to the file format.
  • Error Handling: You might want to add error handling to deal with cases where the name is longer than the allowed length.
  • Alternative Data Formats: For more complex data structures, you might consider using a serialization library or a standard data format like JSON or XML, but that's beyond the scope of this simple example.

This example demonstrates a basic way to store both the player's name and score in the high score file. You can adapt this approach to handle more complex data structures or use different file formats based on your specific needs.

Read/Write Offsets and Seeking

Learn how to manipulate the read/write offset of an SDL_RWops object to control stream interactions.

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Seeking Past EOF
If we seek past the end of a file using SDL_RWseek(), what happens when we try to read or write data? Will it extend the file, or will it result in an error? How can we use this behaviour to append to a file?
Multiple High Scores
How can we modify the high score example to store multiple high scores (e.g., the top 10) instead of just one?
Closing SDL_RWops
Why is it important to close the SDL_RWops using SDL_RWclose() after we're finished with it? What are the potential consequences of not closing it?
Binary vs. Text Mode
What is the difference between binary mode ("wb" or "rb") and text mode ("w" or "r") when opening a file with SDL_RWFromFile()? Why is it important to use binary mode?
Searching Large Files
If we have a very large file, how can we efficiently search for a specific piece of data without reading the entire file into memory?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant