I made a guess game in Rust :D
from kionite231@lemmy.ca to rust@programming.dev on 24 Dec 2024 12:29
https://lemmy.ca/post/35627397

hello,

last time I made a fibonacci series generator in Rust and now I have made something different :)

use std::io;

fn main() {
    let mut input: String = String::new();
    let stdin = io::stdin();

    let x = rand::random::<u32>() % 101;
    let mut attempts = 0;

    loop {
        println!("Guess a number from 0 to 100:");
        stdin.read_line(&mut input);
        input = input.to_string().replace("\n", ""); // removing the \n
        let user_input: u32 = input.parse::<u32>().unwrap();
        if x == user_input {
            println!("You won! attempts: {attempts}");
            break;
        }
        else if x < user_input {
            println!("too big");
            attempts += 1;
        }
        else {
            println!("too small");
            attempts += 1;
        }
        input.clear()
    }
}

feel free to give me suggestion :)

#rust

threaded - newest

Xanatos@mastodon.gamedev.place on 24 Dec 2024 13:13 next collapse

@kionite231
You could save the inputs done by the user and ask if the guesses should be listed after you did enter the correct number.

kionite231@lemmy.ca on 24 Dec 2024 13:27 collapse

done :D

use std::io;

fn main() {
    let mut input: String = String::new();
    let stdin = io::stdin();

    let x = rand::random::<u32>() % 101;
    let mut attempts = 0;

    let mut user_inputs: Vec<u32> = Vec::new();
    loop {
        println!("Guess a number from 0 to 100:");
        stdin.read_line(&mut input);
        input = input.to_string().replace("\n", ""); // removing the \n
        let user_input: u32 = input.parse::<u32>().unwrap();
	user_inputs.push(user_input);
        if x == user_input {
            println!("You won! attempts: {attempts}");
	    println!("Your inputs:");
	    for input in user_inputs {
		print!("{input} ");
	    }
	    println!("");
            break;
        }
        else if x < user_input {
            println!("too big");
            attempts += 1;
        }
        else {
            println!("too small");
            attempts += 1;
        }
        input.clear()
    }
}

Xanatos@mastodon.gamedev.place on 24 Dec 2024 13:36 collapse

@kionite231
You could also color the inputs in different colors

Maybe red if it was wrong and green if the guess was right.

You could also keep that color coding if the list of inputs is getting shown at the end.

kionite231@lemmy.ca on 24 Dec 2024 13:43 collapse

but to do that I have to use external deps? which I am not comfortable doing. (I am newbie :) )

nous@programming.dev on 24 Dec 2024 14:19 next collapse

Coloured text does not require a dep. It is just about printing the right colour codes like:

const BLACK: &'static str = "\u{001b}[30m";
const RED: &'static str = "\u{001b}[31m";
const GREEN: &'static str = "\u{001b}[32m";
const YELLOW: &'static str = "\u{001b}[33m";
const BLUE: &'static str = "\u{001b}[34m";
const MAGENTA: &'static str = "\u{001b}[35m";
const CYAN: &'static str = "\u{001b}[36m";
const WHITE: &'static str = "\u{001b}[37m";
const CLEAR: &'static str = "\u{001b}[0m";

fn main() {
    println!("{RED}red! {BLUE}blue!{CLEAR}");
}

The libraries just make it easier so you don’t need to remember or know what those codes mean.

sukhmel@programming.dev on 25 Dec 2024 09:56 collapse

I thought, colour codes are platform dependent, will it work on windows

I usually run things on Linux or macOS, but using a library (crate) may add portability, imo

nous@programming.dev on 25 Dec 2024 16:22 collapse

🤔 I think the vt100 protocols (where the escape code come from) predate windows and I think all modern terminals still use those as the base. So I think they are cross platform. From a quick search it looks like they are the same codes on windows. So I dont think the libraries are doing anything special for cross platform support.

sukhmel@programming.dev on 25 Dec 2024 22:11 collapse

I see, so I was wrong then

Maybe I should try colour codes on windows when I get to it 😅 thanks for the info

pemptago@lemmy.ml on 24 Dec 2024 23:04 collapse

Nice work and congrats on your progress! Being new and uncomfortable with dependencies, this project might be a good opportunity to read and apply chapter 7 of the rust book if you haven’t already. Specifically 7.2 defining modules … and the sections that follow.

As your projects increase complexity it’s really useful to shift chunks around (within main.rs and into other files/directories). Doing it now will make it easier to see the individual parts (vs waiting till a project has a lot going on). It might make dependencies feel less unfamiliar.

FizzyOrange@programming.dev on 24 Dec 2024 13:33 next collapse

Some suggestions:

  1. Declare input in the loop. That limits scope which is basically always a good idea.
  2. Trim the newline like
let input = input.trim();

This is better in several ways:

  1. Simpler
  2. Doesn’t allocate a new string.
  3. Removes all whitespace at the start and end.
  4. It makes input immutable after that line.

Also attempts is never read - you should have got a compiler warning about that.

Otherwise, good work.

kionite231@lemmy.ca on 24 Dec 2024 13:41 next collapse

Thank you for the suggestions.

attempts is used to display total attempts when user wins the game so it is used in the program, I don’t see any warnings from compiler.

FizzyOrange@programming.dev on 24 Dec 2024 14:35 collapse

Ah yeah you’re right - I missed that.

nous@programming.dev on 24 Dec 2024 13:55 collapse

Declare input in the loop. That limits scope which is basically always a good idea.

There is good reason to not do this. Though in this case it won’t make much difference as performance is not an issue. But by having it outside the loop it allows the allocation of the string to be reused and in more complex programs is typically what you would want to do - allocate once outside the loop and reuse that inside.

In this case waiting on user input is going to outweigh any performance benefit I would not call it out as bad practice either.

FizzyOrange@programming.dev on 24 Dec 2024 14:35 collapse

Yes this is true, but it’s a little advanced for this level so I didn’t mention it. Probably better for beginners to focus on good code style than optimal performance.

nous@programming.dev on 24 Dec 2024 13:43 next collapse

The biggest/only real problem is the .unwrap(). This will cause the program to exit with an error message if the user inputs an invalid value. Which is not a great user experience. Better to reject the input and ask them again (aka start the loop again). You can do this with a match on the returned value of parse with a message and a continue on Err (good time to learn about enums and pattern matching as well).

A minor improvement can be done to this:

        input = input.to_string().replace("\n", ""); // removing the \n
        let user_input: u32 = input.parse::<u32>().unwrap();

By replacing it with:

        let user_input: u32 = input.trim().parse::<u32>().unwrap();

Which has a few advantages:

  • it removes all white space, including tabs and spaces from both the start and end.
  • it requires no allocations.
  • it is one fewer lines while still being clear what it is doing.
  • no mutability needed (though input still needs to be mutable here in other cases this can be a benefit).

These are more nitpicks than anything substantial - would lead to nicer code in more complex situations but negligible at best in this code base (only really pointing out to give you some things to think about):

Since you break in the win condition the attempts += 1; can be moved out of the other branches at the same level as the input.clear().

Rand has a gen_range function which can be used like:

rand::thread_rng().gen_range::<u32>(0..=100);

Though typically you would save the thread_rng() output to a local and reuse that throughout your program instead of creating one each time - here you only need one number so that matters less. The Rng trait has a lot more convenience function on it as well so it is generally worth using over just the random() function. Though in this case it makes very little difference overall here - typically you would want to opt for one of those methods instead so is worth knowing about.

I dislike the name x as it is very ambiguous. IMO short letters should be used only in smaller scopes or when they represent some sort of mathematical value. Here I would use secret_number or secret_value or something more descriptive. Though in this case there is not a much better domain name here, more often there is.

kionite231@lemmy.ca on 24 Dec 2024 13:57 collapse

Thank you so much for the suggestion. I have updated the code locally :)

SWW13@lemmy.brief.guru on 26 Dec 2024 09:02 collapse

You shouldn’t use modulo to get a random number in a specific range (solution already in another comment). Reason is that numbers below 64 will be twice as likely as number 64-101 (in your example) due to how binary numbers and modulo works.

This is obviously no real issue for this game, just keep in mind that you shouldn’t implement something like that (random ranges) yourself, this is especially true for crypto related things.

kionite231@lemmy.ca on 26 Dec 2024 10:45 collapse

Thanks, I will keep that in mind.