How to read stdin without stopping a loop?

Hello everyone,

Still learning the language, diving into the small termion ecosystem, I try to implement my own version of the famous Snake game. So far I managed to:

  • Draw the box
  • Draw the snake
  • Get the snake moving and following my direction

I’m already very happy about my progress. I’ll explain to you where I get stuck. The function that changes the snake’s direction looks like this and is called in a loop:

pub fn change_direction(&mut self) {
    // read a single byte from stdin
    let mut b = [0];
    self.stdin.read(&mut b).unwrap();

    match b[0] {
        b'i' => self.direction = Direction::Up,
        b'j' => self.direction = Direction::Left,
        b'l' => self.direction = Direction::Right,
        b'k' => self.direction = Direction::Down,
        b'q' => panic!("C'est la panique !"),
        _ => {},
    }
    self.stdout.flush().unwrap();
}

It works perfectly, except for this : the snake waits for my command to move! That kind of defeats the whole purpose of the game, does it?

I’m searching for a way to let the program listen to what I type whithout it stopping.

The snake example has this function, which is slightly different:

fn update(&mut self) -> bool {
    let mut key_bytes = [0];
    self.stdin.read(&mut key_bytes).unwrap();

    self.rand.write_u8(key_bytes[0]); // this line is of interest to me

    match key_bytes[0] {
        b'q' => return false,
        b'k' | b'w' => self.turn_snake(Direction::Up),
        b'j' | b's' => self.turn_snake(Direction::Down),
        b'h' | b'a' => self.turn_snake(Direction::Left),
        b'l' | b'd' => self.turn_snake(Direction::Right),
        _ => {},
    }
    self.move_snake();
    true
}

(The returned boolean doesn’t seem to have anything to do with the direction change).
That function is pretty similar to mine, except for the

self.rand.write_u8(key_bytes[0]);

line. It uses extra::rand::Randomizer, which I had trouble intregating into my project (I don’t even manage to compile the game repo).
Mostly I want to understand what it changes to write a random byte into this small variable [0].

Is there another way to react to the user input only when the user types something?

Thanks in advance.

The difference is that the example’s stdin is termion::async_stdin, which I assume yours isn’t (it’s probably std::io::stdin, which blocks).

The self.rand.write_u8 is injecting entropy into the randomizer based on user input. If you use rand::thread_rng, you don’t have to do this; it’s a peculiarity of the specific RNG that was being used that this entropy injection was used.

For that specific randomizer, read_u8 is how you get a random byte out

1 Like

Thanks, I changed my stdin to termion::async_stdin and it works perfectly, my snake wanders about all by itself!

Are you telling me this randomizer here takes entropy from user input ? What would it need to do that ? Oh, I’ll soon find out, I’ve got to implement the random food apparition now :slight_smile: