001package squidpony.squidmath; 002 003import java.util.*; 004 005/** 006 * A type of RNG that can generate values larger or smaller than the normal maximum or minimum, based on a modifier. 007 * You should not use this as a general-purpose substitute for {@link RNG}; it is meant for cases where there is no hard 008 * maximum or minimum for a random value, so it is a poor fit for getting random items from collections or shuffling. 009 * It also uses a curved distribution (almost Gaussian, but slightly more shallow), which makes its results to be most 010 * often near the center of the range they can fall into. The {@link #luck} field affects the distribution simply, and 011 * should generally be between -0.5f and 0.5f except in cases where a character or event routinely defies all odds. 012 * There is no value for luck that will prevent this from sometimes producing values outside the requested range, though 013 * at luck = 0 it is somewhat rare for the bounds to be significantly exceeded. 014 * <br> 015 * The name comes from "critical hit," the rare but potentially very significant strike in many role-playing games. 016 * <br> 017 * Created by Tommy Ettinger on 9/20/2017. 018 */ 019public class CriticalRNG extends RNG { 020 /** 021 * Positive for higher results, negative for lower results; usually this is small, between -0.5f and 0.5f . 022 */ 023 public float luck; 024 025 /** 026 * Makes a CriticalRNG with a luck factor of 0 and a randomly-seeded DiverRNG for its RandomnessSource. 027 */ 028 public CriticalRNG() { 029 super(new DiverRNG()); 030 } 031 032 /** 033 * Makes a CriticalRNG with a luck factor of 0 and a DiverRNG with the given seed for its RandomnessSource. 034 * @param seed any long 035 */ 036 public CriticalRNG(long seed) { 037 super(new DiverRNG(seed)); 038 } 039 /** 040 * Makes a CriticalRNG with a luck factor of 0 and a DiverRNG with the given seed for its RandomnessSource (this 041 * will hash seedString using {@link CrossHash#hash64(CharSequence)} and use the result to seed the DiverRNG). 042 * @param seedString any String 043 */ 044 public CriticalRNG(CharSequence seedString) { 045 super(new DiverRNG(CrossHash.hash64(seedString))); 046 } 047 048 /** 049 * Makes a CriticalRNG with a luck factor of 0 and the given RandomnessSource. 050 * @param random a RandomnessSource, such as a {@link LongPeriodRNG} or {@link LightRNG} 051 */ 052 public CriticalRNG(RandomnessSource random) { 053 super(random); 054 } 055 056 /** 057 * Makes a CriticalRNG with the given luck factor and a randomly-seeded DiverRNG for its RandomnessSource. 058 * @param luck typically a small float, often between -0.5f and 0.5f, that will affect the results this returns 059 */ 060 public CriticalRNG(float luck) { 061 super(new DiverRNG()); 062 this.luck = luck; 063 } 064 065 /** 066 * Makes a CriticalRNG with the given luck factor and a DiverRNG with the given seed for its RandomnessSource. 067 * @param seed any long 068 * @param luck typically a small float, often between -0.5f and 0.5f, that will affect the results this returns 069 */ 070 public CriticalRNG(long seed, float luck) { 071 super(new DiverRNG(seed)); 072 this.luck = luck; 073 } 074 075 /** 076 * Makes a CriticalRNG with a luck factor of 0 and a DiverRNG with the given seed for its RandomnessSource (this 077 * will hash seedString using {@link CrossHash#hash64(CharSequence)} and use the result to seed the DiverRNG). 078 * @param seedString any String 079 * @param luck typically a small float, often between -0.5f and 0.5f, that will affect the results this returns 080 */ 081 public CriticalRNG(CharSequence seedString, float luck) { 082 super(new DiverRNG(CrossHash.hash64(seedString))); 083 this.luck = luck; 084 } 085 086 /** 087 * Makes a CriticalRNG with a luck factor of 0 and the given RandomnessSource. 088 * @param random a RandomnessSource, such as a {@link LongPeriodRNG} or {@link LightRNG} 089 * @param luck typically a small float, often between -0.5f and 0.5f, that will affect the results this returns 090 */ 091 public CriticalRNG(RandomnessSource random, float luck) { 092 super(random); 093 this.luck = luck; 094 } 095 096 @Override 097 public double nextDouble() { 098 return NumberTools.formCurvedDouble(random.nextLong()) * 0.875 + 0.5 + luck; 099 } 100 101 @Override 102 public double nextDouble(double max) { 103 return (NumberTools.formCurvedDouble(random.nextLong()) * 0.875 + 0.5 + luck) * max; 104 } 105 106 @Override 107 public float nextFloat() { 108 return NumberTools.formCurvedFloat(random.nextLong()) * 0.875f + 0.5f + luck; 109 } 110 111 @Override 112 public boolean nextBoolean() { 113 return NumberTools.formCurvedFloat(random.nextLong()) * 0.875f + 0.5f + luck >= 0f; 114 } 115 116 private static int intify(final double t) { 117 return t >= 0.0 ? (int) (t + 0.5) : (int) (t - 0.5); 118 } 119 private static long longify(final double t) { 120 return t >= 0.0 ? (long) (t + 0.5) : (long) (t - 0.5); 121 } 122 123 @Override 124 public long nextLong() { 125 return longify((NumberTools.formCurvedDouble(random.nextLong()) + luck * -2.0) * 0x8000000000000000L); 126 } 127 128 @Override 129 public long nextLong(long bound) { 130 return longify((NumberTools.formCurvedDouble(random.nextLong()) * 0.875 + 0.5 + luck) * bound); 131 } 132 133 @Override 134 public int nextInt(int bound) { 135 return intify((NumberTools.formCurvedDouble(random.nextLong()) * 0.875 + 0.5 + luck) * bound); 136 } 137 138 @Override 139 public int nextIntHasty(int bound) { 140 return intify((NumberTools.formCurvedDouble(random.nextLong()) * 0.875 + 0.5 + luck) * bound); 141 } 142 143 @Override 144 public int nextInt() { 145 return intify((NumberTools.formCurvedDouble(random.nextLong()) + luck * -2.0) * 0x80000000); 146 } 147 148 @Override 149 public <T> T getRandomElement(T[] array) { 150 if (array.length < 1) { 151 return null; 152 } 153 return array[super.nextIntHasty(array.length)]; 154 } 155 156 @Override 157 public <T> T getRandomElement(List<T> list) { 158 if (list.isEmpty()) { 159 return null; 160 } 161 return list.get(super.nextIntHasty(list.size())); 162 } 163 164 @Override 165 public <T> T getRandomElement(Collection<T> coll) { 166 int n; 167 if ((n = coll.size()) <= 0) { 168 return null; 169 } 170 n = super.nextIntHasty(n); 171 T t = null; 172 Iterator<T> it = coll.iterator(); 173 while (n-- >= 0 && it.hasNext()) 174 t = it.next(); 175 return t; 176 } 177}