Problem overview
ScoreKeeper follows one player's game. add(points) records one turn, total() returns the sum of all turns, best() returns the highest single turn (0 before any turn), and reset() starts over.
After add(10), add(25) and add(5), the total is 40 and the best turn is 25. Points are never negative, which is why 0 is a safe starting value for the best.
Approach
The tempting design stores every turn in a list and works out the total and the best when asked. That needs memory for every turn and a pass over them for each question. It is simpler to keep just two numbers and update them as each turn arrives. Then every method runs in constant time: add adds to the total and compares with the best, and the other methods only report the fields.
Step by step
- Two fields with starting values.
totalPointsandbestPointsboth start at 0. Because the fields are initialized where they are declared, the class needs no constructor. - Names that differ from the methods. A field called
totalwould clash with the methodtotal(), so the fields aretotalPointsandbestPoints. - add. Add the points to the total with
+=. Then, if the points beat the best so far, replace the best. This is the "largest so far" idea applied one value at a time. - total and best. Each returns its field.
- reset. Set both fields back to 0, the values a new keeper starts with.
The solution in TypeScript
class ScoreKeeper {
totalPoints: number = 0;
bestPoints: number = 0;
add(points: number): void {
this.totalPoints += points;
if (points > this.bestPoints) {
this.bestPoints = points;
}
}
total(): number {
return this.totalPoints;
}
best(): number {
return this.bestPoints;
}
reset(): void {
this.totalPoints = 0;
this.bestPoints = 0;
}
}
totalPoints: number = 0;declares the field and gives it an initial value. Each new object gets its own copy.voidas a return type means the method returns nothing useful:addandresetchange the object instead.- Strict
>. An equal score leaves the best unchanged, which is the same number anyway.
Using the class
const k = new ScoreKeeper();
k.add(10);
k.add(25);
k.add(5);
console.log(k.total(), k.best());
k.reset();
console.log(k.total(), k.best());
k.add(20);
console.log(k.total(), k.best());
const other = new ScoreKeeper();
other.add(7);
console.log(k.total(), other.total());
const e = new ScoreKeeper();
e.add(0);
console.log(e.best(), e.total());
40 25
0 0
20 20
20 7
0 0
After reset() the earlier 25 no longer counts, so after add(20) the best is 20. The two keepers are independent: other has its own total. A turn worth 0 changes nothing.
Tracing the state
Because the class keeps only two numbers, you can follow its whole life by watching them. Starting from a new keeper, with the fields shown as total and best:
| Call | total | best |
|---|---|---|
add(30) | 30 | 30 |
add(50) | 80 | 50 |
add(20) | 100 | 50 |
reset() | 0 | 0 |
add(20) | 20 | 20 |
The 20 that comes after the reset becomes the best, even though the best was 50 before it: the old 50 is gone. The total always grows by the points of the turn, while the best changes only when a turn beats it. Two equal turns, such as add(7) twice on a new keeper, give a total of 14 and a best of 7.
Why not store the turns
A list of turns would answer the same questions, but best() would have to scan the whole list every time, and the list would grow with every turn. Keeping a running summary is a common pattern: when a question can be answered from a few numbers that are easy to update, keep those numbers and drop the history. The limit of 300 operations in the lesson is small, but the habit matters for larger programs.
Another way: set the fields in a constructor
Field initializers are not the only choice. You can declare the fields without values and set them in a constructor, which behaves the same:
class ScoreKeeper {
totalPoints: number;
bestPoints: number;
constructor() { this.totalPoints = 0; this.bestPoints = 0; }
add(points: number): void {
this.totalPoints += points;
if (points > this.bestPoints) { this.bestPoints = points; }
}
total(): number { return this.totalPoints; }
best(): number { return this.bestPoints; }
}
const k = new ScoreKeeper();
k.add(3);
k.add(9);
k.add(4);
console.log(k.total(), k.best());
This prints 16 9. The initializer form is shorter when every field starts with a fixed value, and the lesson uses it because there is nothing else for a constructor to do. Note that reset() repeats the same two zeros: the starting values and the reset values must always agree.
Edge cases and common mistakes
- Forgetting to reset the best. Resetting only the total would leave an old best behind. Both fields must go back to 0.
- Replacing the best on every add. Without the
if,best()would return the latest turn, not the highest. - Calling
best()before anyadd. It returns 0 because the field starts at 0. - Forgetting
this.. Inside a method,totalPoints += pointsdoes not refer to the field; always writethis.totalPoints.
Key points
- Keep a running summary instead of the full history when a few numbers answer every question.
- A field initializer gives each new object its own starting value, so no constructor is needed.
- Name fields differently from methods:
totalPointsnext tototal(). - The best so far changes only when a bigger value arrives, and a reset must restore every field.
- All four methods run in constant time.
