OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_pcg.gd

58 lines · gd ref:
extends Node
## GATE — DETERMINISTIC RANDOMNESS (#120 F4, docs/node-tree-and-stacks.md §5).
## Pcg32 must give the SAME numbers as O'Neill's pcg-c-basic on every peer. The golden
## values come from an independent reference (Python big ints, pcg32_random_r line for
## line); (42, 54) is also pcg32-demo's published first round.
##   R1  seed (42, 54): the six published outputs
##   R2  seed (0, 0) and a seed using all 64 bits of state and sequence
##   R3  bounded(6): pcg32_boundedrand_r's values (no modulo bias)
##   R4  from_parts (SHA-256 of "dot|stage|seed"): the reference's outputs
##   R5  next_float is u32 / 2^32
##   R6  two streams from the same parts agree; a changed part gives another stream
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_pcg.tscn

var fails := 0
var checks := 0


func _ok(what: String, cond: bool, note: Variant = "") -> void:
	checks += 1
	if not cond:
		fails += 1
	print("  %-64s %s  %s" % [what, "OK" if cond else "**FAIL**", str(note)])


func _take(r: Pcg32, n: int) -> Array:
	var out: Array = []
	for i in n:
		out.append(r.next_u32())
	return out


func _ready() -> void:
	var a := _take(Pcg32.seeded(42, 54), 6)
	_ok("R1 seed (42, 54) = pcg32-demo", a == [0xa15c02b7, 0x7b47f409, 0xba1d3330, 0x83d2f293, 0xbfa4784b, 0xcbed606e], a)
	var z := _take(Pcg32.seeded(0, 0), 4)
	_ok("R2 seed (0, 0)", z == [3837872008, 932996374, 1548399547, 1612522464], z)
	# 0xdeadbeefcafef00d / 0x0123456789abcdef, as halves (the first is above 2^63)
	var big := _take(Pcg32.new(0xdeadbeef, 0xcafef00d, 0x01234567, 0x89abcdef), 4)
	_ok("R2 a full 64-bit state and sequence", big == [2126666864, 2713001681, 3353343622, 300314576], big)
	var r := Pcg32.seeded(42, 54)
	var b: Array = []
	for i in 12:
		b.append(r.bounded(6))
	_ok("R3 bounded(6)", b == [3, 3, 2, 1, 1, 4, 5, 3, 0, 2, 0, 1], b)
	var p := _take(Pcg32.from_parts(["gate-author:17", 2, 7]), 4)
	_ok("R4 from_parts(dot, stage, seed)", p == [497601254, 2144033327, 158901680, 1270403377], p)
	var f := Pcg32.seeded(42, 54)
	var f0 := f.next_float()
	var f1 := f.next_float()
	_ok("R5 next_float", is_equal_approx(f0, 0.6303102204110473) and is_equal_approx(f1, 0.4815666696522385), [f0, f1])
	_ok("R6 the same parts give the same stream",
		_take(Pcg32.from_parts(["d", 1, 0]), 8) == _take(Pcg32.from_parts(["d", 1, 0]), 8))
	_ok("R6 another stage gives another stream",
		_take(Pcg32.from_parts(["d", 1, 0]), 8) != _take(Pcg32.from_parts(["d", 2, 0]), 8))
	print("\nGATE pcg: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
	get_tree().quit(0 if fails == 0 else 1)