#include #include #include #include #include #include #include #include #include using namespace std; struct WordInfo { string word; string definition; mutable size_t useCount; WordInfo(string const & w = "", string const & d = "") : word(w), definition(d), useCount(0) {} bool operator< (WordInfo const & other) const { return word < other.word; } }; // ostream & operator<< (ostream & os, WordInfo const & wordInfo) // { // return os << wordInfo.word << '\n' // << wordInfo.definition << '\n' // << wordInfo.useCount << '\n'; // } string getLowerCaseWord(istream & input) { string word; input >> word; for (string::iterator i = word.begin(); i != word.end(); ++i) { *i = tolower(*i); } return word; } istream & operator>> (istream & input, WordInfo & wordInfo) { bool done = false; while (input && !done) { string const word = getLowerCaseWord(input); string definition; getline(input, definition); if (input && !definition.empty() && (word[0] != '#')) { string::size_type const beg = definition.find_first_not_of(" "); if (beg != string::npos) { wordInfo = WordInfo(word, definition.substr(beg)); done = true; } } } return input; } ostream & operator<< (ostream & output, WordInfo const & wordInfo) { return output << wordInfo.word << "\t\t" << wordInfo.definition; } typedef vector WordInfos; typedef vector TryHistogram; size_t const maxTries = 3; typedef map InitialUseCounts; string makeHistogramName(string const & wordFileName, string const & name) { return wordFileName + "." + name + ".histogram"; } InitialUseCounts readInitialUseCounts(string const & wordFileName, string const & name) { InitialUseCounts initialUseCounts; string const fileName = makeHistogramName(wordFileName, name); ifstream file(fileName.c_str()); while (file) { string word; size_t count; file >> word >> count; initialUseCounts[word] = count; } return initialUseCounts; } void setInitialUseCounts(WordInfos & wordInfos, string const & wordFileName, string const & name, size_t & oldCount) { InitialUseCounts const initialUseCounts = readInitialUseCounts(wordFileName, name); for (size_t i = 0; i != wordInfos.size(); ++i) { InitialUseCounts::const_iterator it = initialUseCounts.find(wordInfos[i].word); if (it != initialUseCounts.end()) { wordInfos[i].useCount = it->second; oldCount += it->second; } } } WordInfos readWords(string const & wordFileName, string const & name, size_t & oldCount) { WordInfos wordInfos; ifstream input(wordFileName.c_str()); if (!input) { cerr << "Cannot open " << wordFileName << '\n'; return wordInfos; } istream_iterator beg(input); istream_iterator end; copy(beg, end, back_inserter(wordInfos)); setInitialUseCounts(wordInfos, wordFileName, name, oldCount); return wordInfos; } size_t howManyTimes(string const & name) { cout << "How many questions do you want " << name << "? "; size_t howMany; cin >> howMany; return (cin) ? howMany : 0; } WordInfo const & selectWord(WordInfos const & wordInfos, size_t currentWordCount) { size_t const round = currentWordCount / wordInfos.size(); size_t const wordsInThisRound = wordInfos.size() - (currentWordCount % wordInfos.size()); int whichWord = rand() % wordsInThisRound; // cout << "wordsInThisRound: " << wordsInThisRound << " "; // cout << "round: " << round << " " ; // cout << "whichWord: " << whichWord << "\n"; WordInfos::const_iterator it = wordInfos.begin(); for ( ; it != wordInfos.end(); ++it) { if (it->useCount == round) { if (--whichWord < 0) { break; } } } assert(it != wordInfos.end()); ++it->useCount; return *it; } void askQuestions(WordInfos const & wordInfos, size_t oldCount, size_t count, TryHistogram & tryHistogram) { cout << '\n'; for (size_t i = 0; i != count; ++i) { WordInfo const & wordInfo = selectWord(wordInfos, oldCount + i); cout << '(' << (i + 1) << ") " << wordInfo.definition << ": "; bool correct = false; size_t tries = 0; while (cin && !correct) { if (getLowerCaseWord(cin) == wordInfo.word) { cout << "Good job!\n"; correct = true; ++tryHistogram.at(tries); } else { ++tries; if (tries == maxTries) { cout << "The answer was '" << wordInfo.word << "'\n"; correct = true; } else { cout << "Please try again: "; } } } } } void showResults(size_t count, TryHistogram const & tryHistogram) { cout << '\n' << "Results:\n" << "--------\n"; for (size_t i = 0; i != maxTries; ++i) { if (tryHistogram[i]) { size_t const percent = static_cast(((static_cast(tryHistogram[i]) * 100 / count) + .5)); bool const excellent = (i == 0) && (percent == 100); if (excellent) { cout << "EXCELLENT! All correct! :)"; } else { if (i == 0) { cout << "Correct after the first try"; } else { cout << "Correct after " << (i + 1) << " tries"; } cout << ": " << tryHistogram[i] << " (" << percent << "%)"; } cout << '\n'; } } } string getUserName() { cout << "What is your name? "; string name = getLowerCaseWord(cin); if (name.length()) { name[0] = toupper(name[0]); } return name; } void saveUseCounts(WordInfos const & wordInfos, string const & wordFileName, string const & name) { InitialUseCounts useCounts = readInitialUseCounts(wordFileName, name); for (size_t i = 0; i != wordInfos.size(); ++i) { useCounts[wordInfos[i].word] = wordInfos[i].useCount; } string const fileName = makeHistogramName(wordFileName, name); ofstream file(fileName.c_str()); for (InitialUseCounts::const_iterator it = useCounts.begin(); it != useCounts.end(); ++it) { if ( ! it->first.empty()) { file << it->first << ' ' << it->second << '\n'; } } } int main(int argc, char * argv[]) { if (argc != 2) { cout << "Usage: " << argv[0] << " \n"; return EXIT_FAILURE; } string const name = getUserName(); size_t oldCount = 0; string const wordFileName = argv[1]; WordInfos const wordInfos = readWords(wordFileName, name, oldCount); if (wordInfos.empty()) { cout << "No words to ask\n"; return 1; } cout << "There are " << wordInfos.size() << " words total\n"; srand(time(0)); // copy(wordInfos.begin(), // wordInfos.end(), // ostream_iterator(cout, "\n")); size_t const count = howManyTimes(name); if (count) { TryHistogram tryHistogram(maxTries); askQuestions(wordInfos, oldCount, count, tryHistogram); showResults(count, tryHistogram); } saveUseCounts(wordInfos, wordFileName, name); }