001package squidpony.squidmath; 002 003import java.io.Serializable; 004import java.util.LinkedHashMap; 005import java.util.Map; 006 007/** 008 * A class that wraps an RNG and allows different String keys to be associated with biases toward low or high results 009 * when a method is called that gets a number from the wrapped RNG. With this, you could make a category of "blessed" or 010 * "cursed" biases that, instead of using a uniform distribution that produces all numbers approximately with equal 011 * likelihood (with doubles between 0.0 and 1.0 averaging at 0.5), have different averages, like 0.7 for blessed or 0.3 012 * for cursed, when generating between 0.0 and 1.0. You could also use this to favor or disfavor the player for "easy 013 * mode" or "hard mode" categories of play. 014 * <br> 015 * The API allows you to associate an alternative average with a kind as a String, like "blessed to-hit" or "hard mode 016 * enemy damage", if you expect to use that number more than once and might want to tweak any averages by changing one 017 * number at a later point. You can also give an average as a double in place of a kind as a String, which avoids a 018 * HashMap lookup and lets you give flexibly-adjusted numbers, but does need more effort to change many values 019 * throughout a codebase if averages aren't all generated by a formula. You can also set the distribution in the 020 * constructor or by changing the public distribution field; you can use constants in this class, TRIANGULAR, 021 * EXPONENTIAL, TRUNCATED, SOFT_TRIANGULAR, and EXP_TRI (the average of EXPONENTIAL and TRIANGULAR), for different 022 * choices, with the default being EXP_TRI. Each one of these has different behavior regarding a preference toward 023 * extreme values; TRIANGULAR almost never produces very high or very low values, EXPONENTIAL frequently produces the 024 * highest or lowest values for high or low expected averages, respectively, TRUNCATED will simply never generate values 025 * that are too far from the average (otherwise it's uniform), SOFT_TRIANGULAR will produce a rounded version of 026 * TRIANGULAR's distribution with less of an angular peak and more frequent high and low values, and EXP_TRI will have 027 * something like a curve shape that may "collide" slightly with the upper bound if the average is high enough. 028 * <br> 029 * Credit for the technique used for the exponential modification to distributions goes to user pjs on StackOverflow, 030 * http://stackoverflow.com/a/17796997 . 031 * Credit should also be given to user vydd of the LispGames community, who made a visualization of the distribution 032 * changing as the expected average changed (at the time, the typical behavior of an exponential distribution looked 033 * like a bug, and the visualization showed that it was correct behavior). Derrick Creamer noticed how strange the 034 * exponential distribution would seem to most players, and that led to adding the simple triangular distribution. 035 * Created by Tommy Ettinger on 3/20/2016. 036 */ 037public class RandomBias implements Serializable { 038 private LinkedHashMap<String, Double> biases; 039 public RNG rng; 040 public int distribution = EXP_TRI; 041 042 /** 043 * A constant for a distribution that linearly increases in probability from a 0.0 chance of 0.0. to a 0.3333... 044 * chance of getting the expected average, then linearly decreases until it reaches a 0.0 chance of 1.0. Doesn't 045 * really support expected averages below 1/3 or above 2/3, due to how the triangular distribution works. 046 */ 047 public static final int TRIANGULAR = 0, 048 /** 049 * A constant for a distribution that, for all values other than 0.5, will strongly favor either high or low 050 * results to push the odds in favor of a high or low expected average. This is probably not what most players 051 * expect, since it leads to massively more critical hits or failures if min or max results are counted as such. 052 */ 053 EXPONENTIAL = 1, 054 /** 055 * Not like the other distributions; this is a constant for a distribution that simply truncates a random number's 056 * possible range to less than 1.0, and adjusts the minimum or maximum value so that the average is the desired one. 057 * This is a uniform random number generator, unlike the others which have a bias toward certain values; it simply 058 * cannot generate values outside a certain range, and the values within the range it generates are all equally 059 * likely. The range gets smaller the closer the expected average is to 0.0 or 1.0, with an expected average of 0.4 060 * producing values between 0.0 and 0.8, and an expected average of 0.9 producing values of 0.8 to 1.0 (in all 061 * cases, this is exclusive on the upper bound). 062 */ 063 TRUNCATED = 2, 064 /** 065 * A constant for a distribution that averages two random floats, each with a triangular distribution (the same as 066 * what using the TRIANGULAR constant would produce, but the distribution becomes more curved when multiple random 067 * "dice rolls" are involved), to soften the point of the triangle and make very high or very low values appear 068 * somewhat more frequently, while the expected average appears less frequently. This should not be used to generate 069 * very large numbers, since the floats this uses lose precision after 24 bits, or about 16 million. It should 070 * produce very reasonable results for common values in games, like 0 to 100 or 0 to 20. Doesn't really support 071 * expected averages below 1/3 or above 2/3, due to how the triangular distribution works. 072 */ 073 SOFT_TRIANGULAR = 3, 074 /** 075 * A constant for a distribution that averages two random floats, one with a triangular distribution (the same as 076 * what using the TRIANGULAR constant would produce), and one with an exponential distribution (the same as what 077 * using the EXPONENTIAL constant would produce) to soften the point of the triangle and make very high or very low 078 * values appear much more frequently, while the expected average appears somewhat less frequently. This should not 079 * be used to generate very large numbers, since the floats this uses lose precision after 24 bits, or about 16 080 * million. It should produce very reasonable results for common values in games, like 0 to 100 or 0 to 20. Has 081 * limited support for expected averages below 1/3 or above 2/3; unlike TRIANGULAR or SOFT_TRIANGULAR, expected 082 * averages outside that range will still affect the generated average due to the EXPONENTIAL distribution 083 * contributing half of the correction needed to match the expected average. An expected average of 5/6 will produce 084 * an approximate average with this of 3/4, as opposed to 2/3 (for pure TRIANGULAR) or 5/6 (for EXPONENTIAL). 085 */ 086 EXP_TRI = 4, 087 /** 088 * "Bathtub-shaped" or "U-shaped" distribution (technically the arcsine distribution) that is significantly more 089 * likely to produce results at either extreme than it is to generate them in the center. The extremes in this case 090 * are the same as the truncated distribution, so not all values are possible unless the expected average is 0.5. 091 */ 092 BATHTUB_TRUNCATED = 5; 093 094 private static final int softRange = 1 << 24; 095 096 private static final long serialVersionUID = 4245874924013134958L; 097 098 public RandomBias() 099 { 100 biases = new LinkedHashMap<>(32); 101 rng = new RNG(); 102 } 103 public RandomBias(RNG rng) 104 { 105 this.rng = rng; 106 biases = new LinkedHashMap<>(32); 107 } 108 public RandomBias(RNG rng, Map<String, Double> mapping) 109 { 110 this(rng, mapping, EXP_TRI); 111 } 112 public RandomBias(RNG rng, Map<String, Double> mapping, int distribution) { 113 this.rng = rng; 114 this.distribution = distribution; 115 if (mapping == null) { 116 biases = new LinkedHashMap<>(32); 117 } else { 118 biases = new LinkedHashMap<>(mapping.size()); 119 double exp; 120 for (Map.Entry<String, Double> kv : mapping.entrySet()) { 121 exp = kv.getValue(); 122 if (exp <= 0) exp = 0.001; 123 if (exp >= 1) exp = 0.999; 124 biases.put(kv.getKey(), exp); 125 } 126 } 127 } 128 129 /** 130 * Adds a kind of bias that can be used to change the average of random numbers generated when specified with that 131 * kind. 132 * @param kind a String that will be used as a key in a Map; can be given later on to bias results using this key 133 * @param expectedAverage above 0.0 and below 1.0, with 0.5 as the normal average but other values are more useful. 134 * @return this for chaining 135 */ 136 public RandomBias putBias(String kind, double expectedAverage) 137 { 138 if(expectedAverage <= 0) expectedAverage = 0.001; 139 if(expectedAverage >= 1) expectedAverage = 0.999; 140 biases.put(kind, expectedAverage); 141 return this; 142 } 143 /** 144 * Adds a number of kinds of bias that can be used to change the average of random numbers generated when specified 145 * with one of those kinds. 146 * @param mapping should have String keys that can be used later, and double values greater than 0 but less than 1. 147 * @return this for chaining 148 */ 149 public RandomBias putBiases(Map<String, Double> mapping) 150 { 151 double exp; 152 for(Map.Entry<String, Double> kv : mapping.entrySet()) 153 { 154 exp = kv.getValue(); 155 if(exp <= 0) exp = 0.001; 156 if(exp >= 1) exp = 0.999; 157 biases.put(kv.getKey(), exp); 158 } 159 return this; 160 } 161 162 private double quantile(double expected) 163 { 164 switch (distribution) 165 { 166 case EXPONENTIAL: return exponentialQuantile(expected); 167 case TRUNCATED: return truncatedQuantile(expected); 168 case TRIANGULAR: return triangularQuantile(expected); 169 case SOFT_TRIANGULAR: return softQuantile(expected); 170 case BATHTUB_TRUNCATED: return bathtubTruncatedQuantile(expected); 171 default: return mixQuantile(expected); 172 } 173 } 174 175 private double triangularQuantile(double expected) 176 { 177 expected = Math.max(0.001, Math.min(0.999, expected * 3.0 - 1.0)); 178 double p = rng.nextDouble(); 179 if(p < expected) 180 return Math.sqrt(expected * p); 181 if(p > expected) 182 return 1 - Math.sqrt((1 - expected) * (1 - p)); 183 return expected; 184 } 185 private double truncatedQuantile(double expected) 186 { 187 if(expected >= 0.5) 188 return rng.nextDouble() * (1.0 - expected) * 2 + expected - (1.0 - expected); 189 return rng.nextDouble() * expected * 2; 190 } 191 private double bathtubQuantile(double expected) 192 { 193 expected = Math.sin(expected * Math.PI * 0.5); 194 return expected * expected; 195 } 196 private double bathtubTruncatedQuantile(double expected) 197 { 198 if(expected >= 0.5) 199 return bathtubQuantile(rng.nextDouble()) * (1.0 - expected) * 2 + expected - (1.0 - expected); 200 return bathtubQuantile(rng.nextDouble()) * expected * 2; 201 } 202 private double exponentialQuantile(double expected) 203 { 204 return 1.0 - Math.pow( rng.nextDouble(), 1.0 / (1.0 - expected) - 1.0); 205 } 206 private static float longToFloat(long n) 207 { 208 return n * 1.0f / softRange; 209 } 210 private double softQuantile(double expected) 211 { 212 expected = Math.max(0.001, Math.min(0.999, expected * 3.0 - 1.0)); 213 long pair = rng.nextLong(); 214 float left = longToFloat(pair >>> 40), right = longToFloat((pair >>> 16) & 0xFFFFFFL); 215 double v; 216 217 if(left < expected) 218 v = Math.sqrt(expected * left); 219 else if(left > expected) 220 v = 1 - Math.sqrt((1 - expected) * (1 - left)); 221 else 222 v = expected; 223 if(right < expected) 224 return (v + Math.sqrt(expected * right)) * 0.5; 225 if(right > expected) 226 return (v + 1 - Math.sqrt((1 - expected) * (1 - right))) * 0.5; 227 return expected; 228 } 229 private double mixQuantile(double expected) 230 { 231 double d2 = Math.max(0.001, Math.min(0.999, expected * 3.0 - 1.0)), v; 232 long pair = rng.nextLong(); 233 float left = longToFloat(pair >>> 40), right = longToFloat((pair >>> 16) & 0xFFFFFFL); 234 235 if(left < d2) 236 v = Math.sqrt(d2 * left); 237 else if(left > d2) 238 v = 1 - Math.sqrt((1 - d2) * (1 - left)); 239 else 240 v = d2; 241 return (Math.pow( right, 1.0 / expected - 1.0) + v) * 0.5; 242 } 243 /** 244 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 245 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 246 * expected average the kind was associated with. The returned number will be a positive long in either case, but 247 * not all long values are possible if this is biased, in part because of generating a double, which has less 248 * precision than long, and in part because some numbers need to be more common than others. If the kind is not in 249 * the map, this generates a positive long, using 63 bits instead of RNG's normal 64 bits since it never generates 250 * negative numbers. 251 * @param kind the kind of bias to look up 252 * @return a random 63-bit positive long, potentially influenced by the bias associated with kind, if present 253 */ 254 public long biasedLong(String kind) 255 { 256 Double d = biases.get(kind); 257 if(d == null) 258 return rng.nextLong() >>> 1; 259 return (long)(quantile(d) * Long.MAX_VALUE); 260 } 261 262 /** 263 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 264 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 265 * expected average the kind was associated with. The returned number will be a long between 0 and bound (exclusive 266 * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all 267 * negative bounds). If the kind is not in the map, this generates a long between 0 and bound (exclusive on bound), 268 * even if bound is negative. 269 * @param kind the kind of bias to look up 270 * @param bound the outer bound, exclusive; can be negative 271 * @return a random long between 0 and bound, potentially influenced by the bias associated with kind, if present 272 */ 273 public long biasedLong(String kind, long bound) 274 { 275 boolean n = bound < 0; 276 Double d = biases.get(kind); 277 if(d == null) 278 return n ? rng.nextLong(-bound) * -1 : rng.nextLong(bound); 279 return (long)(quantile(d) * bound); 280 } 281 282 /** 283 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 284 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 285 * expected average the kind was associated with. The returned number will be a double between 0.0 and 1.0 286 * (exclusive on 1.0). If the kind is not in the map, this generates a double using RNG and no further changes. 287 * @param kind the kind of bias to look up 288 * @return a random double between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present 289 */ 290 public double biasedDouble(String kind) 291 { 292 Double d = biases.get(kind); 293 if(d == null) 294 return rng.nextDouble(); 295 return quantile(d); 296 } 297 298 /** 299 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 300 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 301 * expected average the kind was associated with. The returned number will be a double between 0 and bound (exclusive 302 * on bound), where bound can be negative (the same as RNG). If the kind is not in the map, this doesn't adjust the 303 * average, and acts exactly like RNG. 304 * @param kind the kind of bias to look up 305 * @param bound the outer bound, exclusive; can be negative 306 * @return a random double between 0 and bound, potentially influenced by the bias associated with kind, if present 307 */ 308 public double biasedDouble(String kind, double bound) 309 { 310 Double d = biases.get(kind); 311 if(d == null) 312 return rng.nextDouble(bound); 313 return quantile(d) * bound; 314 } 315 316 /** 317 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 318 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 319 * expected average the kind was associated with. The returned number will be a positive int in either case. If the 320 * kind is not in the map, this generates a positive int, using 31 bits instead of RNG's normal 32 bits since it 321 * never generates negative numbers. 322 * @param kind the kind of bias to look up 323 * @return a random 31-bit positive int, potentially influenced by the bias associated with kind, if present 324 */ 325 public int biasedInt(String kind) 326 { 327 Double d = biases.get(kind); 328 if(d == null) 329 return rng.nextInt() >>> 1; 330 return (int)(quantile(d) * Integer.MAX_VALUE); 331 } 332 333 /** 334 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 335 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 336 * expected average the kind was associated with. The returned number will be an int between 0 and bound (exclusive 337 * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all 338 * negative bounds). If the kind is not in the map, this generates an int between 0 and bound (exclusive on bound), 339 * even if bound is negative. 340 * @param kind the kind of bias to look up 341 * @param bound the outer bound, exclusive; can be negative 342 * @return a random int between 0 and bound, potentially influenced by the bias associated with kind, if present 343 */ 344 public int biasedInt(String kind, int bound) 345 { 346 boolean n = bound < 0; 347 Double d = biases.get(kind); 348 if(d == null) 349 return n ? rng.nextInt(-bound) * -1 : rng.nextInt(bound); 350 return (int)(quantile(d) * bound); 351 } 352 353 /** 354 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 355 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 356 * expected average the kind was associated with. The returned number will be a float between 0.0 and 1.0 357 * (exclusive on 1.0). If the kind is not in the map, this generates a float using RNG and no further changes. 358 * @param kind the kind of bias to look up 359 * @return a random float between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present 360 */ 361 public float biasedFloat(String kind) 362 { 363 Double d = biases.get(kind); 364 if(d == null) 365 return rng.nextFloat(); 366 return (float)(quantile(d)); 367 } 368 369 /** 370 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 371 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 372 * expected average the kind was associated with. The returned number will be a float between 0 and bound (exclusive 373 * on bound), where bound can be negative. If the kind is not in the map, this doesn't adjust the average. 374 * @param kind the kind of bias to look up 375 * @param bound the outer bound, exclusive; can be negative 376 * @return a random double between 0 and bound, potentially influenced by the bias associated with kind, if present 377 */ 378 public float biasedFloat(String kind, float bound) 379 { 380 Double d = biases.get(kind); 381 if(d == null) 382 return rng.nextFloat() * bound; 383 return (float)(quantile(d) * bound); 384 } 385 386 /** 387 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 388 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 389 * expected average the kind was associated with. The returned boolean will be true if the random number (between 390 * 0.0 and 1.0, exclusive upper) is greater than or equal to 0.5. If the kind is not in the map, this generates a 391 * boolean using RNG and no further changes. 392 * @param kind the kind of bias to look up 393 * @return a random float between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present 394 */ 395 public boolean biasedBoolean(String kind) 396 { 397 Double d = biases.get(kind); 398 if(d == null) 399 return rng.nextBoolean(); 400 return quantile(d) >= 0.5; 401 } 402 /** 403 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 404 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 405 * expected average the kind was associated with. The returned number will be an int between min and max (exclusive 406 * on max), where min and/or max can be negative, and the difference between the two can be either positive or 407 * negative. If the kind is not in the map, this doesn't adjust the average. 408 * @param kind the kind of bias to look up 409 * @param min the inner bound, inclusive; can be negative 410 * @param max the outer bound, exclusive; can be negative 411 * @return a random int between min and max, potentially influenced by the bias associated with kind, if present 412 */ 413 public int biasedBetween(String kind, int min, int max) 414 { 415 return biasedInt(kind, max - min) + min; 416 } 417 418 /** 419 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 420 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 421 * expected average the kind was associated with. The returned number will be a long between min and max (exclusive 422 * on max), where min and/or max can be negative, and the difference between the two can be either positive or 423 * negative. If the kind is not in the map, this doesn't adjust the average. 424 * @param kind the kind of bias to look up 425 * @param min the inner bound, inclusive; can be negative 426 * @param max the outer bound, exclusive; can be negative 427 * @return a random long between min and max, potentially influenced by the bias associated with kind, if present 428 */ 429 public long biasedBetween(String kind, long min, long max) 430 { 431 return biasedLong(kind, max - min) + min; 432 } 433 434 /** 435 * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. 436 * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the 437 * expected average the kind was associated with. The returned number will be a double between min and max 438 * (exclusive on max), where min and/or max can be negative, and the difference between the two can be either 439 * positive or negative. If the kind is not in the map, this doesn't adjust the average. 440 * @param kind the kind of bias to look up 441 * @param min the inner bound, inclusive; can be negative 442 * @param max the outer bound, exclusive; can be negative 443 * @return a random double between min and max, potentially influenced by the bias associated with kind, if present 444 */ 445 public double biasedBetween(String kind, double min, double max) 446 { 447 return biasedDouble(kind, max - min) + min; 448 } 449 450 451 /** 452 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 453 * that would have the given expected average. The returned number will be a positive long in either case, but 454 * not all long values are possible if this is biased, in part because of generating a double, which has less 455 * precision than long, and in part because some numbers need to be more common than others. 456 * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0 457 * @return a random 63-bit positive long, potentially influenced by the bias associated with kind, if present 458 */ 459 public long biasedLong(double expectedAverage) 460 { 461 if(expectedAverage <= 0) expectedAverage = 0.001; 462 if(expectedAverage >= 1) expectedAverage = 0.999; 463 return (long)(quantile(expectedAverage) * Long.MAX_VALUE); 464 } 465 466 /** 467 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 468 * that would have the given expected average. The returned number will be a long between 0 and bound (exclusive 469 * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all 470 * negative bounds). 471 * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0 472 * @param bound the outer bound, exclusive; can be negative 473 * @return a random long between 0 and bound, potentially influenced by the bias associated with kind, if present 474 */ 475 public long biasedLong(double expectedAverage, long bound) 476 { 477 if(expectedAverage <= 0) expectedAverage = 0.001; 478 if(expectedAverage >= 1) expectedAverage = 0.999; 479 return (long)(quantile(expectedAverage) * bound); 480 } 481 482 /** 483 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 484 * that would have the given expected average. The returned number will be a double between 0.0 and 1.0 (exclusive 485 * on 1.0). 486 * @param expectedAverage the desired average 487 * @return a random double between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present 488 */ 489 public double biasedDouble(double expectedAverage) 490 { 491 if(expectedAverage <= 0) expectedAverage = 0.001; 492 if(expectedAverage >= 1) expectedAverage = 0.999; 493 return quantile(expectedAverage); 494 } 495 496 /** 497 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 498 * that would have the given expected average. The returned number will be a double between 0 and bound (exclusive 499 * on bound), where bound can be negative (the same as RNG). 500 * @param expectedAverage the desired average 501 * @param bound the outer bound, exclusive; can be negative 502 * @return a random double between 0 and bound, potentially influenced by the bias associated with kind, if present 503 */ 504 public double biasedDouble(double expectedAverage, double bound) 505 { 506 if(expectedAverage <= 0) expectedAverage = 0.001; 507 if(expectedAverage >= 1) expectedAverage = 0.999; 508 return quantile(expectedAverage) * bound; 509 } 510 511 /** 512 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 513 * that would have the given expected average. The returned number will be a positive int from 0 to (2 to the 31)-1 514 * in either case. 515 * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0 516 * @return a random 31-bit positive int, potentially influenced by the bias associated with kind, if present 517 */ 518 public int biasedInt(double expectedAverage) 519 { 520 if(expectedAverage <= 0) expectedAverage = 0.001; 521 if(expectedAverage >= 1) expectedAverage = 0.999; 522 return (int)(quantile(expectedAverage) * Integer.MAX_VALUE); 523 } 524 525 /** 526 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 527 * that would have the given expected average. The returned number will be an int between 0 and bound (exclusive 528 * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all 529 * negative bounds). 530 * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0 531 * @param bound the outer bound, exclusive; can be negative 532 * @return a random int between 0 and bound, potentially influenced by the bias associated with kind, if present 533 */ 534 public int biasedInt(double expectedAverage, int bound) 535 { 536 if(expectedAverage <= 0) expectedAverage = 0.001; 537 if(expectedAverage >= 1) expectedAverage = 0.999; 538 return (int)(quantile(expectedAverage) * bound); 539 } 540 541 /** 542 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 543 * that would have the given expected average. The returned number will be a float between 0.0f and 1.0f (exclusive 544 * on 1.0f). 545 * @param expectedAverage the desired average 546 * @return a random float between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present 547 */ 548 public float biasedFloat(double expectedAverage) 549 { 550 if(expectedAverage <= 0) expectedAverage = 0.001; 551 if(expectedAverage >= 1) expectedAverage = 0.999; 552 return (float)(quantile(expectedAverage)); 553 } 554 555 /** 556 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 557 * that would have the given expected average. The returned number will be a float between 0f and bound (exclusive 558 * on bound), where bound can be negative. 559 * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0 560 * @param bound the outer bound, exclusive; can be negative 561 * @return a random double between 0 and bound, potentially influenced by the bias associated with kind, if present 562 */ 563 public float biasedFloat(double expectedAverage, float bound) 564 { 565 if(expectedAverage <= 0) expectedAverage = 0.001; 566 if(expectedAverage >= 1) expectedAverage = 0.999; 567 return (float)(quantile(expectedAverage) * bound); 568 } 569 570 /** 571 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 572 * that would have the given expected average. The returned boolean will be true if the random number (between 0.0 573 * and 1.0, exclusive upper) is greater than or equal to 0.5. 574 * @param expectedAverage the desired probability of a true result, between 0.0 and 1.0 575 * @return a random float between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present 576 */ 577 public boolean biasedBoolean(double expectedAverage) 578 { 579 if(expectedAverage <= 0) expectedAverage = 0.001; 580 if(expectedAverage >= 1) expectedAverage = 0.999; 581 return quantile(expectedAverage) >= 0.5; 582 } 583 /** 584 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 585 * that would have the given expected average. The returned number will be an int between min and max (exclusive 586 * on max), where min and/or max can be negative, and the difference between the two can be either positive or 587 * negative. 588 * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0 589 * @param min the inner bound, inclusive; can be negative 590 * @param max the outer bound, exclusive; can be negative 591 * @return a random int between min and max, potentially influenced by the bias associated with kind, if present 592 */ 593 public int biasedBetween(double expectedAverage, int min, int max) 594 { 595 return biasedInt(expectedAverage, max - min) + min; 596 } 597 598 /** 599 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 600 * that would have the given expected average. The returned number will be a long between min and max (exclusive 601 * on max), where min and/or max can be negative, and the difference between the two can be either positive or 602 * negative. 603 * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0 604 * @param min the inner bound, inclusive; can be negative 605 * @param max the outer bound, exclusive; can be negative 606 * @return a random long between min and max, potentially influenced by the bias associated with kind, if present 607 */ 608 public long biasedBetween(double expectedAverage, long min, long max) 609 { 610 return biasedLong(expectedAverage, max - min) + min; 611 } 612 613 /** 614 * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution 615 * that would have the given expected average. The returned number will be a double between min and max (exclusive 616 * on max), where min and/or max can be negative, and the difference between the two can be either positive or 617 * negative. 618 * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0 619 * @param min the inner bound, inclusive; can be negative 620 * @param max the outer bound, exclusive; can be negative 621 * @return a random double between min and max, potentially influenced by the bias associated with kind, if present 622 */ 623 public double biasedBetween(double expectedAverage, double min, double max) 624 { 625 return biasedDouble(expectedAverage, max - min) + min; 626 } 627 628 @Override 629 public String toString() { 630 return "RandomBias{" + 631 "biases=" + biases + 632 ", rng=" + rng + 633 ", distribution=" + distribution + 634 '}'; 635 } 636 637}