Skip to main content
Build an interactive guessing game where the program randomly generates numbers and the player tries to match one of them.

What You’ll Build

A command-line game that:
  • Accepts a number guess from the user
  • Generates random numbers multiple times
  • Checks if any random number matches the guess
  • Declares the player a winner if they match
  • Uses proper randomization seeding

What You’ll Learn

  • Generating random numbers with math/rand
  • Seeding random number generators
  • Converting strings to integers with strconv
  • Using loops for game logic
  • Validating user input
  • Early returns for error handling

Game Rules

  1. Player provides a number as a command-line argument
  2. The program generates 5 random numbers between 0 and the player’s number
  3. If any random number matches the guess, the player wins
  4. The larger the number, the harder the game

Understanding Randomization

First, let’s understand how random number generation works:
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	// ❌ Without seeding - same numbers every time
	// rand.Intn(10)  // Always generates same sequence
	
	// ✅ With seeding - different numbers each run
	rand.Seed(time.Now().UnixNano())
	
	guess := 10
	
	// Generate random numbers until we hit the guess
	for n := 0; n != guess; {
		n = rand.Intn(guess + 1)  // 0 to guess (inclusive)
		fmt.Printf("%d ", n)
	}
	fmt.Println()
}
Output (example):
3 7 1 9 4 10

Complete Game Implementation

package main

import (
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	maxTurns = 5 // Number of chances to win
	usage    = `Welcome to the Lucky Number Game! 🍀

The program will pick %d random numbers.
Your mission is to guess one of those numbers.

The greater your number is, harder it gets.

Wanna play?
`
)

func main() {
	// Seed random number generator
	rand.Seed(time.Now().UnixNano())

	// Get command-line arguments
	args := os.Args[1:]

	if len(args) != 1 {
		fmt.Printf(usage, maxTurns)
		return
	}

	// Convert argument to integer
	guess, err := strconv.Atoi(args[0])
	if err != nil {
		fmt.Println("Not a number.")
		return
	}

	// Validate input
	if guess < 0 {
		fmt.Println("Please pick a positive number.")
		return
	}

	// Play the game
	for turn := 0; turn < maxTurns; turn++ {
		n := rand.Intn(guess + 1)

		if n == guess {
			fmt.Println("🎉  YOU WIN!")
			return
		}
	}

	fmt.Println("☠️  YOU LOST... Try again?")
}

Running the Game

# Easy game (small number)
go run main.go 5
# Output might be: 🎉  YOU WIN!

# Medium difficulty
go run main.go 20

# Hard game (large number)
go run main.go 100
# Output likely: ☠️  YOU LOST... Try again?

# No argument
go run main.go
# Shows usage message

# Invalid input
go run main.go hello
# Output: Not a number.

go run main.go -10
# Output: Please pick a positive number.

Key Concepts

import "math/rand"

// Generate random int from 0 to n-1
x := rand.Intn(10)  // 0, 1, 2, ..., 9

// Generate 0 to n (inclusive)
x := rand.Intn(n + 1)  // 0, 1, 2, ..., n

// Generate random float 0.0 to 1.0
f := rand.Float64()
import (
    "math/rand"
    "time"
)

// ❌ Same seed = same sequence
rand.Seed(10)
rand.Intn(100)  // Always same number

// ✅ Time-based seed = different each run
rand.Seed(time.Now().UnixNano())
rand.Intn(100)  // Different number each time
UnixNano() returns current time in nanoseconds since Jan 1, 1970.
import "strconv"

// Convert string to int
num, err := strconv.Atoi("42")
if err != nil {
    // Not a valid integer
    fmt.Println("Invalid number")
    return
}
fmt.Println(num)  // 42

// Examples:
strconv.Atoi("123")   // 123, nil
strconv.Atoi("hello") // 0, error
strconv.Atoi("-5")    // -5, nil
strconv.Atoi("3.14")  // 0, error (use ParseFloat for floats)
func main() {
    if /* error condition */ {
        fmt.Println("Error!")
        return  // Exit early
    }
    
    // Continue normal execution
    // ...
    
    if /* success condition */ {
        fmt.Println("Success!")
        return  // Exit early with success
    }
    
    // Default case
    fmt.Println("Neither error nor success")
}
Early returns make code more readable by handling special cases first.

Game Mechanics Explained

1

Input Validation

// Check argument count
if len(args) != 1 {
    fmt.Printf(usage, maxTurns)
    return
}

// Parse integer
guess, err := strconv.Atoi(args[0])
if err != nil {
    fmt.Println("Not a number.")
    return
}

// Validate range
if guess < 0 {
    fmt.Println("Please pick a positive number.")
    return
}
2

Random Number Generation

// Generate number from 0 to guess (inclusive)
n := rand.Intn(guess + 1)

// Examples:
// guess = 10 → n can be 0, 1, 2, ..., 10
// guess = 5  → n can be 0, 1, 2, 3, 4, 5
3

Win Condition

for turn := 0; turn < maxTurns; turn++ {
    n := rand.Intn(guess + 1)
    
    if n == guess {
        fmt.Println("🎉  YOU WIN!")
        return  // Exit immediately on win
    }
}
// If loop completes, player lost
fmt.Println("☠️  YOU LOST... Try again?")

Probability Mathematics

Understanding the odds:
// Probability of winning on each turn:
// P(win) = 1 / (guess + 1)

// Examples:
guess = 5P(win per turn) = 1/616.7%
guess = 10P(win per turn) = 1/119.1%
guess = 99P(win per turn) = 1/100 = 1%

// Over 5 turns:
// P(win in 5 turns) = 1 - P(lose all 5 turns)
// P(lose all 5) = (1 - 1/(guess+1))^5

// Examples for 5 turns:
guess = 5~60% chance to win
guess = 10~40% chance to win
guess = 99~5% chance to win

Enhancements to Try

  1. Show attempts - Print each random number generated
  2. Difficulty levels - Easy (5 numbers), Medium (10), Hard (20)
  3. Score tracking - Count wins/losses across multiple games
  4. Range guessing - Guess within a range instead of exact match
  5. Hints - Show “higher” or “lower” hints
  6. Time limit - Add a time constraint
  7. Statistics - Track and display win rate

Debugging Version

Show what numbers are generated:
for turn := 0; turn < maxTurns; turn++ {
    n := rand.Intn(guess + 1)
    fmt.Printf("Turn %d: Generated %d\n", turn+1, n)  // Debug output
    
    if n == guess {
        fmt.Println("🎉  YOU WIN!")
        return
    }
}

Common Mistakes

Forgetting to Seed:
// ❌ Without seed - same numbers every run
func main() {
    n := rand.Intn(10)
    fmt.Println(n)  // Always same number!
}

// ✅ With seed - different numbers
func main() {
    rand.Seed(time.Now().UnixNano())
    n := rand.Intn(10)
    fmt.Println(n)  // Different each time
}
Off-by-One Errors:
// Want numbers 0 to 10 (inclusive)

// ❌ Wrong - gives 0 to 9
n := rand.Intn(10)

// ✅ Correct - gives 0 to 10
n := rand.Intn(11)

// Or more clearly:
n := rand.Intn(guess + 1)

Next Steps

Word Finder

More loop practice with strings

Loops Guide

Master Go loops

Build docs developers (and LLMs) love