001package squidpony.squidmath; 002 003import squidpony.StringKit; 004 005import java.io.Serializable; 006 007/** 008 * A UUID-like identifier; not compatible with Java's standard UUID but will work on GWT. 009 * <br> 010 * Meant to be used as an identity type for things like SpatialMap, especially when no special game-specific logic is 011 * needed for identities. 012 * Created by Tommy Ettinger on 4/30/2016. 013 */ 014public class SquidID implements Serializable, Comparable<SquidID> { 015 private static final long serialVersionUID = 8946534790126874460L; 016 private static LongPeriodRNG rng = new LongPeriodRNG(); 017 public final long low, high; 018 019 /** 020 * Constructs a new random SquidID. If you want different random IDs with every run, the defaults should be fine. 021 * If you want stable IDs to be generated, use SquidID.stabilize(), but be careful about collisions! 022 */ 023 public SquidID() { 024 low = rng.nextLong(); 025 high = rng.nextLong(); 026 } 027 028 /** 029 * Constructs a fixed SquidID with the given low and high 64-bit longs. 030 * @param low the least-significant bits of the ID 031 * @param high the most-significant bits of the ID 032 */ 033 public SquidID(long low, long high) { 034 this.low = low; 035 this.high = high; 036 } 037 038 /** 039 * Gets a new random SquidID, the same as calling the no-argument constructor. 040 * The name is for compatibility with Java's standard UUID class. 041 * @return a newly-constructed random SquidID. 042 */ 043 public static SquidID randomUUID() 044 { 045 return new SquidID(); 046 } 047 048 /** 049 * Makes the IDs generated after calling this repeatable, with the same IDs generated in order after this is called. 050 * This class uses a random number generator with a random seed by default to produce IDs, and properties of the 051 * LongPeriodRNG this uses make it incredibly unlikely that IDs will repeat even if the game was run for the rest 052 * of your natural lifespan. For the purposes of tests, you may want stable SquidID values to be generated, the same 053 * for every startup of the program, generating the same IDs in order. This will change the seed used internally to 054 * a constant (large) seed the first time it is called, and it should only be called at or near the start of your 055 * program, no more than once. If an ID is requested immediately after calling this method, and then this method is 056 * called again, the next ID to be generated will be identical to the previous one generated (a collision). There 057 * may be reasons you want this during testing, so there isn't any check for multiple calls to this method. If IDs 058 * can persist between runs of the game (i.e. saved in a file), using this is generally a bad idea, and the default 059 * random IDs should more than suffice. 060 * <br> 061 * You can "undo" the effects of this method with randomize(), changing the seed to a new random value. 062 * <br> 063 * Because IDs aren't likely to have gameplay significance, this uses one seed, the opening paragraph of The 064 * Wonderful Wizard of Oz, by Frank L. Baum, which is in the public domain. Changing the seed is unlikely to change 065 * the likelihood of collisions, which should be less likely than a tornado transporting you to Oz, as long as this 066 * method is called at most once per program run. 067 */ 068 public static void stabilize() 069 { 070 rng.reseed( 071 "Dorothy lived in the midst of the great Kansas prairies, with Uncle Henry, who was a "+ 072 "farmer, and Aunt Em, who was the farmer's wife. Their house was small, for the "+ 073 "lumber to build it had to be carried by wagon many miles. There were four walls, "+ 074 "a floor and a roof, which made one room; and this room contained a rusty looking "+ 075 "cookstove, a cupboard for the dishes, a table, three or four chairs, and the beds."+ 076 " Uncle Henry and Aunt Em had a big bed in one corner, and Dorothy a little bed in "+ 077 "another corner. There was no garret at all, and no cellar—except a small hole dug "+ 078 "in the ground, called a cyclone cellar, where the family could go in case one of "+ 079 "those great whirlwinds arose, mighty enough to crush any building in its path. It "+ 080 "was reached by a trap door in the middle of the floor, from which a ladder led "+ 081 "down into the small, dark hole."); 082 } 083 084 /** 085 * Makes the IDs generated after calling this non-repeatable, with a random 1024-bit seed. 086 * This class uses a random number generator with a random seed by default to produce IDs, and properties of the 087 * LongPeriodRNG this uses make it incredibly unlikely that IDs will repeat even if the game was run for the rest 088 * of your natural lifespan. However, if you call stabilize(), generate some IDs, call stabilize() again, and 089 * generate some more IDs, the first, second, third, etc. IDs generated after each call will be identical -- hardly 090 * the unique ID you usually want. You can "undo" the effects of stabilize by calling this method, making the seed 091 * a new random value. This does not affect the constructor that takes two longs to produce an exact ID, nor will 092 * it change any IDs. 093 */ 094 public static void randomize() 095 { 096 rng.reseed(); 097 } 098 099 /** 100 * Gets the least-significant bits, also accessible by the field low. 101 * The name is for compatibility with Java's standard UUID class. 102 * @return the least-significant bits as a long 103 */ 104 public long getLeastSignificantBits() 105 { 106 return low; 107 } 108 109 /** 110 * Gets the most-significant bits, also accessible by the field high. 111 * The name is for compatibility with Java's standard UUID class. 112 * @return the most-significant bits as a long 113 */ 114 public long getMostSignificantBits() 115 { 116 return high; 117 } 118 119 @Override 120 public boolean equals(Object o) { 121 if (this == o) return true; 122 if (o == null || getClass() != o.getClass()) return false; 123 124 SquidID squidID = (SquidID) o; 125 126 if (low != squidID.low) return false; 127 return high == squidID.high; 128 129 } 130 131 @Override 132 public int hashCode() { 133 int result = (int) (low ^ (low >>> 32)); 134 result = 31 * result + (int) (high ^ (high >>> 32)); 135 return result; 136 } 137 138 @Override 139 public String toString() 140 { 141 return StringKit.hex(high) + '-' + StringKit.hex(low); 142 } 143 144 @Override 145 public int compareTo(SquidID o) { 146 if(o == null) 147 return 1; 148 long diff = high - o.high; 149 if(diff == 0) 150 diff = low - o.low; 151 return diff > 0 ? 1 : diff < 0 ? -1 : 0; 152 } 153}