Start learning free

LESSON 254 OF 574 · FUNCTIONS & CLASSES

Score Keeper in Swift: A Class with Running 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

    SWIFT · FUNCTIONS & CLASSES

    Score Keeper in Swift, explained

    Problem overview

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

    • add(_ points: Int) 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 that total() and best() are 0 until the next add.

    After add(10), add(25), add(5), the total is 40 and the best is 25.

    Approach

    The keeper never needs to remember the individual turns. Two numbers are enough: the running total and the best so far. Each add updates both, so total() and best() only report them. Every method is O(1).

    The best starts at 0 because points are never negative: the first turn is always at least 0, so it can't be hidden by the start value, and "no add yet" correctly reports 0.

    Step by step

    1. Declare totalPoints with var, type Int, starting at 0.
    2. Declare bestPoints the same way. The property names differ from the method names total and best, because a property and a method can't share a name in the same class.
    3. In add, grow the total: totalPoints += points, whatever the points are.
    4. Then update the best: if points is greater than bestPoints, store it. This is the "largest so far" idea applied one value at a time. An equal score changes nothing, which is fine because it is the same number.
    5. total() and best() return the two properties.
    6. reset() sets both properties back to 0, the values a new keeper starts with.

    The solution in Swift

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

    Here it is in use:

    let k = ScoreKeeper()
    print(k.best(), k.total())
    k.add(30)
    k.add(50)
    print(k.total(), k.best())
    k.reset()
    print(k.total(), k.best())
    k.add(20)
    print(k.best(), k.total())
    
    0 0
    80 50
    0 0
    20 20
    

    After the reset, the 50 no longer counts: the best after add(20) is 20.

    Swift details worth knowing

    • Default values replace init. Both properties have a starting value, so the class needs no initializer: ScoreKeeper() works as it is, and every keeper gets its own pair of numbers.
    • _ in add(_ points: Int) means the caller writes no label: k.add(30), not k.add(points: 30).
    • Methods can change properties of a class without any special keyword. (A struct would need mutating on add and reset.)

    Each keeper has its own numbers

    The two properties belong to the object, not to the class. If you create a second keeper and call add(7) on it, the first keeper's total doesn't move: after the run above, the first keeper still has a total of 20 while the new one has 7. That is what "one new ScoreKeeper" means in the lesson's tests, and why the properties are declared inside the class body without static.

    Why reset sets the best to 0

    It would be tempting to think of reset() as "forget the total". But the best is also history, and a keeper that has been reset should behave like a brand new one. The simplest way to guarantee that is for reset() to assign the same two starting values that the property declarations use.

    Edge cases and common mistakes

    • Asking before any add. Both answers are 0, as the first line of the run shows.
    • A turn worth 0. add(0) changes neither number.
    • Forgetting bestPoints = 0 in reset. The total would restart, but an old best of 50 would survive and best() would wrongly return 50 after add(20).
    • Naming a property total. It clashes with the method total(); pick a different property name.
    • Recomputing instead of remembering. Storing every turn in an array would work but needs more memory and more time per question; two numbers do the job.

    Complexity

    Every method does a fixed number of steps: one addition and one comparison in add, a single read in total() and best(), two assignments in reset(). So each call is O(1) time, however many turns have been added, and the class uses O(1) space: two integers.

    Testing

    These sequences are worth trying in a file of your own, with the answers each call must give:

    • add(10), add(25), add(5), then total() and best(): 40 and 25.
    • best() and total() on a brand new keeper: 0 and 0.
    • add(30), add(50), reset(), then total() and best(): 0 and 0.
    • After that, add(20), then best() and total(): 20 and 20.

    The same sequences, run on the solution above, print exactly these numbers.

    Key takeaways

    • When a question asks for a running summary, store the summary and update it on every event, instead of storing the events.
    • Each object has its own copy of its properties.
    • A reset method should put the object back into the same state a new one starts in.
    Keep going with Swift500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free