Hangman game in Rust: a story about an API you can use for free

Recently, I took part in a Rust programming course, and one of the tasks we had to do as homework was a “hangman” game. The funny thing is that to create a game with real words, not just “random” interpolated letters, I had to find a library that can take a random word from a dictionary. You can find the library commented at the top of the code: use random_word::Lang; The problem was that the library I found did not support the Polish language, and my kids did not want to play. Then I started thinking, maybe there is an API publicly available where I could get a random Polish word. And of course there are some; take a look at the example https://api.poocoo.pl. Below, you can find the snippet of code I wrote:

//use random_word::Lang;
use std::io::{self, Write};
use std::process::ExitCode;
use serde::Deserialize;

fn guess_word(s: &str) {
    let mut chance : i8 = 6;
    let mut ch : char = ' ';
    println!("The word has {} letters.", s.chars().count());
    let mut char_array: Vec<char> = vec!['_'; s.chars().count()];
    loop {
            println!("--- Hangman game ---");
            println!("Guess the word! You have 6 tries to win the game.");
            println!("Enter your letter that might me in the word (A-Z): ");

            println!("Currently word looks like this:");
            char_array.iter().for_each(|c| print!(" {} ", c));

            println!("\n");
            let mut input = String::new();
            io::stdin().read_line(&mut input);

            let trimmed = input.trim();
            if trimmed.chars().count() != 1 {
                println!("Please enter exactly one letter.");
            }else {
                ch = trimmed.chars().next().unwrap();
            }

            println!("Processing letter: {:?}", ch);
            if s.contains(ch) {
                println!("Letter {:?} is already guessed.", ch);
                char_array.iter_mut().enumerate().for_each(|(i, c)| {
                    if s.chars().nth(i).unwrap() == ch {
                        *c = ch;
                    }
                });
            } else {
                println!("Letter {:?} is not guessed yet.", ch);
                chance -= 1;
            }

            if !char_array.contains(&'_') {
                println!("You won the game!");
                println!("The word was: ",);
                char_array.iter().for_each(|c| print!(" {} ", c));
                println!("\n");
                std::process::exit(0);
            }
            hungman(chance, &s);
    }   
}

fn hungman(i : i8, s : &str) {
    match i 
    {
        6 => {
            println!(
                r#"
        ============================================



                ^
        ============================================
        "#
            );
        }
        5 => {
        println!(
                r#"
        ============================================


                |
                ^
        ============================================
        "#
            );
    }
    4 => {
        println!(
                r#"
        ============================================

                |
                |
                ^
        ============================================
        "#
            );
    }
    3 => {
        println!(
            r#"
        ============================================
                ____
                |
                |
                ^
        ============================================
        "#
            );
    }
    2 => {
        println!(
            r#"
        ============================================
                ____
                |   O
                |
                ^
        ============================================
        "#
            );
    }
    1 => {
        println!(
            r#"
        ============================================
                ____
                |   O
                |  /|\
                ^   
        ============================================
        "#
            );
    }
    0 => {
        println!(
            r#"
        ============================================
                ____
                |   O
                |  /|\
                ^  / \
        ============================================
        "#
            );
        println!("You lost the game!");
        println!("The word was: {:?}", s);
        println!("\n");
        std::process::exit(1);
        }
    _ => {
        println!( "Something went wrong, please try again!"  );
        }
    }
}

#[derive(Deserialize)]
struct ApiResponse {
    data: WordsPage,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct WordsPage {
    words: Vec<String>,
    total_pages: u32,
}

fn fetch_words_page(word_length: u32, page: u32) -> Result<WordsPage, reqwest::Error> {
    let url = format!("https://api.poocoo.pl/api/v1/words?length={word_length}&page={page}");
    let response: ApiResponse = reqwest::blocking::get(&url)?.json()?;
    Ok(response.data)
}

fn get_random_word_from_rest_api(word_length: u32) -> Result<String, reqwest::Error> {
    let total_pages = fetch_words_page(word_length, 1)?.total_pages;
    println!("totalPages = {total_pages}");

    let page = fetch_words_page(word_length, rand::random_range(1..=total_pages))?;
    let index = rand::random_range(0..page.words.len());
    Ok(page.words[index].clone())
}

fn main() {
    let word_length = rand::random_range(3..=10);
    println!("Random search for a word in a dictionary");
    //let word = random_word::get_len(word_length, Lang::En);
    let word = get_random_word_from_rest_api(word_length).expect("failed to fetch word");
    //println!("Found word: {:?}", word);
    guess_word(&word);
}


This is a publicly available REST API you can use for your projects and tests. Be careful about the number of calls you send, as sometimes it can return an HTTP 429 Error, this means you exceeded the rate limit. Don't overdo it; they accept 120 requests per minute per IP address.