ā¦while it is certainly enough to execute without error, please note, that it is semantically different from my original formulation, as it applies two masks and not just the one, being passed to next
, which I donāt want.
Actually, I would go event further and write everything as follows, to avoid needing to write overly specific typesā¦
MaskedRNG(seed::Integer, nbits) = MaskedRNG(RNG(seed), ~UInt64(0) >> ifelse(0 < abs(nbits) <= 64, sign(nbits)*64 - nbits, 64))
ā¦I should probably give some context, at this point, of how I am actually using these rngs in my chess-engine, at all. I have datastructures like this, a lotā¦
mutable struct TTEntry
d0::UInt64 # use this, for now (1.)
d1::UInt16 # use this, for now (10 bytes, total)
# later...
# ( 4) checkkey :: Uint32
# ( 6) bestmove :: UInt16
# ( 8) score :: Int16
# (10) details / flags :: UInt16
# score-type : 1..2 {LOWER,EXACT,UPPER}={CUT,PV,ALL}
# age : ... (replacement-policy)
# quality : ... {#nodes used for score}
# ...
# (10) Bytes, total
end
ā¦it might look a bit cryptic, but it is just a simple declaration of the structure of a single hashtable-entry, of what is called a transposition-table in chess. It needs to pack ādataā as compactly as possible, as the hashtable will be rather large (typically some singleā¦double-digit - percentage of total ram available on a computer, so on my laptop with 16GB ram, total, something in the range of 512MBā¦8GB, eventually), such that memory-layout of single entries becomes quite an issue.
As (semantic units of) data is packed into a few bits, only (a flag might even be packed into a single bit), I will (eventually) have several constant masks defined, for being able to have functions, accessing data according to this simple patternā¦
function age(TTEntry::entry)
return (entry.someData & MASK_TTENTRY_AGE) >> SHIFT_TTENTRY_AGE
end
function quality(TTEntry::entry)
return (entry.someData & MASK_TTENTRY_QUALITY) >> SHIFT_TTENTRY_QUALITY
end
So when using the rng, I only ever want to use the lowest n bits (for adressing indexed data, which is arranged in hashtable-structures of some size=2^k
) [use-case #1], for example when performance-testing one of the hashtables. Or I want to conveniently be able to randomly set (possibly a bunch of) specific flags / detailed data, packed in some (unsigned integer-) primitive type similar to this: primitiveType ā»= next(random, MASK_AGE | MASK_QUALITY | MASK_OBSCUREFLAG)
[use-case #2]. Reason for even coding my own, is (a) to have the ability to test against a reference-implementation, in java, with the same random-data-stream and (b) having a very specialized rng, optimized for performance-testing, within my chess-engine.
For any other use-case, Iād likely just use one of the existing rngs. So Iām perfectly happy, having the rng be specific to my use-cases, only, while maintaining simplicity.
While not adding to any body of libraries, restricting ones codes to meet specific requirements, often yields remarkable conciseness and quality-of-code / patterns.