Documentation
¶
Overview ¶
Package prng provides the random number generators a Z-machine may own.
Section 2.4 of the Z-Machine Standards Document requires two states rather than one algorithm: a "random" state, whose sequence the story cannot predict, and a "predictable" state, which a given seed always reproduces. It says nothing about which numbers either state produces, so two conforming interpreters given the same seed will disagree.
That freedom is why a Source is an interface. PCG is the engine's own generator and is what every ordinary machine uses. Comparing the engine's behaviour against another interpreter's, though, means drawing the numbers that interpreter would draw, which no general-purpose generator does by accident; see Frotz.
A Source is owned by one machine. Nothing here is held in a package-level variable, and no generator is shared between machines.
Index ¶
Constants ¶
const ( // FrotzIntervalSeed is the seed below which Frotz's seed_random counts // instead of generating. FrotzIntervalSeed = 1000 // FrotzStateSize is the marshalled size of a Frotz: A, then the interval // and counter of its predictable mode. FrotzStateSize = 8 )
The constants of Frotz's generator.
const ( KindPCG uint8 = 1 KindFrotz uint8 = 2 )
The generator kinds recorded in a saved state.
Variables ¶
var ErrInvalidState = errors.New("invalid random generator state")
ErrInvalidState classifies a generator state that cannot be read. Saved states are untrusted, so a state that means nothing is refused rather than accepted as some other sequence.
The engine wraps this in its own ErrInvalidState, so a caller classifying a failed restore finds it there.
Functions ¶
This section is empty.
Types ¶
type Frotz ¶
type Frotz struct {
// contains filtered or unexported fields
}
Frotz reproduces the generator in Frotz's src/common/random.c, so that this engine can be compared against dfrotz turn for turn.
It exists for differential testing and for nothing else. It is not a better generator than PCG and should not be used to run a real session; the reason to have it is that a text comparison against another interpreter is worthless the moment the two draw different numbers, and section 2.4 guarantees they will.
Frotz draws like this:
A = 0x015a4e35L * A + 1; result = (A >> 16) & 0x7fff; store(result % range + 1);
A is a C long, so it wraps at whatever width the host uses, but the result is unaffected: the low bits of a linear congruential generator depend only on the low bits of its previous state, so bits 16 to 30 of A evolve the same way whether A is held in 32 bits, 64 bits, or the 31 kept here.
func (*Frotz) MarshalState ¶
MarshalState implements Source.
func (*Frotz) SeedWith ¶
SeedWith reproduces Frotz's seed_random.
Frotz splits the seed three ways, and the split is why a differential test must choose its seeds with care:
value == 0 take a seed from the host's entropy value < 1000 count from 0 to value-1 forever, generating nothing otherwise begin the generator at A = value
So dfrotz -s 42 does not run the generator at all, while dfrotz -s 1042 does. A comparison against dfrotz wants a seed of at least 1000.
func (*Frotz) UnmarshalState ¶
UnmarshalState implements Source.
type PCG ¶
type PCG struct {
// contains filtered or unexported fields
}
PCG is the engine's own generator: the PCG of math/rand/v2. It is what every ordinary machine uses.
func (*PCG) MarshalState ¶
MarshalState implements Source.
func (*PCG) UnmarshalState ¶
UnmarshalState implements Source.
type Source ¶
type Source interface {
// Draw returns a value between 1 and n inclusive. n is always positive:
// section 15 gives the other cases their own meaning, and the random
// opcode handles them before reaching here.
Draw(n uint16) uint16
// SeedWith puts the generator into the predictable state of section 2.4.2.
// The same seed must always produce the same sequence.
SeedWith(seed uint64)
// Reseed puts the generator into the unpredictable state of section 2.4.
Reseed() error
// MarshalState returns the generator's state, and UnmarshalState restores
// one, so that a generator can cross a request boundary. UnmarshalState
// rejects states it does not recognise with an error wrapping
// ErrInvalidState.
MarshalState() ([]byte, error)
UnmarshalState(data []byte) error
// Kind identifies the generator in a saved state, so that restoring picks
// the generator the state was written by rather than misreading it.
Kind() uint8
}
Source is a random number generator a machine can own.