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}