Problem overview
A game shows each player two numbers: the total of all their turns and their best single turn. You write a class ScoreKeeper that keeps both up to date as points come in, and can start over.
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 singleaddin that time, or 0 if nothing was added.reset()starts over: both numbers go back to 0.
After add(10), add(25) and add(5), the total is 40 and the best is 25.
Approach
You could store every turn in a list and compute the sum and the maximum on demand. That works, but every question would have to look at all the turns. Instead, keep just two numbers and update them as each turn arrives. Then total and best only report a stored value, and every method takes constant time.
The two numbers are properties of the object: each ScoreKeeper has its own pair. Their names must differ from the method names total and best, so the lesson uses totalPoints and bestPoints.
Step by step
- Declare the properties. Both are
var, becauseaddandresetchange them, and both start at 0. Zero is a safe start for the best: before anyaddthe answer must be 0, and every score is at least 0. - add the points to the total. This happens on every call, whatever the points are.
- Update the best. If
pointsis greater thanbestPoints, store it. This is the "largest so far" idea applied one value at a time. An equal score leaves the best unchanged, which is the same number anyway. - total and best just return the stored values.
- reset sets both properties back to 0, the values a new keeper starts with. The old best no longer counts: after a reset and
add(20), the best is 20, even if it was 50 before.
The solution in Kotlin
class ScoreKeeper {
var totalPoints: Int = 0
var bestPoints: Int = 0
fun add(points: Int) {
totalPoints += points
if (points > bestPoints) {
bestPoints = points
}
}
fun total(): Int {
return totalPoints
}
fun best(): Int {
return bestPoints
}
fun reset() {
totalPoints = 0
bestPoints = 0
}
}
A property declared in the class body needs an initial value (here 0), and every new object gets its own copy. add and reset return nothing, so they have no return type; Kotlin treats that as Unit.
Using the class
fun main() {
val s = ScoreKeeper()
println("${s.total()} ${s.best()}")
s.add(30)
s.add(50)
println("${s.total()} ${s.best()}")
s.reset()
println("${s.total()} ${s.best()}")
s.add(20)
println("${s.best()} ${s.total()}")
}
It prints:
0 0
80 50
0 0
20 20
Why two numbers are enough
The first idea many learners have is to store every turn in a list, because total and best sound like questions about a list. The list does work, but it has a price: it grows with each add, and each total() or best() call walks over all of it. With up to 300 operations that is harmless, but the habit is worth dropping. Ask what the answer needs to know about the past. The sum needs only the sum so far, and the maximum needs only the maximum so far. Everything else about the old turns can be forgotten the moment each turn is folded into the two numbers.
That is also why reset is so short. There is no list to empty, only two numbers to put back to the values a new keeper starts with. After it, the object cannot be told apart from a fresh one.
Each object has its own numbers
Properties belong to the object, not to the class. Two keepers never share their totals:
fun main() {
val ann = ScoreKeeper()
val bob = ScoreKeeper()
ann.add(10)
bob.add(7)
bob.add(3)
println("${ann.total()} ${ann.best()}")
println("${bob.total()} ${bob.best()}")
}
It prints:
10 10
10 7
Both totals are 10, but the best turns differ: 10 for Ann and 7 for Bob.
Common mistakes
- Forgetting to reset the best. Resetting only the total leaves the old best in place, so after a reset
best()would still report the old best. - Starting the best at a huge negative number. It isn't needed here, and it would make
best()return the wrong value before anyadd. - Recomputing from scratch. Don't keep a list of turns; two running numbers are enough.
- Naming a property
total. Kotlin would let a property and a function share a name, but the lesson's names keep the two apart and make the code easier to read.
Edge cases to try
- Nothing added yet.
total()andbest()must both return 0. Because the properties start at 0, this works without any special code. - A turn worth 0 points.
add(0)changes neither number: the total grows by 0, and 0 is not greater than the best. The best stays 0 if it was 0. - Repeated equal scores. After
add(40)twice, the total is 80 and the best is 40. The strict>doesn't matter here, because replacing 40 with 40 changes nothing. - Reset on a fresh keeper. Calling
reset()before anyaddis harmless: it sets two zeros to zero. - Reset in the middle. Only turns added after the reset count, for both numbers.
