taylorconor / tinytetris

80x23 terminal tetris!
Apache License 2.0
2.93k stars 580 forks source link

Request: modern Tetris randomization logic #4

Open JobLeonard opened 5 years ago

JobLeonard commented 5 years ago

Currently any piece can follow any piece. In modern Tetris implementations we instead get pieces in shuffled sequences, where each sequence contains every tetronimo piece exactly once. See also:

https://tetris.fandom.com/wiki/Random_Generator

Would make the game a lot more "fair" to play :)

sneakernets commented 5 years ago

Here's what I did for my terrible Commodore version: I created a "bag" and put each piece in twice, then I made sure you didn't get the same piece twice. It ended up being a good chunk of the program, though.

It's probably not the best way to do it but it sure kept the program from generating a deluge of S and Z pieces.

JobLeonard commented 5 years ago

Why two though? Wouldn't the simplest way be to have a sequence of seven and shuffle that sequence every seven steps?

Currently, the logic for a new piece is:

// create a new piece, don't remove old one (it has landed and should stick)
void new_piece() {
  y = py = 0;
  p = rand() % 7;
  r = pr = rand() % 4;
  x = px = rand() % (10 - NUM(r, 16));
}

The key line being p = rand() % 7;

If I'm correct, something like this should work: (just wrote this of the top of my head, haven't really written C++ in a while so probably contains a mistake somewhere)


// index into bag sequence and bag of seven tetronimos
int n = 0, bag[7] = {0, 1, 2, 3, 4, 5, 6}

void new_piece() {
  y = py = 0;
  // Fisher-Yates shuffle the bag every time
  // one random sequence is finished
  if (n == 0) { 
    for(int i = 1; i < 7; i++) {
      int j = rand() % i;
      p = bag[j];
      bag[j] = bag[i];
      bag[i] = p;
    }
  }
  p = bag[n];
  n = (n + 1) % 7;
  r = pr = rand() % 4;
  x = px = rand() % (10 - NUM(r, 16));
}