coding-horror/basic-computer-games · error

AI RAN OUT OF OPTIONS!

Error message

AI RAN OUT OF OPTIONS!

What it means

A panic via .expect("AI RAN OUT OF OPTIONS!") on Iterator::choose() over all_positions in ai_turn() of the Bombardment Rust port. choose() returns None only when the slice is empty, so this fires when the AI has no remaining board positions to target. all_positions is meant to track every not-yet-fired cell; reaching an empty vector before the game is won indicates a logic/state bug (the AI firing loop is being entered after the position pool was exhausted).

Source

Thrown at 11_Bombardment/rust/src/main.rs:95

            );
        } else {
            println!(
                "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN MY TRANSISTO&S RECUP%RA*E!\n\n"
            );
            return false;
        }
    } else {
        println!("HA, HA YOU MISSED. MY TURN NOW\n");
    }
    true
}

fn ai_turn(all_positions: &mut Vec<u8>, player_positions: &mut Vec<u8>) -> bool {
    std::thread::sleep(Duration::from_secs(1));

    let ai_missile = *all_positions
        .choose(&mut rand::thread_rng())
        .expect("AI RAN OUT OF OPTIONS!");

    let index = all_positions
        .iter()
        .position(|p| p == &ai_missile)
        .expect("AI CHOOSE AN INVALID POSITION!");

    all_positions.remove(index);

    if let Some(index) = player_positions.iter().position(|p| p == &ai_missile) {
        player_positions.remove(index);

        let remaining = player_positions.len();

        if remaining > 0 {
            println!("I GOT YOU. IT WON'T BE LONG NOW. POST {ai_missile} WAS HIT.");
            println!(
                "YOU HAVE ONLY {} OUTPOST LEFT.\n",
                get_text_from_number(remaining)

View on GitHub (pinned to 5301155192)

Solutions

  1. Guard ai_turn() with `if all_positions.is_empty() { return false; }` before calling choose.
  2. Verify the main game loop breaks immediately when either side has no positions left.
  3. Ensure all_positions is sized to the full board and only one entry is removed per AI turn.

Example fix

// before
let ai_missile = *all_positions
    .choose(&mut rand::thread_rng())
    .expect("AI RAN OUT OF OPTIONS!");

// after
if all_positions.is_empty() {
    return false; // no targets left, end game
}
let ai_missile = *all_positions
    .choose(&mut rand::thread_rng())
    .expect("AI RAN OUT OF OPTIONS!");
Defensive patterns

Strategy: validation

Validate before calling

// Guard ai_turn before calling choose
fn ai_turn(all_positions: &mut Vec<u8>, player_positions: &mut Vec<u8>) -> bool {
    if all_positions.is_empty() { return false; }
    // ... existing logic

Type guard

// Rust: empty check is the idiomatic guard
let has_targets = !all_positions.is_empty();

Try / catch

// Replace .expect with a match on Option
let ai_missile = match all_positions.choose(&mut rand::thread_rng()) {
    Some(&p) => p,
    None => return false, // no targets left; end game
};

Prevention

When it happens

Trigger: Calling ai_turn() when all_positions is empty (every cell already consumed); a turn-order bug that lets the AI take extra turns; a victory/loss check that fails to stop the loop before the AI runs out of targets.

Common situations: Miscalibrated board size vs number of turns; a draw/stalemate condition not detected; the game loop not breaking after player_positions hits zero, letting ai_turn run again on an empty pool.

Related errors


AI-assisted analysis of coding-horror/basic-computer-games@5301155192 (2026-08-13). Data as JSON: /api/errors/6b2a376c1949b1db. Report an issue: GitHub.