Start learning free

LESSON 248 OF 568 · FUNCTIONS & CLASSES

Score Keeper in C++: Tracking a Total and a Best Value

Practice O(1) Read the full explanation

A game shows each player two numbers: their total, all the points of their turns added up, and their best single turn. Write a class ScoreKeeper that keeps both numbers up to date as the points come in, and can start over.

Implement the class ScoreKeeper:

  • ScoreKeeper() creates a keeper with no points yet.
  • add(points) records the points of one turn.
  • total() returns the sum of all the points added since the keeper was created or last reset.
  • best() returns the highest points of a single add in that time, or 0 if there was no add yet.
  • reset() starts over: afterwards total() and best() both return 0 until the next add.

The input is a list of operations, applied in order to one new ScoreKeeper. The output lists what the total and best calls return, in order. The starter code spells these names the way your language does (in Go and Rust, ScoreKeeper is a struct with methods). Give the fields names that differ from the methods' names: in several languages a field named total would clash with the method total().

 

Example 1:

Input:
    add(10)
    add(25)
    add(5)
    total()
    best()
Output: 40 25
Explanation: 10 + 25 + 5 = 40. The best single turn was the 25.

Example 2:

Input:
    best()
    total()
    add(0)
    best()
    total()
Output: 0 0 0 0
Explanation: Before any add, both numbers are 0. A turn worth 0 points changes neither.

Example 3:

Input:
    add(30)
    add(50)
    total()
    best()
    reset()
    total()
    best()
    add(20)
    best()
    total()
Output: 80 50 0 0 20 20
Explanation: After reset() the keeper starts over. The 50 from before no longer counts,
so after add(20) the best is 20, not 50.

 

Constraints:

  • 0 <= points <= 1000
  • At most 300 operations on one ScoreKeeper.

Note:

Every method should run in O(1) time: add updates the two numbers as each turn comes in, so total and best never need to look back at old turns.

STEP 1 OF 7
    Ctrl + Enter to check

    C++ · FUNCTIONS & CLASSES

    Score Keeper in C++, explained

    Problem overview

    A game shows each player two numbers: the total of all their turns, and their best single turn. The class ScoreKeeper keeps both up to date as points come in. It has four methods:

    • add(points) records one turn.
    • total() returns the sum of all points added since the keeper was created or last reset.
    • best() returns the highest points of a single add in that time, or 0 if there was none.
    • reset() starts over, so that total() and best() both return 0 until the next add.

    After add(10), add(25) and add(5), the total is 40 and the best is 25. Points are between 0 and 1000, and a test has at most 300 operations.

    Approach

    One way is to store every turn in a list and compute the sum and the maximum whenever they are asked for. That works, but it keeps data the class never needs and does more work with every question. A simpler idea is to keep only two numbers and update them as each turn arrives. The total grows by points every time. The best changes only when a bigger score shows up, which is the same idea as finding the largest value of a list one value at a time.

    With two fields, every method takes constant time: add does one addition and one comparison, and the other three methods just read or set the fields. The total and best never need to look back at old turns.

    Step by step

    1. Declare totalPoints, an int that starts at 0. A field can start with a value, written like a variable's, so every new keeper gets its own zero and the class needs no constructor.
    2. Declare bestPoints the same way. Before any add, best() must return 0, and every score is at least 0, so 0 is a safe start.
    3. In add, add the points to the total with +=.
    4. Update the best. If points is greater than bestPoints, store it. With >, an equal score leaves the best as it is, which is the same number anyway.
    5. total() and best() return the two fields. They only report: add keeps the fields current.
    6. reset() sets both fields back to 0, the values a new keeper starts with. After a reset the best counts only the adds that come later.

    The solution in C++

    class ScoreKeeper {
    public:
        int totalPoints = 0;
        int bestPoints = 0;
    
        void add(int points) {
            totalPoints += points;
            if (points > bestPoints) {
                bestPoints = points;
            }
        }
    
        int total() {
            return totalPoints;
        }
    
        int best() {
            return bestPoints;
        }
    
        void reset() {
            totalPoints = 0;
            bestPoints = 0;
        }
    };

    The field names differ from the method names on purpose. A class can't have a data member and a member function with the same name in C++, so a field called total next to a method total() would not compile.

    A run

    This is the third example: two turns, a reset, then a new turn.

    #include <iostream>
    using namespace std;
    
    // the class above goes here
    int main() {
        ScoreKeeper s;
        s.add(30);
        s.add(50);
        cout << s.total() << " " << s.best() << endl;
        s.reset();
        cout << s.total() << " " << s.best() << endl;
        s.add(20);
        cout << s.best() << " " << s.total() << endl;
    }

    It prints:

    80 50
    0 0
    20 20

    The 50 from before the reset no longer counts, so after add(20) the best is 20, not 50. A keeper that has never received an add reports zeros:

    #include <iostream>
    using namespace std;
    
    // the class above goes here
    int main() {
        ScoreKeeper s;
        cout << s.best() << " " << s.total() << endl;
    }

    It prints:

    0 0

    Two keepers at once

    Every object has its own fields, so two keepers never share a total. In this run, the first keeper gets two turns and the second gets one:

    #include <iostream>
    using namespace std;
    
    // the class above goes here
    int main() {
        ScoreKeeper first;
        ScoreKeeper second;
        first.add(7);
        first.add(3);
        second.add(1);
        cout << first.total() << " " << first.best() << endl;
        cout << second.total() << " " << second.best() << endl;
        first.reset();
        cout << first.total() << " " << second.total() << endl;
    }

    It prints:

    10 7
    1 1
    0 1

    After first.reset(), the first keeper's total is 0 while the second keeper's total is still 1. The second keeper is untouched by the first one's adds and by its reset. That is what makes a class useful: the same code, many independent sets of values.

    Details worth knowing

    • Default member values. Writing int totalPoints = 0; inside the class gives every object that starting value. Without it, an int field of an object created as ScoreKeeper s; holds an indeterminate value, and reading it is undefined behaviour.
    • Each object has its own fields. Two keepers never share totals: adding to one leaves the other alone.
    • A turn worth 0. add(0) is allowed. It changes neither the total nor the best, because 0 is not greater than the best, which is at least 0.

    Common mistakes

    • Forgetting to reset bestPoints in reset(), so the old best survives. Resetting only the total is the usual slip.
    • Updating the best with bestPoints = points for every turn, which turns it into the last turn instead of the highest.
    • Comparing points with totalPoints instead of bestPoints. The best is about single turns, so it must be compared with the best turn so far, not with the sum of all turns.
    • Starting bestPoints at a large number such as 1000: no turn would ever beat it. Starting it at 0 is right because points are never negative. If scores could be negative, 0 would be wrong and you would need to remember whether any add had happened.
    Keep going with C++500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free