|
| 1 | +# Piece RNG |
| 2 | + |
| 3 | +Though TETR.IO has five different types of [randomizer](Room_Config.md#Bag-type), all of them are based upon the same pseudorandom number generator. This RNG is the [Lehmer random number generator](https://en.wikipedia.org/wiki/Lehmer_random_number_generator), specifically MINSTD (`a = 16807` and `m = 2147483647`). The `shuffleArray` function uses the [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle). |
| 4 | + |
| 5 | +A JavaScript reference implementation closely matching that found in [tetrio.js](https://tetr.io/js/tetrio.js) follows: |
| 6 | + |
| 7 | +```js |
| 8 | +function rng(seed) { |
| 9 | + let t = seed % 2147483647; |
| 10 | + |
| 11 | + if (t <= 0) { |
| 12 | + t += 2147483646; |
| 13 | + } |
| 14 | + |
| 15 | + return { |
| 16 | + next: function () { |
| 17 | + return t = 16807 * t % 2147483647; |
| 18 | + }, |
| 19 | + nextFloat: function () { |
| 20 | + return (this.next() - 1) / 2147483646; |
| 21 | + }, |
| 22 | + shuffleArray: function (array) { |
| 23 | + if (array.length == 0) { |
| 24 | + return array; |
| 25 | + } |
| 26 | + |
| 27 | + for (let i = array.length - 1; i != 0; i--) { |
| 28 | + console.log(i) |
| 29 | + const r = Math.floor(this.nextFloat() * (i + 1)); |
| 30 | + [array[i], array[r]] = [array[r], array[i]]; |
| 31 | + } |
| 32 | + |
| 33 | + return array; |
| 34 | + } |
| 35 | + } |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +In singleplayer, replays with `no_szo` set to true (currently accessible via stride mode) will skip the first piece if it is any of S, Z, or O. |
| 40 | + |
| 41 | +# `"7-bag"` |
| 42 | + |
| 43 | +The 7-bag randomization system generates a set of the seven tetrominos and shuffles them before putting them into the queue. This guarantees that there will never be a gap of more than 12 tetrominos between any two tetrominos of the same type. As with any shuffling algorithm, the order of the input array matters. TETR.IO uses the ordering `ZLOSIJT` (a rainbow, how cute!) for all purposes. |
| 44 | + |
| 45 | +```js |
| 46 | +queue.push(...rng.shuffleArray(["Z", "L", "O", "S", "I", "J", "T"])); |
| 47 | +``` |
| 48 | + |
| 49 | +# `"14-bag"` |
| 50 | + |
| 51 | +The 14-bag randomization system is a variant of the 7-bag system with less of a guarantee against droughts. The input ordering is `ZLOSIJTZLOSIJT`. |
| 52 | + |
| 53 | +```js |
| 54 | +queue.push(...rng.shuffleArray(["Z", "L", "O", "S", "I", "J", "T", "Z", "L", "O", "S", "I", "J", "T"])); |
| 55 | +``` |
| 56 | + |
| 57 | +# `"classic"` |
| 58 | + |
| 59 | +This is the randomizer used in *Tetris* for the NES. For each piece, the randomizer rolls an eight-sided die, with seven sides representing seven tetrominos and the eighth side representing a reroll. If a reroll or the same tetromino that was previously generated is rolled, then the randomizer selects randomly from the tetrominos. |
| 60 | + |
| 61 | +```js |
| 62 | +const TETROMINOS = ["Z", "L", "O", "S", "I", "J", "T"]; |
| 63 | + |
| 64 | +let index = Math.floor(rng.nextFloat() * (TETROMINOS.length + 1)); |
| 65 | + |
| 66 | +if (index === lastGenerated || index >= TETROMINOS.length) { |
| 67 | + index = Math.floor(rng.nextFloat() * TETROMINOS.length); |
| 68 | +} |
| 69 | + |
| 70 | +lastGenerated = index; |
| 71 | +queue.push(TETROMINOS[index]); |
| 72 | +``` |
| 73 | + |
| 74 | +# `"pairs"` |
| 75 | + |
| 76 | +This randomizer picks pairs of tetromino types and gives three of each in a random order. |
| 77 | + |
| 78 | +```js |
| 79 | +const s = rng.shuffleArray(["Z", "L", "O", "S", "I", "J", "T"]), |
| 80 | +const pairs = [s[0], s[0], s[0], s[1], s[1], s[1]]; |
| 81 | +rng.shuffleArray(pairs); |
| 82 | + |
| 83 | +queue.push(...pairs) |
| 84 | +``` |
| 85 | + |
| 86 | +# `"total mayhem"` |
| 87 | + |
| 88 | +This randomizer is entirely random. <!-- xD --> |
| 89 | + |
| 90 | +```js |
| 91 | +const TETROMINOS = ["Z", "L", "O", "S", "I", "J", "T"]; |
| 92 | +queue.push(TETROMINOS[Math.floor(rng.nextFloat() * TETROMINOS.length)]); |
| 93 | +``` |
0 commit comments