/** * Construct a Spill without a level to actually scan. This constructor allows you to specify an RNG, but the actual * RandomnessSource the RNG that this object uses will not be identical to the one passed as random (64 bits will * be requested from the passed RNG, and that will be used to seed this class' RNG). * * If you use this constructor, you must call an initialize() method before using this class. * @param random an RNG that will be converted to a StatefulRNG if it is not one already */ public MultiSpill(IRNG random) { this(random.nextLong()); }
/** * Constructor for a random GreasedRegion of the given width and height, typically assigning approximately half of * the cells in this to "on" and the rest to off. A RandomnessSource can be slightly more efficient than an RNG when * you're making a lot of calls on it, so you may prefer {@link #GreasedRegion(RandomnessSource, int, int)}. * @param random an IRNG, such as an RNG, that this will use to generate its contents * @param width the maximum width for the GreasedRegion * @param height the maximum height for the GreasedRegion */ public GreasedRegion(final IRNG random, final int width, final int height) { this.width = width; this.height = height; ySections = (height + 63) >> 6; yEndMask = -1L >>> (64 - (height & 63)); data = new long[width * ySections]; for (int i = 0; i < width * ySections; i++) { data[i] = random.nextLong(); } if(ySections > 0 && yEndMask != -1) { for (int a = ySections - 1; a < data.length; a += ySections) { data[a] &= yEndMask; } } counts = new int[width * ySections]; tallied = false; } /**
private double mixQuantile(double expected) { double d2 = Math.max(0.001, Math.min(0.999, expected * 3.0 - 1.0)), v; long pair = rng.nextLong(); float left = (pair >>> 40) * 0x1p-24f, right = (pair & 0xFFFFFFL) * 0x1p-24f; if(left < d2) v = Math.sqrt(d2 * left); else if(left > d2) v = 1 - Math.sqrt((1 - d2) * (1 - left)); else v = d2; return (Math.pow( right, 1.0 / expected - 1.0) + v) * 0.5; } /**
public DungeonUtility(IRNG rng) { this.rng = new StatefulRNG(rng.nextLong()); }
private double softQuantile(double expected) { expected = Math.max(0.001, Math.min(0.999, expected * 3.0 - 1.0)); long pair = rng.nextLong(); float left = (pair >>> 40) * 0x1p-24f, right = (pair & 0xFFFFFFL) * 0x1p-24f; double v; if(left < expected) v = Math.sqrt(expected * left); else if(left > expected) v = 1 - Math.sqrt((1 - expected) * (1 - left)); else v = expected; if(right < expected) return (v + Math.sqrt(expected * right)) * 0.5; if(right > expected) return (v + 1 - Math.sqrt((1 - expected) * (1 - right))) * 0.5; return expected; } private double mixQuantile(double expected)
public static FakeLanguageGen randomLanguage(IRNG rng) { return randomLanguage(rng.nextLong()); }
/** * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the * expected average the kind was associated with. The returned number will be a long between 0 and bound (exclusive * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all * negative bounds). If the kind is not in the map, this generates a long between 0 and bound (exclusive on bound), * even if bound is negative. * @param kind the kind of bias to look up * @param bound the outer bound, exclusive; can be negative * @return a random long between 0 and bound, potentially influenced by the bias associated with kind, if present */ public long biasedLong(String kind, long bound) { boolean n = bound < 0; Double d = biases.get(kind); if(d == null) return n ? rng.nextLong(-bound) * -1 : rng.nextLong(bound); return (long)(quantile(d) * bound); }
/** * Constructs a new Thesaurus, seeding its RNG (used to shuffle word order) with the next long from the given RNG. * @param rng an RNG that will only be used to get one long (for seeding this class' RNG) */ public Thesaurus(IRNG rng) { mappings = new OrderedMap<>(256, Hashers.caseInsensitiveStringHasher); this.rng = new GWTRNG(rng.nextLong()); }
/** * Used to construct a Spill from the output of another, specifying a distance calculation and RNG. * <br> * This constructor allows you to specify an RNG, but the actual RandomnessSource the RNG that this object uses will * not be identical to the one passed as random (64 bits will be requested from the passed RNG, and that will be * used to seed this class' RNG). * * @param level a short[][] that should have been the spillMap of another MultiSpill * @param measurement a Spill.Measurement that should usually be MANHATTAN */ public MultiSpill(final short[][] level, Measurement measurement, IRNG random) { rng = new StatefulRNG(random.nextLong()); this.measurement = measurement; initialize(level); }
/** * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other * char[][] where '#' means a wall and anything else is a walkable tile. If you only have * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a * map that can be used here. This constructor specifies a distance measurement. * <br> * This constructor allows you to specify an RNG, but the actual RandomnessSource the RNG that this object uses will * not be identical to the one passed as random (64 bits will be requested from the passed RNG, and that will be * used to seed this class' RNG). * @param level a char[][] that should use '#' for walls and '.' for floors * @param measurement a Spill.Measurement that should usually be MANHATTAN * @param random an RNG that will be converted to a StatefulRNG if it is not one already */ public MultiSpill(final char[][] level, Measurement measurement, IRNG random) { rng = new StatefulRNG(random.nextLong()); this.measurement = measurement; initialize(level); }
/** * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG. * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the * expected average the kind was associated with. The returned number will be a positive long in either case, but * not all long values are possible if this is biased, in part because of generating a double, which has less * precision than long, and in part because some numbers need to be more common than others. If the kind is not in * the map, this generates a positive long, using 63 bits instead of RNG's normal 64 bits since it never generates * negative numbers. * @param kind the kind of bias to look up * @return a random 63-bit positive long, potentially influenced by the bias associated with kind, if present */ public long biasedLong(String kind) { Double d = biases.get(kind); if(d == null) return rng.nextLong() >>> 1; return (long)(quantile(d) * Long.MAX_VALUE); }
indexSections[rotated] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations(inner)), inner, build);
/** * Make a SectionDungeonGenerator with the given height, width, and RNG. Use this if you want to seed the RNG. If * width or height is greater than 256, then this will expand the Coord pool from its 256x256 default so it stores a * reference to each Coord that might be used in the creation of the dungeon (if width and height are 300 and 300, * the Coord pool will be 300x300; if width and height are 500 and 100, the Coord pool will be 500x256 because it * won't shrink below the default size of 256x256). * @param width The width of the dungeon in cells * @param height The height of the dungeon in cells * @param rng The RNG to use for all purposes in this class; if it is a StatefulRNG, then it will be used as-is, * but if it is not a StatefulRNG, a new StatefulRNG will be used, randomly seeded by this parameter */ public SectionDungeonGenerator(int width, int height, IRNG rng) { Coord.expandPoolTo(width, height); this.rng = (rng instanceof StatefulRNG) ? (StatefulRNG) rng : new StatefulRNG(rng.nextLong()); utility = new DungeonUtility(this.rng); rebuildSeed = this.rng.getState(); this.height = height; this.width = width; roomFX = new EnumMap<>(FillEffect.class); corridorFX = new EnumMap<>(FillEffect.class); caveFX = new EnumMap<>(FillEffect.class); }
for (int i = 0; i < indexSections.length - 1; i++) { indexSections[i] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations(portionSize)), portionSize, i * portionSize); sz2 -= portionSize; rng.nextLong(PermutationGenerator.getTotalPermutations((int)sz2)), (int)sz2, (indexSections.length - 1) * portionSize);
/** * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works. * @param elements a Collection of T that will not be modified */ public GapShuffler(Collection<T> elements) { rng = new RNG(new LongPeriodRNG()); this.elements = rng.shuffle(elements); size = this.elements.size(); double sz2 = size; index = 0; int portionSize = Math.min(20, Math.max(1, size / 2)); int minSection = Math.min(5, size / 2 + 1); while (size % portionSize < minSection && portionSize > 2) portionSize--; indexSections = new int[(int)Math.ceil(sz2 / portionSize)][]; for (int i = 0; i < indexSections.length - 1; i++) { indexSections[i] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations(portionSize)), portionSize, i * portionSize); sz2 -= portionSize; } indexSections[indexSections.length - 1] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations((int)sz2)), (int)sz2, (indexSections.length - 1) * portionSize); }
/** * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works. * @param elements a Collection of T that will not be modified */ public GapShuffler(Collection<T> elements, String seed) { rng = new RNG(new LongPeriodRNG(seed)); this.elements = rng.shuffle(elements); size = this.elements.size(); double sz2 = size; index = 0; int portionSize = Math.min(20, Math.max(1, size / 2)); int minSection = Math.min(5, size / 2 + 1); while (size % portionSize < minSection && portionSize > 2) portionSize--; indexSections = new int[(int)Math.ceil(sz2 / portionSize)][]; for (int i = 0; i < indexSections.length - 1; i++) { indexSections[i] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations(portionSize)), portionSize, i * portionSize); sz2 -= portionSize; } indexSections[indexSections.length - 1] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations((int)sz2)), (int)sz2, (indexSections.length - 1) * portionSize); }
/** * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works. * @param elements a Collection of T that will not be modified */ public GapShuffler(T[] elements, CharSequence seed) { rng = new RNG(new LongPeriodRNG(seed)); this.elements = Maker.makeList(rng.shuffle(elements)); size = this.elements.size(); double sz2 = size; index = 0; int portionSize = Math.min(20, Math.max(1, size / 2)); int minSection = Math.min(5, size / 2 + 1); while (size % portionSize < minSection && portionSize > 2) portionSize--; indexSections = new int[(int)Math.ceil(sz2 / portionSize)][]; for (int i = 0; i < indexSections.length - 1; i++) { indexSections[i] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations(portionSize)), portionSize, i * portionSize); sz2 -= portionSize; } indexSections[indexSections.length - 1] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations((int)sz2)), (int)sz2, (indexSections.length - 1) * portionSize); }
/** * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works. * @param elements a Collection of T that will not be modified */ public GapShuffler(T[] elements) { rng = new RNG(new LongPeriodRNG()); this.elements = Maker.makeList(rng.shuffle(elements)); size = this.elements.size(); double sz2 = size; index = 0; int portionSize = Math.min(20, Math.max(1, size / 2)); int minSection = Math.min(5, size / 2 + 1); while (size % portionSize < minSection && portionSize > 2) portionSize--; indexSections = new int[(int)Math.ceil(sz2 / portionSize)][]; for (int i = 0; i < indexSections.length - 1; i++) { indexSections[i] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations(portionSize)), portionSize, i * portionSize); sz2 -= portionSize; } indexSections[indexSections.length - 1] = PermutationGenerator.decodePermutation( rng.nextLong(PermutationGenerator.getTotalPermutations((int)sz2)), (int)sz2, (indexSections.length - 1) * portionSize); }
/** * Make a ModularMapGenerator with the given height, width, and RNG. Use this if you want to seed the RNG. * * @param width The width of the dungeon in cells * @param height The height of the dungeon in cells * @param rng The RNG to use for all purposes in this class; if it is any kind of IStatefulRNG, then it will be * used as-is; otherwise, a new GWTRNG will be used, randomly seeded by this parameter */ public ModularMapGenerator(int width, int height, IRNG rng) { CoordPacker.init(); this.rng = (rng instanceof IStatefulRNG) ? (IStatefulRNG) rng : new GWTRNG(rng.nextLong()); utility = new DungeonUtility(this.rng); rebuildSeed = this.rng.getState(); this.height = height; this.width = width; map = new char[width][height]; environment = new int[width][height]; for (int x = 0; x < this.width; x++) { Arrays.fill(map[x], '#'); } initModules(); }
/** * Make a DungeonGenerator with the given height, width, and RNG. Use this if you want to seed the RNG. If width or * height is greater than 256, then this will expand the Coord pool from its 256x256 default so it stores a * reference to each Coord that might be used in the creation of the dungeon (if width and height are 300 and 300, * the Coord pool will be 300x300; if width and height are 500 and 100, the Coord pool will be 500x256 because it * won't shrink below the default size of 256x256). * @param width The width of the dungeon in cells * @param height The height of the dungeon in cells * @param rng The RNG to use for all purposes in this class; if it is a StatefulRNG, then it will be used as-is, * but if it is not a StatefulRNG, a new StatefulRNG will be used, randomly seeded by this parameter */ public DungeonGenerator(int width, int height, IRNG rng) { Coord.expandPoolTo(width, height); this.rng = (rng instanceof IStatefulRNG) ? (IStatefulRNG) rng : new GWTRNG(rng.nextLong()); gen = new DungeonBoneGen(this.rng); utility = new DungeonUtility(this.rng); rebuildSeed = this.rng.getState(); this.height = height; this.width = width; fx = new EnumMap<>(FillEffect.class); }