Start learning free

LESSON 253 OF 573 · FUNCTIONS & CLASSES

Score Keeper in Scala: A Class with State

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

    SCALA · FUNCTIONS & CLASSES

    Score Keeper in Scala, explained

    Problem overview

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

    The methods are:

    • 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 single add in that time, or 0 if there was none;
    • reset() starts over, so 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.

    Approach: update the two numbers as the points arrive

    You could store every turn in a list and work out the total and the best when asked. That needs memory that grows with the number of turns, and every question has to look through the whole list. It is simpler to keep just two numbers inside the object and change them in add. Then total() and best() only report a field, and every method runs in O(1) time.

    The object has to remember its numbers between calls, so they become fields of the class. They are vars, because add and reset change them. The statement asks for field names that differ from the methods' names, so the fields are totalPoints and bestPoints.

    Step by step

    1. Declare totalPoints inside the class, a var of type Int starting at 0. Every new keeper gets its own copy.
    2. Declare bestPoints the same way. Before any add, best() must return 0, and points are never negative, so 0 is a safe starting value.
    3. In add, add the points to totalPoints with +=. The total grows on every turn.
    4. Update the best. If points is greater than bestPoints, store it. This is the same idea as finding the largest value of a list one value at a time. An equal score leaves the best unchanged, which is the same number anyway.
    5. total and best just return their fields.
    6. reset sets both fields back to 0, the values a new keeper starts with.

    The solution in Scala

    class ScoreKeeper {
        var totalPoints: Int = 0
        var bestPoints: Int = 0
    
        def add(points: Int): Unit = {
            totalPoints += points
            if (points > bestPoints) {
                bestPoints = points
            }
        }
    
        def total(): Int = {
            return totalPoints
        }
    
        def best(): Int = {
            return bestPoints
        }
    
        def reset(): Unit = {
            totalPoints = 0
            bestPoints = 0
        }
    }
    

    Scala details:

    • Fields live in the class body. A var declared there with an initial value is created for every new object, so two keepers never share their numbers.
    • Unit means nothing useful is returned. add and reset are called for their effect, so their result type is Unit.
    • Calling with or without parentheses. The methods are declared with empty parentheses, so write k.total(). Creating the object works as new ScoreKeeper or new ScoreKeeper().

    Using the class

    val k = new ScoreKeeper
    println(s"${k.best()} ${k.total()}")
    k.add(30); k.add(50)
    println(s"${k.total()} ${k.best()}")
    k.reset()
    println(s"${k.total()} ${k.best()}")
    k.add(20)
    println(s"${k.best()} ${k.total()}")

    It prints:

    0 0
    80 50
    0 0
    20 20

    A new keeper reports 0 and 0. After 30 and 50 the total is 80 and the best is 50. After reset() both are 0, and after add(20) the best is 20, not 50: the old 50 no longer counts.

    A second keeper with add(10), add(25) and add(5) reports a total of 40 and a best of 25.

    Why the fields belong to the object

    Each call to add is a separate call, and local variables vanish when a method ends. Anything that has to survive from one call to the next must live in a field. The two vars are created when new ScoreKeeper runs and stay with that object until it is no longer used. Because each object has its own copy, two keepers can run side by side: one can hold a total of 40 while the other is brand new and holds 0.

    The design also explains why add does the bookkeeping and the two getters do none. If the work were done in total() and best(), the object would need to remember every turn. Doing it in add keeps just the two numbers that the questions need.

    Complexity

    Every method does a constant amount of work: one addition, one comparison, or one field read. All of them run in O(1) time, and the object stores two integers.

    Testing

    Try these sequences on a new keeper each time, and compare with the results:

    • add(10), add(25), add(5), then total() and best(): 40 and 25.
    • best(), total(), add(0), best(), total(): all four answers are 0, since a turn worth 0 changes neither number.
    • add(30), add(50), reset(), add(20), then best() and total(): 20 and 20.

    The third case is the one that catches a reset that forgets the best: it would answer 50 for the best.

    Common mistakes

    • Not resetting the best. Setting only totalPoints to 0 in reset leaves the old best behind.
    • Starting the best at a large number. It would never be beaten. Start at 0.
    • Replacing the total with the new points. Use +=, not =.
    • Adding 0 points. It changes neither number, and add(0) on a new keeper leaves both at 0.
    Keep going with Scala500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free