# 2. A guessing game

> Chapter 2: build a number guessing game step by step and meet variables, input, nil, loops and comparisons.

Canonical: https://alice-lang.org/documentation/learn/guessing-game/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

Let's write a real program before we study the parts. You will see several ideas at once here: variables, reading input, values that may be missing, loops and decisions. Don't worry about the details yet. Each idea gets its own chapter later. The goal now is to see how they fit together.

The game works like this. The program picks a secret number between 1 and 100. You guess. The program says whether your guess is too small or too big. When you guess right, it congratulates you and stops.

## Step 1: read a guess

Create a file `guess.al`:

```alice stdin="41\n"
puts "Guess the number!"
print "Your guess: "
line = gets
puts "You typed: #{line}"
```

Run it with `alice run guess.al`, type `41` and press Enter:

```text title="Output"
Guess the number!
Your guess: You typed: 41
```

On your terminal you also see the `41` you typed, after `Your guess: `. Here is what each line does:

- `print` is like `puts` but does not add a newline. The cursor waits right after the prompt.
- `gets` reads one line of input. It returns the text you typed without the final newline.
- `line = gets` creates a **variable** named `line` and stores the text in it. You do not declare a type: Alice sees that `gets` returns text and gives `line` that type.

## Step 2: what if there is no input?

There is a subtle problem. What does `gets` return when there is nothing more to read? That happens when you press Ctrl-D on Linux or macOS, or when the input comes from an empty file.

In many languages this is a bug waiting to happen: the program crashes later, far from the cause. In Alice, `gets` returns a `String?`, read "String or nil". `nil` means "no value". The `?` in the type is the compiler's reminder that you must handle that case.

Watch what happens if you forget. This program turns the line into a number with `to_i` without checking it first:

```alice fail="AL0310"
line = gets
puts line.to_i + 1
```

```text title="Error"
error[AL0310]: value may be nil here; check it (`if x`, `x.nil?`, `x || default`) or assert with `x.not_nil!`
```

The compiler refuses to build the program, and it suggests fixes. The simplest fix is to check:

```alice stdin="41\n"
line = gets
if line
  puts line.to_i + 1
else
  puts "No input."
end
```

```text title="Output"
42
```

In Alice, `nil` and `false` are the only values that count as false in a condition. So `if line` means "if there is a line". Inside the `if`, the compiler knows that `line` is a `String`, not a `String?`, and lets you use it.

## Step 3: turn text into a number

`"41".to_i` gives `41`. But what does `"abc".to_i` give? It gives `0`, as in Ruby, and `0` looks like a real guess. A typo would silently count as a try. We want to tell a number from garbage.

`to_i?` answers exactly that question. It returns an `Int32?`: the number, or `nil` when the text is not a number.

```alice
puts "41".to_i?
puts "abc".to_i?.nil?
```

```text title="Output"
41
true
```

`strip` removes spaces around the text, so `" 41 "` also counts. Now we can compare the guess with a secret. For now we pick the secret ourselves. That makes the program easy to test.

```alice stdin="50\n"
secret = 42
line = gets
if line
  guess = line.strip.to_i?
  if guess
    if guess < secret
      puts "Too small."
    elsif guess > secret
      puts "Too big."
    else
      puts "You win!"
    end
  else
    puts "Please type a number."
  end
end
```

```text title="Output"
Too big.
```

`if`, `elsif` and `else` choose one branch. `<` and `>` compare numbers. Notice how the inner `if guess` turns an `Int32?` into an `Int32`, just as `if line` did for the text. Without it, `guess < secret` would not compile.

## Step 4: let the player try again

The nesting is getting deep. Two Alice habits flatten it:

- `loop do ... end` repeats its body until a `break`.
- `break unless line` and `next unless guess` are **guard clauses**. `unless` means "if not". `next` skips to the next round of the loop. After a guard, the compiler knows the value is not `nil`.

```alice stdin="50\nabc\n20\n42\n"
secret = 42
puts "Guess the number!"
loop do
  print "Your guess: "
  line = gets
  break unless line
  guess = line.strip.to_i?
  unless guess
    puts "Please type a number."
    next
  end
  if guess < secret
    puts "Too small."
  elsif guess > secret
    puts "Too big."
  else
    puts "You win!"
    break
  end
end
```

The book's test feeds this program four lines: `50`, `abc`, `20` and `42`.

```text title="Output"
Guess the number!
Your guess: Too big.
Your guess: Please type a number.
Your guess: Too small.
Your guess: You win!
```

## Step 5: a real secret

A game with a fixed secret is not much fun. .NET has a random number generator, and Alice can use any .NET class directly. `Random.Shared.Next(1, 101)` returns a number from 1 up to, but not including, 101.

Let's also count the tries. `tries += 1` is short for `tries = tries + 1`.

```alice
secret = Random.Shared.Next(1, 101)
tries = 0
puts "Guess the number between 1 and 100!"

loop do
  print "Your guess: "
  line = gets
  break unless line
  guess = line.strip.to_i?
  unless guess
    puts "Please type a number."
    next
  end
  tries += 1
  if guess < secret
    puts "Too small."
  elsif guess > secret
    puts "Too big."
  else
    puts "You win after #{tries} tries!"
    break
  end
end
```

The output differs every time, so the book only checks that this version compiles. A game might go like this:

```text title="A sample game"
Guess the number between 1 and 100!
Your guess: 50
Too big.
Your guess: 25
Too small.
Your guess: 37
You win after 3 tries!
```

That is the whole game. In about twenty lines you used variables, input, nil checks, numbers, comparisons, loops and a .NET class.

:::caution\[Common mistakes]

- **Using `gets` without a check.** Its type is `String?`. Calling a method on it directly is `error[AL0310]`. Check it with `if line` or a guard such as `break unless line`.
- **Using `to_i` to validate input.** `"abc".to_i` is `0`, not an error. Use `to_i?` and check for `nil`.
- **`Next(1, 100)` for 1 to 100.** The upper bound of `Random.Next` is excluded. Write `Next(1, 101)`.
  :::

## Exercises

1. Make the game refuse guesses outside 1 to 100 with the message `Between 1 and 100, please.`, without counting them as tries.
2. End the game after 7 tries and reveal the secret.
3. Why does `line.strip.to_i?` compile inside the loop, while `line.to_i` did not compile in step 2?

<details>
<summary>Answers</summary>

1. Add a second guard after the number check. `||` means "or":

   ```alice stdin="500\n42\n"
   secret = 42
   tries = 0
   loop do
     line = gets
     break unless line
     guess = line.strip.to_i?
     next unless guess
     if guess < 1 || guess > 100
       puts "Between 1 and 100, please."
       next
     end
     tries += 1
     if guess == secret
       puts "You win after #{tries} tries!"
       break
     end
   end
   ```

   ```text title="Output"
   Between 1 and 100, please.
   You win after 1 tries!
   ```

2. Check the count after each wrong guess:

   ```alice stdin="1\n2\n3\n4\n5\n6\n7\n"
   secret = 42
   tries = 0
   loop do
     line = gets
     break unless line
     guess = line.strip.to_i?
     next unless guess
     tries += 1
     if guess == secret
       puts "You win!"
       break
     end
     if tries == 7
       puts "Out of tries. It was #{secret}."
       break
     end
   end
   ```

   ```text title="Output"
   Out of tries. It was 42.
   ```

3. Because of the guard `break unless line` just above it. After the guard, the compiler knows `line` cannot be `nil`, so its type there is `String`.

</details>

## Summary

- `gets` returns `String?`. A `?` type may be `nil`, and the compiler makes you check before use.
- `if x`, `unless x` and guards like `break unless x` check for `nil`. After the check, `x` has the plain type.
- `to_i?` parses a number or returns `nil`. `to_i` returns `0` for text that is not a number.
- `loop do ... end` repeats until `break`. `next` skips to the next round.
- Any .NET class, like `Random`, is available by name.

[Chapter 3](https://alice-lang.org/documentation/learn/common-concepts.md) looks at each of these ideas closely, starting with values and variables.

## Continue reading

- Previous: [1.3 Hello, project](https://alice-lang.org/documentation/learn/first-project.md)
- Next: [3. Common programming concepts](https://alice-lang.org/documentation/learn/common-concepts.md)
