← thecodex.expert · The Codex Family of Knowledge
Tier 0 · Absolute Beginner · Kotlin Project

Number Guessing Game

Your very first program with a memory and a decision: the computer picks a secret number, and you guess until you get it. You will learn how a program asks a question, checks an answer, and repeats.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want a tiny game: the computer thinks of a number between 1 and 100, and the player keeps guessing. After each guess the computer says “too high” or “too low” until the player gets it right. Simple — but it teaches the three things every program does: ask, decide, and repeat.

Where this shows up in real life: every time an app checks your password, validates a form, or keeps asking until you give a valid answer, it is doing exactly this — take input, compare it to something, and loop until the condition is met. Learn it here in 15 lines, and you have learned the heartbeat of almost every program.

2 How to Think About It

Before writing any code, picture the game as a loop with a decision inside it. You do not need to know Kotlin yet — you just need to know the shape of what happens:

The plan — in plain English
1. Pick a secret number once, at the start. → 2. Ask the player to guess. → 3. Compare the guess to the secret: lower, higher, or equal? → 4. If not equal, go back to step 2. If equal, celebrate and stop. That is the whole program. Everything below is just saying this in Kotlin.

Too low

Too high

Correct!

Computer picks a secret number 1-100

Ask the player to guess

Is the guess right?

Say 'too low'

Say 'too high'

Show how many guesses it took

Game over

3 The Build — explained part by part

Here is the complete game. Read each part’s note below — you should understand the whole thing from the notes alone.

KotlinguessingGame.kt
import kotlin.random.Random
import java.io.PrintStream

/**
 * Plays one game against a known secret, reading guesses from [guesses]
 * instead of a real terminal — kept separate from `main` so it is directly
 * testable with no randomness and no typed input involved.
 *
 * @return the number of guesses it took, or null if [guesses] ran out first
 */
fun playGame(secret: Int, guesses: List<Int>): Int? {
    var taken = 0
    for (guess in guesses) {
        taken++
        if (guess == secret) return taken
    }
    return null
}

fun main() {
    System.setOut(PrintStream(System.out, true, "UTF-8")) // keep the em dash readable on any locale
    val secretNumber = Random.nextInt(1, 101) // 1..100 inclusive
    var guessesTaken = 0

    println("I am thinking of a number between 1 and 100.")

    while (true) {
        print("Your guess: ")
        val line = readLine() ?: run {
            println("\nNo more input — goodbye.")
            return
        }
        val guess = line.trim().toIntOrNull()
        if (guess == null) {
            println("Please type a whole number.")
            continue
        }
        guessesTaken++

        when {
            guess < secretNumber -> println("Too low. Try a bigger number.")
            guess > secretNumber -> println("Too high. Try a smaller number.")
            else -> {
                println("Correct! You got it in $guessesTaken guesses.")
                return
            }
        }
    }
}
⚠ No in-browser playground here
Kotlin compiles to real JVM bytecode, not something a browser can run directly — running it live would need either a server-side compiler or a third-party embed, the same kind of external dependency this site avoids relying on for a core teaching example. Copy the code below and run it with a real kotlinc on your own machine instead; the “Run It” section explains exactly how.
What each part does — in plain words
import kotlin.random.Random — Kotlin's own multiplatform random API. Random.nextInt(1, 101) picks a whole number from 1 up to (but not including) 101 — i.e. 1 to 100.

fun playGame(secret: Int, guesses: List<Int>): Int? — a testable version of the game, pulled out of the interactive loop exactly like the Python/Go versions. Its return type, Int?, is Kotlin's nullable type: “either a number of guesses, or nothing at all” — and the compiler forces every caller to check before treating the result as a real Int.

val line = readLine() ?: run { ...; return } — the Elvis operator ?: supplies what to do when readLine() returns null (input has run out): here, print a message and return from main entirely, so the loop can never spin forever on exhausted input.

when { guess < secretNumber -> ...; guess > secretNumber -> ...; else -> ... } — a condition-only when (no subject) reads like a clean chain of if/else if, picking the first branch whose condition is true.
Common mistakes — and how to avoid them
✗ Using a predictable or low-quality source of randomness for the secret number.
✓ kotlin.random.Random is fine for a game; for anything where unpredictability actually matters (see the password generator project), reach for java.security.SecureRandom instead.
✗ Looping on input forever, even after the input source has run out.
✓ Check whether readLine() returned null and break out (or return) — otherwise a piped-in file with too few lines causes an infinite loop with no new prompts, since a plain, unchecked loop keeps asking.
✗ Writing the return type as just Int when the function can also return “no answer”.
✓ Use the nullable Int? so the compiler reminds every caller to check (with ?., ?:, or an explicit if) before using the result.

4 Test & Prove Each Part

How do we know this works? We pull the real logic into small, plain functions and check each one against cases we already know the answer to.

If the player guesses the secret immediately, it takes 1 try
If the right answer is the third guess, it returns 3
If the player never guesses it, the result is null, not a crash
The secret number is always between 1 and 100, across 1000 trials
KotlinguessingGameTest.kt
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue

class GuessingGameTest {
    @Test
    fun correctFirstTry() {
        assertEquals(1, playGame(42, listOf(42)))
    }

    @Test
    fun takesThreeTries() {
        assertEquals(3, playGame(50, listOf(10, 90, 50)))
    }

    @Test
    fun neverGuesses() {
        assertNull(playGame(7, listOf(1, 2, 3)))
    }

    @Test
    fun secretIsInRange() {
        repeat(1000) {
            val n = Random.nextInt(1, 101)
            assertTrue(n in 1..100)
        }
    }
}

Compile with kotlinc guessingGame.kt guessingGameTest.kt -include-runtime -d guessingGame.jar and run with JUnit's own runner. Because playGame takes the secret and the list of guesses as plain arguments, the tests never have to deal with real randomness or real typed input — they hand it fixed numbers and check the return value.

5 The Interface

Even a tiny program has an interface — the way a person interacts with it. Here is its contract, documented plainly, the same way a professional would describe any tool.

INPUTYour guessa whole number 1–100, typed by the player
What it expects
A number like 42, typed and then Enter pressed.
OUTPUTFeedbackone of three replies
What it returns
"Too low. Try a bigger number."
"Too high. Try a smaller number."
"Correct! You got it in N guesses."

6 Run It & Automate It

Save the code as guessingGame.kt and compile it with kotlinc guessingGame.kt -include-runtime -d guessingGame.jar.

Run it locally
kotlinc guessingGame.kt -include-runtime -d guessingGame.jar && java -jar guessingGame.jar
Keep guessing until you find the secret number between 1 and 100.

A CI tool like Jenkins compiles and tests automatically whenever the code changes — every line below has a plain explanation.

What you should see when it works
Terminala real run
I am thinking of a number between 1 and 100.
Your guess: 50
Too low. Try a bigger number.
Your guess: 75
Too high. Try a smaller number.
Your guess: 63
Correct! You got it in 3 guesses.
If it breaks — how to fix it
🚨 The game loops forever printing “Too low” after I pipe in a short list of guesses
That means the program is not checking for the end of input. Make sure you return (or break) as soon as readLine() comes back null (see the code above).
🚨 A typed word like “fifty” is silently ignored instead of ending the game
line.trim().toIntOrNull() returns null for non-numeric text; this program's loop prints “Please type a whole number.” and asks again rather than crashing — type an actual whole number to continue.
GroovyJenkinsfile
// Jenkinsfile &mdash; compiles and tests automatically every time the code changes.
pipeline {
    agent any                                  // run on any available machine

    stages {
        stage('Get the code') {
            steps { checkout scm }             // download the latest code
        }
        stage('Set up Kotlin') {
            steps {
                sh 'kotlinc -version'                             // confirm the compiler is installed
            }
        }
        stage('Compile and test') {
            steps {
                sh 'kotlinc guessingGame.kt guessingGameTest.kt -include-runtime -d build.jar'  // one real JVM jar, no build tool required
                sh 'java -cp build.jar:kotlin-test-junit.jar:junit.jar org.junit.runner.JUnitCore GuessingGameTest'
            }
        }
    }

    post {
        success { echo 'All tests passed.' }
        failure { echo 'A test failed &mdash; look above.' }
    }
}
🎯 Try this next — make it yours

You have a working number guessing game. Extend it:

  1. Limit the attempts. End the game after 10 wrong guesses. (Teaches: a counter plus an early exit.)
  2. Add difficulty levels. Let the player choose a range like 1–1000. (Teaches: parameterising Random.nextInt.)
  3. Give a “warmer/colder” hint. Compare how close each guess is to the last one. (Teaches: keeping extra state between loop iterations.)
  4. Play again. After a win, ask “Play again? (y/n)” and restart. (Teaches: wrapping the whole game in an outer loop.)
What you learned
You learned a testable-core pattern (playGame takes plain data, not live input), Kotlin’s nullable Int? for “a value or nothing”, and the Elvis operator ?: for a default when a nullable value is missing. Related reference: Null Safety & Types, Functions in Kotlin.