prng

package
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 5 Imported by: 0

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

View Source
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.

View Source
const (
	KindPCG   uint8 = 1
	KindFrotz uint8 = 2
)

The generator kinds recorded in a saved state.

Variables

View Source
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) Draw

func (f *Frotz) Draw(n uint16) uint16

Draw reproduces the generating half of Frotz's z_random.

func (*Frotz) Kind

func (f *Frotz) Kind() uint8

Kind implements Source.

func (*Frotz) MarshalState

func (f *Frotz) MarshalState() ([]byte, error)

MarshalState implements Source.

func (*Frotz) Reseed

func (f *Frotz) Reseed() error

Reseed implements Source.

func (*Frotz) SeedWith

func (f *Frotz) SeedWith(seed uint64)

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

func (f *Frotz) UnmarshalState(data []byte) error

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) Draw

func (p *PCG) Draw(n uint16) uint16

Draw implements Source.

func (*PCG) Kind

func (p *PCG) Kind() uint8

Kind implements Source.

func (*PCG) MarshalState

func (p *PCG) MarshalState() ([]byte, error)

MarshalState implements Source.

func (*PCG) Reseed

func (p *PCG) Reseed() error

Reseed implements Source.

func (*PCG) SeedWith

func (p *PCG) SeedWith(seed uint64)

SeedWith implements Source.

func (*PCG) UnmarshalState

func (p *PCG) UnmarshalState(data []byte) error

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.

func New

func New(kind uint8) (Source, error)

New builds a generator of the given kind in its zero state. The caller seeds it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL