001package squidpony.squidmath; 002 003import squidpony.Maker; 004 005import java.io.Serializable; 006import java.util.*; 007 008/** 009 * Meant to take a fixed-size set of items and produce a shuffled stream of them such that an element is never chosen in 010 * quick succession; that is, there should always be a gap between the same item's occurrences. This is an Iterable of 011 * T, not a Collection, because it can iterate without stopping, infinitely, unless you break out of a foreach loop that 012 * iterates through one of these, or call the iterator's next() method only a limited number of times. Collections have 013 * a size that can be checked, but Iterables can be infinite (and in this case, this one is). 014 * Created by Tommy Ettinger on 5/21/2016. 015 * @param <T> the type of items to iterate over; ideally, the items are unique 016 */ 017public class GapShuffler<T> implements Iterator<T>, Iterable<T>, Serializable { 018 private static final long serialVersionUID = 1L; 019 public IRNG rng; 020 private ArrayList<T> elements; 021 private int index; 022 private GapShuffler() {} 023 024 public GapShuffler(T single) 025 { 026 rng = new RNG(); 027 elements = new ArrayList<>(1); 028 elements.add(single); 029 index = 0; 030 } 031 /** 032 * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely 033 * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should 034 * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance 035 * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works. 036 * @param elements a Collection of T that will not be modified 037 */ 038 public GapShuffler(Collection<T> elements) 039 { 040 this(elements, new RNG(new LongPeriodRNG())); 041 042 } 043 044 /** 045 * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely 046 * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should 047 * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance 048 * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works. 049 * @param elements a Collection of T that will not be modified 050 */ 051 public GapShuffler(Collection<T> elements, String seed) 052 { 053 this(elements, new RNG(new LongPeriodRNG(seed))); 054 } 055 056 /** 057 * Constructor that takes any Collection of T, shuffles it with the given RNG, and can then iterate infinitely 058 * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should 059 * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance 060 * of a gap if every item in items is unique, but that is not necessary and does not affect how this works. The 061 * rng parameter is copied so externally using it won't change the order this produces its values; the rng field is 062 * used whenever the iterator needs to re-shuffle the internal ordering of items. I suggest that the IRNG should 063 * use {@link LongPeriodRNG} as its RandomnessSource, since it is in general a good choice for shuffling, or 064 * {@link XoshiroStarPhi32RNG} for GWT or other 32-bit platforms, but the choice is unlikely to matter in practice. 065 * @param items a Collection of T that will not be modified 066 * @param rng an IRNG that can be pre-seeded; will be copied and not used directly 067 */ 068 public GapShuffler(Collection<T> items, IRNG rng) 069 { 070 this.rng = rng.copy(); 071 elements = this.rng.shuffle(items); 072 index = 0; 073 } 074 075 /** 076 * Constructor that takes any Collection of T, shuffles it with the given RNG, and can then iterate infinitely 077 * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should 078 * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance 079 * of a gap if every item in items is unique, but that is not necessary and does not affect how this works. The 080 * rng parameter will be copied if {@code shareRNG} is true, otherwise the reference will be shared (which could 081 * make the results of this GapShuffler depend on outside code, though it will always maintain a gap between 082 * identical elements if the elements are unique). I suggest that the IRNG should use {@link LongPeriodRNG} as its 083 * RandomnessSource, since it is in general a good choice for shuffling, or {@link XoshiroStarPhi32RNG} for GWT or 084 * other 32-bit platforms, but the choice is unlikely to matter in practice. Any {@link IStatefulRNG} should work in 085 * most cases, like {@link GWTRNG}, {@link StatefulRNG}, or {@link SilkRNG}. If you encounter unusually-low-quality 086 * shuffles, try a different starting seed, and if that doesn't work, try SilkRNG (it has a different internal 087 * structure than the other types; it could yield better results at the very start). 088 * @param items a Collection of T that will not be modified 089 * @param rng an IRNG that can be pre-seeded; will be copied and not used directly 090 * @param shareRNG if false, {@code rng} will be copied and no reference will be kept; if true, {@code rng} will be shared with the outside code 091 */ 092 public GapShuffler(Collection<T> items, IRNG rng, boolean shareRNG) 093 { 094 this.rng = shareRNG ? rng : rng.copy(); 095 elements = this.rng.shuffle(items); 096 index = 0; 097 } 098 099 /** 100 * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely 101 * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should 102 * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance 103 * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works. 104 * @param elements a Collection of T that will not be modified 105 */ 106 public GapShuffler(T[] elements) 107 { 108 this(elements, new RNG(new LongPeriodRNG())); 109 } 110 111 /** 112 * Constructor that takes any Collection of T, shuffles it with an unseeded RNG, and can then iterate infinitely 113 * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should 114 * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance 115 * of a gap if every item in elements is unique, but that is not necessary and does not affect how this works. 116 * @param elements a Collection of T that will not be modified 117 */ 118 public GapShuffler(T[] elements, CharSequence seed) 119 { 120 this(elements, new RNG(new LongPeriodRNG(seed))); 121 } 122 123 /** 124 * Constructor that takes any Collection of T, shuffles it with the given RNG, and can then iterate infinitely 125 * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should 126 * always have a large amount of "gap" in order between one appearance and the next. It helps to keep the appearance 127 * of a gap if every item in items is unique, but that is not necessary and does not affect how this works. The 128 * rng parameter is copied so externally using it won't change the order this produces its values; the rng field is 129 * used whenever the iterator needs to re-shuffle the internal ordering of items. I suggest that the IRNG should 130 * use {@link LongPeriodRNG} as its RandomnessSource, since it is in general a good choice for shuffling, or 131 * {@link XoshiroStarPhi32RNG} for GWT or other 32-bit platforms, but the choice is unlikely to matter in practice. 132 * @param items a Collection of T that will not be modified 133 * @param rng an IRNG that can be pre-seeded; will be copied and not used directly 134 */ 135 public GapShuffler(T[] items, IRNG rng) 136 { 137 this.rng = rng.copy(); 138 elements = Maker.makeList(items); 139 this.rng.shuffleInPlace(elements); 140 index = 0; 141 } 142 143 /** 144 * Constructor that takes any Collection of T, shuffles it with the given RNG, and can then iterate infinitely 145 * through mostly-random shuffles of the given collection. These shuffles are spaced so that a single element should 146 * always have at least one "gap" element between one appearance and the next. It helps to keep the appearance 147 * of a gap if every item in items is unique, but that is not necessary and does not affect how this works. The 148 * rng parameter will be copied if {@code shareRNG} is false, otherwise the reference will be shared (which could 149 * make the results of this GapShuffler depend on outside code, though it will always maintain a gap between 150 * identical elements if the elements are unique). I suggest that the IRNG should use {@link LongPeriodRNG} as its 151 * RandomnessSource, since it is in general a good choice for shuffling, or {@link XoshiroStarPhi32RNG} for GWT or 152 * other 32-bit platforms, but the choice is unlikely to matter in practice. Any {@link IStatefulRNG} should work in 153 * most cases, like {@link GWTRNG}, {@link StatefulRNG}, or {@link SilkRNG}. If you encounter unusually-low-quality 154 * shuffles, try a different starting seed, and if that doesn't work, try SilkRNG (it has a different internal 155 * structure than the other types; it could yield better results at the very start). 156 * @param items a Collection of T that will not be modified 157 * @param rng an IRNG that can be pre-seeded; will be copied and not used directly 158 * @param shareRNG if false, {@code rng} will be copied and no reference will be kept; if true, {@code rng} will be shared with the outside code 159 */ 160 public GapShuffler(T[] items, IRNG rng, boolean shareRNG) 161 { 162 this.rng = shareRNG ? rng : rng.copy(); 163 elements = Maker.makeList(items); 164 this.rng.shuffleInPlace(elements); 165 index = 0; 166 } 167 168 /** 169 * Gets the next element of the infinite sequence of T this shuffles through. This class can be used as an 170 * Iterator or Iterable of type T. 171 * @return the next element in the infinite sequence 172 */ 173 public T next() { 174 int size = elements.size(); 175 if(size == 1) 176 { 177 return elements.get(0); 178 } 179 if(index >= size) 180 { 181 final int n = size - 1; 182 for (int i = n; i > 1; i--) { 183 Collections.swap(elements, rng.nextSignedInt(i), i - 1); 184 } 185 Collections.swap(elements, 1 + rng.nextSignedInt(n), n); 186 index = 0; 187 } 188 return elements.get(index++); 189 } 190 /** 191 * Returns {@code true} if the iteration has more elements. 192 * This is always the case for GapShuffler. 193 * 194 * @return {@code true} always 195 */ 196 @Override 197 public boolean hasNext() { 198 return true; 199 } 200 /** 201 * Not supported. 202 * 203 * @throws UnsupportedOperationException always throws this exception 204 */ 205 @Override 206 public void remove() { 207 throw new UnsupportedOperationException("remove() is not supported"); 208 } 209 210 /** 211 * Returns an <b>infinite</b> iterator over elements of type {@code T}; the returned iterator is this object. 212 * You should be prepared to break out of any for loops that use this once you've gotten enough elements! 213 * The remove() method is not supported by this iterator and hasNext() will always return true. 214 * 215 * @return an infinite Iterator over elements of type T. 216 */ 217 @Override 218 public Iterator<T> iterator() { 219 return this; 220 } 221 222 public IRNG getRNG() { 223 return rng; 224 } 225 226 227 /** 228 * Sets the IRNG this uses to shuffle the order of elements, always copying the given IRNG before using it. Always 229 * reshuffles the order, which may eliminate a gap that should have been present, so treat the sequence before and 230 * after like separate GapShuffler objects. 231 * @param rng an IRNG, such as {@link RNG}, {@link GWTRNG}, {@link StatefulRNG}, and so on; always copied 232 */ 233 public void setRNG(IRNG rng) { 234 setRNG(rng, false); 235 } 236 237 /** 238 * Sets the IRNG this uses to shuffle the order of elements, optionally sharing a reference between outside code and 239 * the internal rng (when {@code shareRNG} is true). Always reshuffles the order, which may eliminate a gap that 240 * should have been present, so treat the sequence before and after like separate GapShuffler objects. 241 * @param rng an IRNG, such as {@link RNG}, {@link GWTRNG}, {@link StatefulRNG}, and so on 242 * @param shareRNG if false, {@code rng} will be copied and no reference will be kept; if true, {@code rng} will be shared with the outside code 243 */ 244 public void setRNG(IRNG rng, boolean shareRNG) { 245 this.rng = shareRNG ? rng : rng.copy(); 246 this.rng.shuffleInPlace(elements); 247 } 248 249 /** 250 * The internal items used here are private, but you can still use this method to put a shallow copy of them into 251 * some other Collection. If the type {@code T} is mutable, changes to individual items will be reflected in this 252 * GapShuffler, so be careful in that case (if T is immutable, like if it is String, then there's nothing to need to 253 * be careful about). This copies each item in the GapShuffler's sequence once, in no particular order, but it may 254 * give a prediction of what items this will return in the future (or has already returned). 255 * @param coll a Collection that will have each of the possible items this can produce added into it, in no particular order 256 */ 257 public void fillInto(Collection<T> coll) { 258 coll.addAll(elements); 259 } 260 261}