Understanding Kotlin Concepts Through a Tennis Scoring System

Here’s the complete implementation followed by an explanation of key Kotlin language features:


Entire Code Implementation
(hiker.kt)

package hiker
 
const val PLAYER_1 = 1
const val PLAYER_2 = 2
 
fun answer(points: List<Int>): String {
    return when (points.size) {
        0 -> "love"
        1 -> "fifteen"
        2 -> "thirty"
        3 -> "forty"
        else -> handleComplexCases(points)
    }
}
 
private fun countPoints(points: List<Int>): List<Int> {
    val player1Points = points.count { it == PLAYER_1 }
    return listOf(player1Points, points.size - player1Points)
}
 
private fun handleComplexCases(points: List<Int>): String {
    val (player1, player2) = countPoints(points)
    return if (player1 == player2 && player1 >= 3) "deuce" else "TBD"
}

(HikerTest.kt)

package hiker
 
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
 
class BasicScoringSpec : StringSpec({
    "Return 'love' when no points scored" {
        answer(emptyList()) shouldBe "love"
    }
    // ... other basic scoring tests ...
})
 
class DeuceScenariosSpec : StringSpec({
    "Identify deuce at 3-3 points" {
        val points = List(3) { PLAYER_1 } + List(3) { PLAYER_2 }
        answer(points) shouldBe "deuce"
    }
    // ... other deuce tests ...
})

Key Kotlin Concepts Explained

1. Compile-Time Constants

const val PLAYER_1 = 1
  • What: Immutable values known at compile time
  • Why: Safer than magic numbers, improves readability
  • Usage: Score identification markers that never change

2. When Expression

fun answer(points: List<Int>): String {
    return when (points.size) {
        0 -> "love"
        // ... other cases ...
    }
}
  • Type-Safe: Compiler checks for exhaustive coverage
  • Expression Body: Directly returns a value
  • Clean Alternative: Replaces complex if-else chains

3. Collection Operations

points.count { it == PLAYER_1 }
List(3) { PLAYER_1 }
  • Higher-Order Functions: count() with predicate lambda
  • List Factory: Create lists with repeated elements
  • Immutability: Default to read-only collections

4. Destructuring Declarations

val (player1, player2) = countPoints(points)
  • Tuple Unpacking: Directly assign components to variables
  • Works With: Lists, data classes, pairs
  • Safety: Compiler verifies component count

5. Testing DSL

class BasicScoringSpec : StringSpec({
    "Return 'love'..." {
        answer(emptyList()) shouldBe "love"
    }
})
  • StringSpec Style: Tests read like natural language
  • Infix Notation: shouldBe for fluent assertions
  • Grouping: Logical separation into multiple specs

6. Type Inference

val player1Points = points.count { ... } // Inferred as Int
listOf(player1Points, ...) // Inferred as List<Int>
  • Compiler Smarts: Deduces types from context
  • Reduced Ceremony: No explicit type declarations needed
  • Safety: Maintains strict typing while being concise

7. Function Visibility

private fun countPoints(...)
  • Encapsulation: Hide implementation details
  • Scope Control: Only expose public API (answer())
  • Module Safety: Prevent accidental external usage

8. Immutable Variables

val points = List(3) { ... } // Can't be reassigned
  • Principle: Prefer val over var by default
  • Thread Safety: Avoid unexpected state changes
  • Predictability: Values remain constant after initialization

Why These Concepts Matter

  1. Null Safety (Implicit in design)

    • No nullable types used in this implementation
    • Empty list handled explicitly (points.size == 0)
  2. Extension Functions (Behind the scenes)

    • count() is a stdlib extension for collections
    • List(n) { ... } uses factory functions
  3. Lambda Expressions

    List(3) { PLAYER_1 } // Lambda initializer
    points.count { it == PLAYER_1 } // Predicate lambda
  4. Standard Library Utilization

    • Leverage built-in functions instead of manual loops
    • Use type-safe operations for collection manipulation

This implementation demonstrates Kotlin’s philosophy of pragmatic, readable code while maintaining strong type safety and modern language features.