001package squidpony.squidmath; 002 003import java.io.Serializable; 004 005/** 006 * A slight variant on RNG that always uses a stateful RandomessSource and so can have its state 007 * set or retrieved using setState() or getState(). 008 * Created by Tommy Ettinger on 9/15/2015. 009 * @author Tommy Ettinger 010 */ 011public class StatefulRNG extends RNG implements Serializable, IStatefulRNG { 012 013 private static final long serialVersionUID = -2456306898212937163L; 014 015 public StatefulRNG() { 016 super(); 017 } 018 019 public StatefulRNG(RandomnessSource random) { 020 super((random instanceof StatefulRandomness) ? random : new DiverRNG(random.nextLong())); 021 } 022 023 /** 024 * Seeded constructor uses DiverRNG, which is of high quality, but low period (which rarely matters for games), 025 * and has good speed and tiny state size. 026 */ 027 public StatefulRNG(long seed) { 028 this(new DiverRNG(seed)); 029 } 030 /** 031 * String-seeded constructor uses the hash of the String as a seed for DiverRNG, which is of high quality, but 032 * low period (which rarely matters for games), and has good speed and tiny state size. 033 * 034 * Note: This constructor changed behavior on April 22, 2017, again on December 23, 2017, and again on June 14, 035 * 2018. The first was when it was noticed that it was not seeding very effectively (only assigning to 32 bits of 036 * seed instead of all 64). The older behavior isn't fully preserved, since it used a rather low-quality String 037 * hashing algorithm and so probably had problems producing good starting seeds, but you can get close by replacing 038 * {@code new StatefulRNG(text)} with {@code new StatefulRNG(new LightRNG(CrossHash.hash(text)))}. The new technique 039 * assigns to all 64 bits and has less correlation between similar inputs causing similar starting states. It's also 040 * faster, but that shouldn't matter in a constructor. It uses a better hashing algorithm because CrossHash no 041 * longer has the older, worse one. The latest change in June switched to DiverRNG instead of LightRNG. 042 */ 043 public StatefulRNG(CharSequence seedString) { 044 this(new DiverRNG(CrossHash.hash64(seedString))); 045 } 046 047 @Override 048 public void setRandomness(RandomnessSource random) { 049 super.setRandomness(random == null ? new DiverRNG() : 050 (random instanceof StatefulRandomness) ? random : new DiverRNG(random.nextLong())); 051 } 052 053 /** 054 * Creates a copy of this StatefulRNG; it will generate the same random numbers, given the same calls in order, as 055 * this StatefulRNG at the point copy() is called. The copy will not share references with this StatefulRNG. 056 * 057 * @return a copy of this StatefulRNG 058 */ 059 @Override 060 public StatefulRNG copy() { 061 return new StatefulRNG(random.copy()); 062 } 063 064 /** 065 * Get a long that can be used to reproduce the sequence of random numbers this object will generate starting now. 066 * @return a long that can be used as state. 067 */ 068 public long getState() 069 { 070 return ((StatefulRandomness)random).getState(); 071 } 072 073 /** 074 * Sets the state of the random number generator to a given long, which will alter future random numbers this 075 * produces based on the state. 076 * @param state a long, which typically should not be 0 (some implementations may tolerate a state of 0, however). 077 */ 078 public void setState(long state) 079 { 080 ((StatefulRandomness)random).setState(state); 081 } 082 083 @Override 084 public String toString() { 085 return "StatefulRNG{" + Long.toHexString(((StatefulRandomness)random).getState()) + "}"; 086 } 087 /** 088 * Returns this StatefulRNG in a way that can be deserialized even if only {@link IRNG}'s methods can be called. 089 * @return a {@link Serializable} view of this StatefulRNG; always {@code this} 090 */ 091 @Override 092 public Serializable toSerializable() { 093 return this; 094 } 095 096}