0

Basically, i want to read highscores from a file and check if the user has scored enought points to be on the scoreboard. I'm trying to do it like this:

string initials[10];
int scores[10];

//load txt
ifstream highscores("highscores.txt");
if(highscores.is_open())
{
   while(highscores.good())
   {
    for(int x=0;x<10;x++)
    {
        getline(highscores,initials[x],' '); 
        highscores >> scores[x];
    }
   }

    }

the initials are only 3 chars long, so i could implement a 2 dim. array but i wanted to try it with strings. It shows that i mad one string of size 10. How should i write it so it will work with 10 arrays instead of 1? (I know i could make 10 arrays naming them from array1.. to 10, looping through them sound much better. The highscores file is just a set of 10 initialas AAA,BBB etc. and some scores.

Example of highscores.txt:

AAA 5000
BBB 4000
CCC 3000
3
  • Is the format of each line in the file "AAA 104" for example? Could you post some example content. Commented Dec 10, 2011 at 12:48
  • why not use vector< map<string,int> > ? Commented Dec 10, 2011 at 12:55
  • how would i implement that? and would using a vector be worth it? i will always have 10 highscores only Commented Dec 10, 2011 at 13:00

3 Answers 3

1

Use std::map to hold initials and associated score. For example:

int main()
{
    // Map is keyed by initials.
    std::map<std::string, int> scores;

    std::ifstream in("highscores.txt");
    if (in.is_open())
    {
        for (;;)
        {
            std::string line;
            std::getline(in, line);

            if (!in.good())
            {
                break;
            }

            const size_t space_idx = line.find(' ');
            if (std::string::npos != space_idx)
            {
                // The initials are everthing before the space.
                // Score everything after the space.
                scores[line.substr(0, space_idx)] =
                    atoi(line.substr(space_idx + 1).c_str());
            }
        }
        in.close();
    }

    // Check who has achieved required score.
    for (std::map<std::string, int>::iterator i = scores.begin();
         i != scores.end();
         i++)
    {
        if (i->second > 3500)
        {
            std::cout << i->first << "\n";
        }
    }
    return 0;
}
Sign up to request clarification or add additional context in comments.

2 Comments

If anything, it should be a multimap, because neither the score nor the initials are unique. It should also be keyed by the score, because that is what you are usually sorting by.
@BjörnPollex, if the initials are not unique then yes, you are correct. Could modify the answer to cope by retaining the highest score only for a set of initials.
1

There are two tasks:

  • add a new user score if it is high enough

    // Add a new score (score => name pair); pop the lowerest score and return it
    template <class Map> typename Map::value_type
    add_score_if(Map& scores, typename Map::value_type new_score) {
      scores.insert(new_score);
      // pop the lowerest score
      auto it = scores.begin();
      typename Map::value_type lowerest(*it);
      scores.erase(it);
      return lowerest;
    }
    

    add_score_if() pops the lowerest score thus if new_score is not high enough it won't stay in the score table i.e., the content of the scores will be the same before/after add_score_if() in this case.

  • load scores from the file

    // Load scores (score => name pairs) from input stream
    // where each line is: name score
    // Return whether all scores have been loaded
    template <class Istream, class Map> bool
    load_scores(Istream& in, Map& scores) {
      std::string name; int score;
      while (in >> name >> score) scores.insert(std::make_pair(score, name));
      return in.eof(); //XXX ignore errors at eof
    }
    

Program

#include <iostream>
#include <map>

template<class Map> void
dump_scores(std::ostream& out, const Map& scores) {
  for (auto it = scores.rbegin(); it != scores.rend(); ++it)
    out << it->second << ' ' << it->first << '\n';
}

int main() {
  // load scores
  std::multimap<int, std::string> scores;
  if (! load_scores(std::cin, scores)) {
    std::cerr << "error: not all scores have been loaded\n";
    return 1;
  }
  std::cout << "Before:\n";
  dump_scores(std::cout, scores);

  // add new score
  add_score_if(scores, std::make_pair(4000, "AAA"));
  std::cout << "\nAfter:\n";
  dump_scores(std::cout, scores);
}

Example

$ g++ -std=c++0x *.cc && printf "AAA 5000\nCCC 3000\nBBB 4000" | ./a.out
Before:
AAA 5000
BBB 4000
CCC 3000

After:
AAA 5000
AAA 4000
BBB 4000

Comments

0

Got your question after re-reading it :)

1. string str[10] as you mentioned does not create an array of 10 strings as strings are created on heap not on stack.

If you want to create an array of strings to put your data to..then it should be char * name[10]; and every time you read a line and get the first 3 characters you do new char[3] (you gotta store it in char * right ??).

Else, to be more efficient and depending upon your data, you can create a char arr[30] and do the alignment of 3 bytes for reading all by yourself.

Now, you can make your life much easier by using the STL containers..

vector < map < string,int > > arr; arr.reserve(10);

The advantages are many: 1) No memory management to be done by you.

2) Use of iterators to loop through.

3) And there wont be much difference in performance also if you compare it with my first method.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.