Skip to content

Better RNG #57

@Lonami

Description

@Lonami

https://harddrop.com/wiki/Random_Generator is a useful link.

Quoting @bb010g:

basically, you don't normally want an algorithm that's friendly in certain ways, you want randomness that's less prone to dick the player over and that advanced players can work with at a higher level. Tetris handles this by allocating a "bag" of each piece type when new tetrominoes are needed, shuffling that, and then passing out from there.

Bags let you do things like avoid really long runs of certain pieces and adjust rarity without being complex or too deterministic.

alternatively, Akira's The Grand Master traditionally handles this by keeping a history of the last N pieces, and then generating a new piece up to M times until a unique piece appears or the limit is reached. https://harddrop.com/wiki/TGM_randomizer

huh, Terror Instinct does a thing where it keeps a 35 piece bag for that generation that starts with 5 of each piece, and when a piece is genned (pulled) from it a piece of the least recent type is put in its place, which keeps pieces from not showing up for a long while.

https://tetrisconcept.net/threads/randomizer-theory.512/page-9#post-55478

# PETE ROOLZ
#
# you can get a quick sequence out with
# python -c 'import tgm; r = tgm.TiRandomizer(); print "".join([r.next() for i in range(70)])'

import random
import collections

# On a Ti randomizer bug:
#
# > When these 3 conditions are met:
# >
# >     1. The randomizer has just chosen the most droughted piece
# >     2. A reroll happened during the choice of that piece.
# >     3. Excluding the first piece of the game, and including this chosen piece, you have received at least one of each of the 7 piece types.
# >
# > Then the roll that chose that most droughted piece will not update the bag of 35 pieces.
#
# -- colour_thief, http://tetrisconcept.net/threads/randomizer-theory.512/page-7

def TiRandomizer():
    bag = ['j', 'i', 'z', 'l', 'o', 't', 's'] * 5
    history = collections.deque(['s', 'z', 's', 'z'])
    drought_order = ['j', 'i', 'z', 'l', 'o', 't', 's']
    count = { 'j' : 0, 'i' : 0, 'z' : 0, 'l' : 0, 'o' : 0, 't' : 0, 's' : 0 }
    n = 1

    # first piece is special
    first = random.choice(['j','i','l','t'])
    history.popleft()
    history.append(first)
    yield first

    while True:
        for roll in range(6):
            i = random.randint(0, 34)
            piece = bag[i]
            if piece not in history:
                break
            if roll < 5:
                bag[i] = drought_order[0]
        count[piece] += 1
        emulate_bug = all ([
            piece == drought_order[0],
            roll > 0,
            0 not in count.values()
            ])
        if not emulate_bug:
            bag[i] = drought_order[0]
        drought_order.remove(piece)
        drought_order.append(piece)
        history.popleft()
        history.append(piece)
        yield piece

if __name__ == '__main__':
    r = TiRandomizer()
    print "".join([r.next() for i in range(70)])

anyways, point is that you can keep randomness in a lot of ways while not just calling rand and being done

I should probably just PR something for this

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions