001package squidpony.squidmath;
002
003import java.io.Serializable;
004
005/**
006 * A subclass of StatefulRNG (and thus RNG) that allows customizing many parts of the random number generation.
007 * This is meant to be a more comprehensible version of the functionality present in RandomBias, and also for it to be
008 * easier to use with methods that expect an RNG.
009 * <br>
010 * You can change the expected average for the values this produces, which uses the RandomBias.EXPONENTIAL distribution,
011 * with all the caveats it has: it strongly favors either high or low values when the average gets especially high or
012 * low, but it can essentially cover all averages between 0.0 and 1.0 (this class limits it to 0.1 and 0.9, so other
013 * techniques can be used effectively).
014 * <br>
015 * You can also affect the "centrality" of random numbers, causing more to occur near the expected average (a bell curve
016 * effect), or cause more near extreme ends of the random number spectrum. In practice, centrality changes are hard to
017 * notice, but may be useful to simulate certain effects. An example of centrality changes in existing games include the
018 * Nintendo title Advance Wars 2, where a brutish commander could increase the amount of damage his units dealt but also
019 * suffered unpredictability; attacks could deal even more or much less damage than normal without any way to build
020 * tactics around it. Square Enix's Final Fantasy XII also notably differentiated certain weapons (axes, hammers, and
021 * "hand-cannons") from other similar options by making them deal less predictable damage. In both cases the connotation
022 * is that more randomness is fitting for a brute-force approach to combat where pre-planned strategies are less
023 * emphasized. It should also be noted that increasing the frequency of extreme results makes small bonuses to defense
024 * or offense typically less useful, and small penalties less harmful. The opposite can be true for a carefully tuned
025 * game where the most common results are tightly clustered, and most target numbers are just slightly above the
026 * ordinary average. In tabletop games, 1d20 and 3d6 have the same average, but 1d20 is uniform, where 3d6 is clustered
027 * around 10 and 11, each the result of 1/8 of rolls on their own and 1/4 together. This makes the case where a +1 bonus
028 * to succeed changes the outcome on approximately 5% of 1d20 rolls, regardless of the required number to succeed if it
029 * is less than 20. However, a +1 bonus matters on a variable portion of 3d6 rolls; if you become able to succeed on a
030 * 10 or 11 where that was a failure before, the bonus applies approximately 12.5% of the time. Becoming able to succeed
031 * on an 18 where that was a failure before is essentially worthless, affecting less than 0.5% of rolls. This property
032 * of centralized results should be considered if game balance and/or the lethality of combat is important. One lengthy
033 * stretch of extreme results by enemies that work against the favor of a player character generally result in a dead
034 * player character, and RNGs that make extreme results more common may seem particularly cruel to players.
035 * <br>
036 * This generator sets a field, rawLatest, every time a random number is produced. This stores a pseudo-random double
037 * between 0.0 (inclusive) and 1.0 (exclusive) that is not subject to the bias an expected average introduces, and is
038 * close to uniformly distributed. You should expect rawLatest to be higher when higher numbers are returned from a
039 * method like nextInt(), and lower when lower numbers are returned. This can be useful for rare effects that should not
040 * be drastically more or less likely when slight changes are made to the expected average; if the expected average is
041 * 0.65, many more random doubles from nextDouble() will be between 0.95 and 1.0 (probably more than 10% of random
042 * numbers), but rawLatest will only be between 0.95 and 1.0 for close to 5% of all generations.
043 * <br>
044 * You can get and set the state this uses internally, and this is stored as a 64-bit long.
045 * <br>
046 * The choice of RandomnessSource doesn't really matter since this will always use a LightRNG internally.
047 * LightRNG is the current best StatefulRandomness implementation, with excellent performance characteristics and
048 * few flaws, and though its relatively low period may sometimes be a detriment, all StatefulRandomness implementations
049 * will have the same or lower period.
050 * <br>
051 * More customizations may be added in the future to the ones available currently.
052 */
053public class EditRNG extends StatefulRNG implements Serializable{
054
055        /** Used to tweak the generator toward high or low values. */
056    private double expected = 0.5;
057
058    // These are tied to expected, and must change when it does.
059    private double offset;
060    private double range = 1.0;
061    /**
062     * When positive, makes the generator more likely to generate values close to the average (bell curve).
063     * When zero (the default), makes no changes to the centering of values.
064     * When negative, makes the generator swing more toward extremes rather than gravitate toward the average.
065     * Values are typically between -100 and 100, but can go as low as -200 or as high as 200 (stopping there).
066     */
067    private double centrality;
068
069
070    // This lets us avoid a conversion to double every time we generate a number.
071    private long centralityCalculated = 0x0008000000000000L;
072
073    /**
074     * The latest generated double, between 0.0 and 1.0, before changes for centrality and expected average.
075     * Doubles are used to generate all random numbers this class produces, so be aware that calling getRandomElement()
076     * will change this just as much as nextDouble(), nextInt(), or between() will. Primarily useful to obtain
077     * uniformly-distributed random numbers that are related to the biased random numbers this returns as a main result,
078     * such as to find when the last number generated was in the bottom 5% (less than 0.05, which could represent some
079     * kind of critical failure or fumble) or top 10% (greater than or equal to 0.9, which could grant a critical
080     * success or luck-based reward of some kind).
081     */
082    public double rawLatest = 0.5;
083        private static final long serialVersionUID = -2458726316853811777L;
084
085    /**
086     * Constructs an EditRNG with a pseudo-random seed from Math.random().
087     */
088    public EditRNG()
089    {
090    }
091    /**
092     * Construct a new EditRNG with the given seed.
093     *
094     * @param seed used to seed the default RandomnessSource.
095     */
096    public EditRNG(final long seed) {
097        super(seed);
098    }
099    /**
100     * Construct a new EditRNG with the given seed.
101     *
102     * @param seed used to seed the default RandomnessSource.
103     */
104    public EditRNG(final CharSequence seed) {
105        super(seed);
106    }
107
108    /**
109     * Construct a new EditRNG with the given seed.
110     *
111     * @param seed used to seed the default RandomnessSource.
112     * @param expected the expected average for random doubles, which will be capped between 0.1 and 0.9
113     */
114    public EditRNG(final long seed, double expected) {
115        super(seed);
116        setExpected(expected);
117    }
118    /**
119     * Construct a new EditRNG with the given seed.
120     *
121     * @param seed used to seed the default RandomnessSource.
122     * @param expected the expected average for random doubles, which will be capped between 0.1 and 0.9
123     */
124    public EditRNG(final String seed, double expected) {
125        super(seed);
126        setExpected(expected);
127    }
128
129    /**
130     * Construct a new EditRNG with the given seed.
131     *
132     * @param seed used to seed the default RandomnessSource.
133     * @param expected the expected average for random doubles, which will be capped between 0.1 and 0.9
134     * @param centrality if positive, makes results more likely to be near expected; if negative, the opposite. The
135     *                   absolute value of centrality affects how centered results will be, with 0 having no effect
136     */
137    public EditRNG(final long seed, double expected, double centrality) {
138        super(seed);
139        setExpected(expected);
140        setCentrality(centrality);
141    }
142    /**
143     * Construct a new EditRNG with the given seed.
144     *
145     * @param seed used to seed the default RandomnessSource.
146     * @param expected the expected average for random doubles, which will be capped between 0.1 and 0.9
147     * @param centrality if positive, makes results more likely to be near expected; if negative, the opposite. The
148     *                   absolute value of centrality affects how centered results will be, with 0 having no effect
149     */
150    public EditRNG(final String seed, double expected, double centrality) {
151        super(seed);
152        setExpected(expected);
153        setCentrality(centrality);
154    }
155
156    /**
157     * Construct a new EditRNG with the given seed.
158     *
159     * @param rs the implementation used to generate random bits.
160     */
161    public EditRNG(final RandomnessSource rs) {
162        super(rs);
163    }
164    /**
165     * Construct a new EditRNG with the given seed.
166     *
167     * @param rs the implementation used to generate random bits.
168     * @param expected the expected average for random doubles, which will be capped between 0.1 and 0.9
169     */
170    public EditRNG(final RandomnessSource rs, double expected) {
171        super(rs);
172        setExpected(expected);
173    }
174    /**
175     * Construct a new EditRNG with the given seed.
176     *
177     * @param rs the implementation used to generate random bits.
178     * @param expected the expected average for random doubles, which will be capped between 0.1 and 0.9
179     * @param centrality if positive, makes results more likely to be near expected; if negative, the opposite. The
180     *                   absolute value of centrality affects how centered results will be, with 0 having no effect
181     */
182    public EditRNG(final RandomnessSource rs, double expected, double centrality) {
183        super(rs);
184        setExpected(expected);
185        setCentrality(centrality);
186    }
187    private double twist(double input) {
188        return (input += 1.0) - (int)input;
189    }
190
191    /**
192     * Generate a random double, altered to try to match the expected average and centrality.
193     * @return a double between 0.0 (inclusive) and 1.0 (exclusive)
194     */
195    @Override
196    public double nextDouble() {
197        return offset + range * ((centralityCalculated > (random.nextLong() & 0xfffffffffffffL) ?
198                ((random.nextLong() & 0xfffffffffffffL) - (random.nextLong() & 0xfffffffffffffL)) * 0x1p-53 + 0.5 :
199                twist(((random.nextLong() & 0xfffffffffffffL) - (random.nextLong() & 0xfffffffffffffL)) * 0x1p-53)));
200    }
201
202    /**
203     * This returns a random double between 0.0 (inclusive) and max (exclusive).
204     *
205     * @return a value between 0 (inclusive) and max (exclusive)
206     */
207    @Override
208    public double nextDouble(double max) {
209        return nextDouble() * max;
210    }
211
212    /**
213     * Returns a random integer below the given bound, or 0 if the bound is 0 or
214     * negative.
215     *
216     * @param bound the upper bound (exclusive)
217     * @return the found number
218     */
219    @Override
220    public int nextInt(int bound) {
221        if (bound <= 0) {
222            return 0;
223        }
224
225        return (int)(nextDouble() * bound);
226    }
227
228    /**
229     * Returns a random integer, which may be positive or negative.
230     * @return A random int
231     */
232    @Override
233    public int nextInt() {
234        return (int)((nextDouble() * 2.0 - 1.0) * 0x7FFFFFFF);
235    }
236
237    /**
238     * Returns a random long, which may be positive or negative.
239     * @return A random long
240     */
241    @Override
242    public long nextLong() {
243        return (long)((nextDouble() * 2.0 - 1.0) * 0x7FFFFFFFFFFFFFFFL);
244    }
245
246    /**
247     * Returns a random long below the given bound, or 0 if the bound is 0 or
248     * negative.
249     *
250     * @param bound the upper bound (exclusive)
251     * @return the found number
252     */
253    @Override
254        public long nextLong(long bound) {
255        if (bound <= 0) {
256            return 0;
257        }
258        return (long)(nextDouble() * bound);
259    }
260    /**
261     * Gets the current expected average for this EditRNG.
262     * @return the current expected average.
263     */
264    public double getExpected() {
265        return expected;
266    }
267
268    /**
269     * Sets the expected average for random doubles this produces, which must always be between 0.1 and 0.9, and will be
270     * set to 0.5 if an invalid value is passed.
271     * @param expected the expected average to use, which should be 0.1 &lt;= fairness &lt; 0.9
272     */
273    public void setExpected(double expected) {
274        if(expected < 0.1 || expected >= 0.9)
275            this.expected = 0.5;
276        else
277            this.expected = expected;
278        offset = Math.max(0.0, this.expected - 0.5) * 2.0;
279        range = this.expected <= 0.5 ? this.expected * 2.0 : 1.0 - offset;
280    }
281
282    /**
283     * Gets the current centrality measure of this EditRNG.
284     * Centrality has several possible effects:
285     * When positive, makes the generator more likely to generate values close to the average (bell curve).
286     * When zero (the default), makes no changes to the centering of values.
287     * When negative, makes the generator swing more toward extremes rather than gravitate toward the average.
288     * <br>
289     * Values are typically between -100 and 100, but can have extreme weight and overshadow other parts of the RNG if
290     * they go much higher than 200.
291     * @return the current centrality
292     */
293    public double getCentrality() {
294        return centrality;
295    }
296
297    /**
298     * Gets the current centrality measure of this EditRNG.
299     * Centrality has several possible effects:
300     * When positive, makes the generator more likely to generate values close to the average (bell curve).
301     * When zero (the default), makes no changes to the centering of values.
302     * When negative, makes the generator swing more toward extremes rather than gravitate toward the average.
303     * <br>
304     * Values are typically between -100 and 100, but can have extreme weight and overshadow other parts of the RNG if
305     * they go much higher than 200.
306     * @param centrality the new centrality measure to use
307     */
308    public void setCentrality(double centrality) {
309        this.centrality = Math.max(-200, Math.min(200, centrality));
310        centralityCalculated = NumberTools.doubleToLongBits(this.centrality * 0.0024999 + 1.5) & 0xfffffffffffffL;
311    }
312
313    /**
314     *
315     * @param bits the number of bits to be returned
316     * @return a random int of the number of bits specified.
317     */
318    @Override
319    public int next(int bits) {
320        return (int)(nextDouble() * (1L << bits));
321    }
322
323    @Override
324    public float nextFloat() {
325        return (float)nextDouble();
326    }
327
328    @Override
329    public boolean nextBoolean() {
330        return nextDouble() >= 0.5;
331    }
332
333    @Override
334    public RandomnessSource getRandomness() {
335        return random;
336    }
337
338    @Override
339    public void setRandomness(RandomnessSource random) {
340        this.random = random;
341    }
342
343    /**
344     * Gets the latest "un-biased" random double used to produce the most recent (potentially) biased random number
345     * generated for another method in this class, such as nextDouble(), between(), or getRandomElement(). This is a
346     * double between 0.0 (inclusive) and 1.0 (exclusive).
347     * @return the latest uniformly-distributed double before bias is added; between 0.0 and 1.0 (exclusive upper)
348     */
349    public double getRawLatest() {
350        return rawLatest;
351    }
352
353    /**
354     * Creates a copy of this StatefulRNG; it will generate the same random numbers, given the same calls in order, as
355     * this StatefulRNG at the point copy() is called. The copy will not share references with this StatefulRNG.
356     *
357     * @return a copy of this StatefulRNG
358     */
359    @Override
360    public EditRNG copy() {
361        EditRNG next = new EditRNG(random.copy(), expected, centrality);
362        next.rawLatest = rawLatest;
363        return next;
364    }
365
366    @Override
367    public String toString() {
368        return "EditRNG{" +
369                "expected=" + expected +
370                ", centrality=" + centrality +
371                ", Randomness Source=" + random +
372                '}';
373    }
374
375    @Override
376    public boolean equals(Object o) {
377        if (this == o) return true;
378        if (o == null || getClass() != o.getClass()) return false;
379        if (!super.equals(o)) return false;
380
381        EditRNG editRNG = (EditRNG) o;
382
383        if (Double.compare(editRNG.expected, expected) != 0) return false;
384        return Double.compare(editRNG.centrality, centrality) == 0;
385    }
386
387    @Override
388    public int hashCode() {
389        int result = super.hashCode() * 31;
390        result += NumberTools.doubleToMixedIntBits(expected);
391        result = 31 * result + NumberTools.doubleToMixedIntBits(centrality);
392        return result;
393    }
394
395    /**
396     * Returns a random non-negative integer below the given bound, or 0 if the bound is 0.
397     * Uses a slightly optimized technique. This method is considered "hasty" since
398     * it should be faster than nextInt() doesn't check for "less-valid" bounds values. It also
399     * has undefined behavior if bound is negative, though it will probably produce a negative
400     * number (just how negative is an open question).
401     *
402     * @param bound the upper bound (exclusive); behavior is undefined if bound is negative
403     * @return the found number
404     */
405    @Override
406    public int nextIntHasty(int bound) {
407        return (int)(nextDouble() * bound);
408    }
409    /**
410     * Returns this EditRNG in a way that can be deserialized even if only {@link IRNG}'s methods can be called.
411     * @return a {@link Serializable} view of this EditRNG; always {@code this}
412     */
413    @Override
414    public Serializable toSerializable() {
415        return this;
416    }
417
418
419}