001package squidpony; 002 003import regexodus.*; 004import squidpony.squidmath.*; 005 006import java.io.Serializable; 007import java.util.ArrayList; 008import java.util.Collection; 009import java.util.Iterator; 010import java.util.Map; 011 012import static squidpony.Maker.makeList; 013import static squidpony.Maker.makeOM; 014 015/** 016 * A text processing class that can swap out occurrences of words and replace them with their synonyms. 017 * Created by Tommy Ettinger on 5/23/2016. 018 */ 019public class Thesaurus implements Serializable{ 020 private static final long serialVersionUID = 3387639905758074640L; 021 protected static final Pattern wordMatch = Pattern.compile("([\\pL`]+|@)"), 022 similarFinder = Pattern.compile(".*?\\b(\\w\\w\\w\\w).*?{\\@1}.*$", "ui"); 023 public OrderedMap<CharSequence, GapShuffler<String>> mappings; 024 public ArrayList<FakeLanguageGen.Alteration> alterations = new ArrayList<>(4); 025 public SilkRNG rng; 026 protected GapShuffler<String> plantTermShuffler, fruitTermShuffler, nutTermShuffler, flowerTermShuffler, 027 potionTermShuffler; 028 public FakeLanguageGen defaultLanguage = FakeLanguageGen.SIMPLISH; 029 public transient ArrayList<FakeLanguageGen> randomLanguages = new ArrayList<>(2); 030 public transient String latestGenerated = "Nationia"; 031 /** 032 * Constructs a new Thesaurus with an unseeded RNG used to shuffle word order. 033 */ 034 public Thesaurus() 035 { 036 mappings = new OrderedMap<>(256, Hashers.caseInsensitiveStringHasher); 037 rng = new SilkRNG(); 038 addKnownCategories(); 039 } 040 041 /** 042 * Constructs a new Thesaurus, seeding its RNG (used to shuffle word order) with the next long from the given RNG. 043 * @param rng an RNG that will only be used to get one long (for seeding this class' RNG) 044 */ 045 public Thesaurus(IRNG rng) 046 { 047 this(rng.nextLong()); 048 } 049 050 /** 051 * Constructs a new Thesaurus, seeding its RNG (used to shuffle word order) with shuffleSeed. 052 * @param shuffleSeed a long for seeding this class' RNG 053 */ 054 public Thesaurus(long shuffleSeed) 055 { 056 mappings = new OrderedMap<>(256, Hashers.caseInsensitiveStringHasher); 057 this.rng = new SilkRNG(shuffleSeed); 058 addKnownCategories(); 059 } 060 061 062 /** 063 * Constructs a new Thesaurus, seeding its RNG (used to shuffle word order) with shuffleSeed. 064 * @param shuffleSeed a String for seeding this class' RNG 065 */ 066 public Thesaurus(String shuffleSeed) 067 { 068 this(CrossHash.hash64(shuffleSeed)); 069 } 070 071 /** 072 * Changes the sequences for all groups of synonyms this can produce, effectively turning this Thesaurus into a 073 * different version that knows all the same synonyms and categories but will produce different results. 074 * @param state any long; will be given to {@link SilkRNG#setState(long)} 075 */ 076 public void refresh(long state) 077 { 078 refresh((int)(state & 0xFFFFFFFFL), (int)(state >>> 32)); 079 } 080 081 /** 082 * Changes the sequences for all groups of synonyms this can produce, effectively turning this Thesaurus into a 083 * different version that knows all the same synonyms and categories but will produce different results. 084 * @param stateA any int; the first part of the state of a {@link SilkRNG} 085 * @param stateB any int; the second part of the state of a {@link SilkRNG} 086 */ 087 public void refresh(int stateA, int stateB) 088 { 089 this.rng.setState(stateA, stateB); 090 final int sz = mappings.size(); 091 for (int i = 0; i < sz; i++) { 092 mappings.getAt(i).setRNG(rng, true); 093 } 094 } 095 096 public Thesaurus addReplacement(CharSequence before, String after) 097 { 098 mappings.put(before, new GapShuffler<>(after)); 099 return this; 100 } 101 102 /** 103 * Allows this Thesaurus to replace a specific keyword, typically containing multiple backtick characters 104 * ({@code `}) so it can't be confused with a "real word," with one of the words in synonyms (chosen in shuffled 105 * order). The backtick is the only punctuation character that this class' word matcher considers part of a word, 106 * both for this reason and because it is rarely used in English text. 107 * @param keyword a word (typically containing backticks, {@code `}) that will be replaced by a word from synonyms 108 * @param synonyms a Collection of lower-case Strings with similar meaning and the same part of speech 109 * @return this for chaining 110 */ 111 public Thesaurus addCategory(String keyword, Collection<String> synonyms) 112 { 113 if(synonyms.isEmpty()) 114 return this; 115 GapShuffler<String> shuffler = new GapShuffler<>(synonyms, rng, true); 116 mappings.put(keyword, shuffler); 117 return this; 118 } 119 120 /** 121 * Adds several pre-made categories to this Thesaurus' known categories, but won't cause it to try to replace normal 122 * words with synonyms (only categories, which contain backticks in the name). The keywords this currently knows, 123 * and the words it will replace those keywords with, are: 124 * <br> 125 * <ul> 126 * <li>calm`adj` : calm, harmonious, peaceful, placid, pleasant, serene, tranquil</li> 127 * <li>calm`noun` : calm, harmony, kindness, peace, serenity, tranquility</li> 128 * <li>org`noun` : association, brotherhood, fellowship, foundation, fraternity, group, guild, order, partnership</li> 129 * <li>org`nouns` : associations, brotherhoods, fellowships, foundations, fraternities, groups, guilds, orders, partnerships</li> 130 * <li>empire`adj` : ascendant, dynastic, emir's, hegemonic, imperial, king's, lordly, monarchic, prince's, regal, royal, sultan's</li> 131 * <li>empire`noun` : ascendancy, commonwealth, dominion, dynasty, emirate, empire, hegemony, imperium, kingdom, monarchy, sultanate, triumvirate</li> 132 * <li>empire`nouns` : ascendancies, commonwealths, dominions, dynasties, emirates, empires, hegemonies, imperia, kingdoms, monarchies, sultanates, triumvirates</li> 133 * <li>emperor`noun` : emir, emperor, king, lord, pharaoh, ruler, sultan</li> 134 * <li>emperor`nouns` : emirs, emperors, kings, lords, pharaohs, rulers, sultans</li> 135 * <li>empress`noun` : emira, empress, lady, pharaoh, queen, ruler, sultana</li> 136 * <li>empress`nouns` : emiras, empresses, ladies, pharaohs, queens, rulers, sultanas</li> 137 * <li>union`adj` : allied, associated, confederated, congressional, democratic, federated, independent, people's, unified, united</li> 138 * <li>union`noun` : alliance, coalition, confederacy, confederation, congress, faction, federation, league, republic, union</li> 139 * <li>union`nouns` : alliances, coalitions, confederacies, confederations, congresses, factions, federations, leagues, republics, unions</li> 140 * <li>militia`noun` : fighters, front, irregulars, liberators, militants, militia, rebellion, resistance, warriors</li> 141 * <li>militia`nouns` : fighters, fronts, irregulars, liberators, militants, militias, rebellions, resistances, warriors</li> 142 * <li>gang`noun` : cartel, crew, gang, mafia, mob, posse, syndicate</li> 143 * <li>gang`nouns` : cartels, crews, gangs, mafias, mobs, posses, syndicates</li> 144 * <li>duke`noun` : baron, duke, earl, fief, lord, shogun</li> 145 * <li>duke`nouns` : barons, dukes, earls, fiefs, lords, shoguns</li> 146 * <li>duchy`noun` : barony, duchy, earldom, fiefdom, lordship, shogunate</li> 147 * <li>duchy`nouns` : baronies, duchies, earldoms, fiefdoms, lordships, shogunates</li> 148 * <li>magical`adj` : arcane, enchanted, ensorcelled, magical, mystical, sorcerous</li> 149 * <li>holy`adj` : auspicious, blessed, divine, godly, holy, prophetic, sacred, virtuous</li> 150 * <li>priest`noun` : bishop, cardinal, chaplain, cleric, preacher, priest</li> 151 * <li>priest`nouns` : bishops, cardinals, chaplains, clergy, preachers, priests</li> 152 * <li>unholy`adj` : accursed, bewitched, macabre, occult, profane, unholy, vile</li> 153 * <li>witch`noun` : cultist, defiler, necromancer, occultist, warlock, witch</li> 154 * <li>witch`nouns` : cultists, defilers, necromancers, occultists, warlocks, witches</li> 155 * <li>forest`adj` : bountiful, fertile, lush, natural, primal, verdant</li> 156 * <li>forest`noun` : copse, forest, glen, greenery, grove, jungle, nature, woodland</li> 157 * <li>shaman`noun` : animist, druid, shaman, warden</li> 158 * <li>shaman`nouns` : animists, druids, shamans, wardens</li> 159 * <li>fancy`adj` : glorious, grand, great, magnanimous, magnificent, majestic, powerful</li> 160 * <li>evil`adj` : abhorrent, cruel, debased, evil, heinous, horrible, malevolent, nefarious, scurrilous, terrible, vile, wicked</li> 161 * <li>villain`noun` : blasphemer, evildoer, killer, knave, monster, murderer, villain</li> 162 * <li>villain`nouns` : blasphemers, evildoers, killers, knaves, monsters, murderers, villains</li> 163 * <li>monster`noun` : abomination, beast, creature, demon, devil, fiend, ghoul, monster</li> 164 * <li>monsters`nouns` : abominations, beasts, creatures, demons, devils, fiends, ghouls, monsters</li> 165 * <li>good`adj` : compassionate, flawless, good, kind, moral, perfect, pure, righteous</li> 166 * <li>lethal`adj` : bloodstained, cutthroat, deadly, fatal, lethal, murderous, poisonous, silent, stalking, venomous</li> 167 * <li>lethal`noun` : assassin, blood, killer, murder, ninja, poison, razor, silence, slayer, snake, tiger, venom</li> 168 * <li>blade`noun` : axe, blade, cutlass, flail, glaive, halberd, hammer, hatchet, katana, knife, lance, mace, maul, nunchaku, saber, scimitar, scythe, sickle, spear, stiletto, sword, trident, whip</li> 169 * <li>bow`noun` : atlatl, bolas, bow, crossbow, dagger, javelin, longbow, net, shortbow, shuriken, sling</li> 170 * <li>weapon`noun` : atlatl, axe, blade, bolas, bow, crossbow, cutlass, dagger, flail, glaive, halberd, hammer, hatchet, javelin, katana, knife, lance, longbow, mace, maul, net, nunchaku, saber, scimitar, scythe, shortbow, shuriken, sickle, sling, spear, stiletto, sword, trident, whip</li> 171 * <li>musket`noun` : arquebus, blunderbuss, cannon, flintlock, matchlock, musket, wheellock</li> 172 * <li>grenade`noun` : bomb, explosive, flamethrower, grenade, missile, rocket, warhead</li> 173 * <li>rifle`noun` : firearm, handgun, longarm, pistol, rifle, shotgun</li> 174 * <li>blade`nouns` : axes, blades, cutlasses, flails, glaives, halberds, hammers, hatchets, katana, knives, lances, maces, mauls, nunchaku, sabers, scimitars, scythes, sickles, spears, stilettos, swords, tridents, whips</li> 175 * <li>bow`nouns` : atlatls, bolases, bows, crossbows, daggers, javelins, longbows, nets, shortbows, shuriken, slings</li> 176 * <li>weapon`nouns` : atlatls, axes, blades, bolases, bows, crossbows, cutlasses, daggers, flails, glaives, halberds, hammers, hatchets, javelins, katana, knives, lances, longbows, maces, mauls, nets, nunchaku, sabers, scimitars, scythes, shortbows, shuriken, sickles, slings, spears, stilettos, swords, tridents, whips</li> 177 * <li>musket`nouns` : arquebusses, blunderbusses, cannons, flintlocks, matchlocks, muskets, wheellocks</li> 178 * <li>grenade`nouns` : bombs, explosives, flamethrowers, grenades, missiles, rockets, warheads</li> 179 * <li>rifle`nouns` : firearms, handguns, longarms, pistols, rifles, shotguns</li> 180 * <li>scifi`adj` : genetic, gravitational, laser, nanoscale, phase, photonic, plasma, quantum, tachyonic, warp</li> 181 * <li>tech`adj` : crypto, cyber, digital, electronic, hacker, mechanical, servo, techno, turbo</li> 182 * <li>sole`adj` : final, last, singular, sole, total, true, ultimate</li> 183 * <li>light`adj` : bright, gleaming, glowing, luminous, lunar, radiant, shimmering, solar, stellar</li> 184 * <li>light`noun` : dawn, gleam, glow, light, moon, radiance, shimmer, star, sun, torch</li> 185 * <li>light`nouns` : glimmers, lights, moons, stars, suns, torches</li> 186 * <li>shadow`noun` : blackness, darkness, gloom, murk, shadow, twilight</li> 187 * <li>shadow`nouns` : blackness, darkness, gloom, murk, shadows, twilight</li> 188 * <li>fire`noun` : blaze, conflagration, fire, flame, inferno, pyre</li> 189 * <li>fire`nouns` : blazes, conflagrations, fires, flames, infernos, pyres</li> 190 * <li>ice`noun` : blizzard, chill, cold, frost, ice, snow</li> 191 * <li>ice`nouns` : blizzards, chills, cold, frosts, ice, snow</li> 192 * <li>lightning`noun` : lightning, shock, spark, storm, thunder, thunderbolt</li> 193 * <li>lightning`nouns` : lightning, shocks, sparks, storms, thunder, thunderbolts</li> 194 * <li>ground`noun` : clay, dirt, earth, loam, mud, peat, sand, soil</li> 195 * <li>lake`noun` : bog, fen, glade, lake, pond, puddle, sea, swamp</li> 196 * <li>leaf`noun` : bark, blossom, branch, bud, cress, flower, leaf, root, sap, seed, shoot, stalk, stem, thorn, twig, vine, wood, wort</li> 197 * <li>fruit`noun` : apple, banana, berry, cherry, date, fig, fruit, grape, juniper, lime, mango, melon, papaya, peach, pear, quince</li> 198 * <li>nut`noun` : almond, bean, cashew, chestnut, hazelnut, nut, pea, peanut, pecan, walnut</li> 199 * <li>flower`noun` : amaryllis, camellia, chrysanthemum, daisy, dandelion, flower, gardenia, hibiscus, jasmine, lantana, lilac, lily, lotus, mallow, oleander, orchid, peony, petunia, phlox, rose, tulip</li> 200 * <li>tree`noun` : alder, beech, birch, cactus, cedar, elm, hazel, juniper, larch, magnolia, mangrove, maple, oak, palm, pine, tree, willow</li> 201 * <li>flavor`noun` : acid, grease, herb, salt, smoke, spice, sugar</li> 202 * <li>flavor`adj` : bitter, salty, savory, smoky, sour, spicy, sweet</li> 203 * <li>color`adj` : black, blue, brown, gray, green, orange, red, violet, white, yellow</li> 204 * <li>shape`adj` : delicate, drooping, fibrous, fragile, giant, hardy, hollow, long, miniature, spiny, stiff, stubby, sturdy, thorny, tufted, yielding</li> 205 * <li>sensory`adj` : aromatic, fragrant, fuzzy, glossy, pungent, rough, rustling, smooth, soft, weeping</li> 206 * <li>liquid`noun` : brew, broth, elixir, fluid, liquid, potion, serum, tonic</li> 207 * <li>liquid`adj` : bubbling, congealing, effervescent, milky, murky, slimy, sloshing, swirling, thick</li> 208 * <li>bottle`noun` : bottle, canister, flagon, flask, jug, phial, vial</li> 209 * <li>bottle`adj` : brown glass, clear glass, curvaceous glass, dull pewter, fluted crystal, green glass, rough-cut glass, sharp-edged tin, shining silver, smoky glass, tarnished silver</li> 210 * <li>calabash`adj` : calabash, hollow gourd, milk carton, waterskin, wineskin</li> 211 * <li>smart`adj` : aware, brilliant, clever, cunning, genius, mindful, smart, wise</li> 212 * <li>smart`noun` : acumen, awareness, cunning, genius, knowledge, mindfulness, smarts, wisdom</li> 213 * <li>stupid`adj` : careless, complacent, dull, dumb, foolish, idiotic, moronic, reckless, sloppy, stupid</li> 214 * <li>stupid`noun` : carelessness, complacency, foolishness, idiocy, recklessness, sloppiness, stupidity</li> 215 * <li>bandit`noun` : bandit, brigand, highwayman, pirate, raider, rogue, thief</li> 216 * <li>bandit`nouns` : bandits, brigands, highwaymen, pirates, raiders, rogues, thieves</li> 217 * <li>soldier`noun` : combatant, fighter, mercenary, soldier, trooper, warrior</li> 218 * <li>soldier`nouns` : combatants, fighters, mercenaries, soldiers, troops, warriors</li> 219 * <li>guard`noun` : defender, guard, guardian, knight, paladin, protector, sentinel, shield, templar, warden, watchman</li> 220 * <li>guard`nouns` : defenders, guardians, guards, knights, paladins, protectors, sentinels, shields, templars, wardens, watchmen</li> 221 * <li>hunter`noun` : hunter, poacher, stalker, tracker, trapper, warden</li> 222 * <li>explorer`noun` : explorer, nomad, pathfinder, questant, seeker, wanderer</li> 223 * <li>hunter`nouns` : hunters, poachers, stalkers, trackers, trappers, wardens</li> 224 * <li>explorer`nouns` : explorers, nomads, pathfinders, questants, seekers, wanderers</li> 225 * <li>rage`noun` : anger, frenzy, fury, rage, vengeance, wrath</li> 226 * <li>ominous`adj` : baleful, fateful, foreboding, ominous, portentous</li> 227 * <li>many`adj` : countless, infinite, manifold, many, myriad, thousandfold, unlimited</li> 228 * <li>impossible`adj` : abominable, forbidden, impossible, incomprehensible, indescribable, ineffable, unearthly, unspeakable</li> 229 * <li>gaze`noun` : eye, gaze, observation, purveyance, stare, watch</li> 230 * <li>pain`noun` : agony, excruciation, misery, pain, torture</li> 231 * <li>god`noun` : deity, father, god, king, lord, lordship, ruler</li> 232 * <li>goddess`noun` : deity, goddess, lady, ladyship, mother, queen, ruler</li> 233 * <li>hero`noun` : champion, crusader, hero, knight, savior</li> 234 * <li>heroes`nouns` : champions, crusaders, heroes, knights, saviors</li> 235 * <li>heroine`noun` : champion, crusader, heroine, knight, maiden, savior</li> 236 * <li>heroines`nouns` : champions, crusaders, heroines, knights, maidens, saviors</li> 237 * <li>popular`adj` : adored, beloved, revered, worshipped</li> 238 * <li>unpopular`adj` : despised, hated, loathed, reviled</li> 239 * <li>glyph`noun` : glyph, mark, seal, sigil, sign, symbol</li> 240 * <li>glyph`nouns` : glyphs, marks, seals, sigils, signs, symbols</li> 241 * <li>power`noun` : authority, dominance, force, potency, power, strength</li> 242 * <li>power`adj` : authoritative, dominant, forceful, potent, powerful, strong</li> 243 * </ul> 244 * There are also terms, which typically produce multiple words procedurally and may use {@link #defaultLanguage}. 245 * See {@link #makePlantName()}, {@link #makeFruitName()}, {@link #makeNutName()}, {@link #makeFlowerName()}, and 246 * {@link #makePotionDescription()} for more info and examples. 247 * <li> 248 * <li>plant`term` : @'s color`adj` leaf`noun`, @'s color`adj` flower`noun`, @'s color`adj` tree`noun`, @'s flower`noun`, @'s ground`noun` leaf`noun`, @'s sensory`adj`-leaf`noun`, @'s shape`adj` flower`noun`, @'s tree`noun`, color`adj` flower`noun`, color`adj` flower`noun` of @, color`adj` fruit`noun` tree`noun`, color`adj` nut`noun` tree`noun`, color`adj`-leaf`noun` flower`noun`, flavor`adj` fruit`noun` tree`noun`, flavor`adj` nut`noun` tree`noun`, flavor`noun` leaf`noun` tree`noun`, flower`noun` of @, ground`noun` flower`noun`, ground`noun` leaf`noun`, ground`noun` leaf`noun` of @, leaf`noun` of @, sensory`adj` flower`noun` of @, sensory`adj` flower`noun`-flower`noun`, sensory`adj` tree`noun` of @, sensory`adj` tree`noun`-tree`noun`, sensory`adj`-leaf`noun` flower`noun`, sensory`adj`-leaf`noun` tree`noun`, shape`adj` flower`noun`, shape`adj`-fruit`noun` tree`noun`, shape`adj`-leaf`noun` flower`noun`, shape`adj`-leaf`noun` tree`noun`</li> 249 * <li>fruit`term` : @'s color`adj` fruit`noun`, @'s flavor`adj` fruit`noun`, @'s fruit`noun`, color`adj` fruit`noun`-fruit`noun`, flavor`adj` fruit`noun`-fruit`noun`, fruit`noun` of @</li> 250 * <li>nut`term` : @'s color`adj` nut`noun`, @'s flavor`adj` nut`noun`, @'s nut`noun`, color`adj` nut`noun`, color`adj` nut`noun` of @, flavor`adj` nut`noun`, nut`noun` of @, sensory`adj` nut`noun`</li> 251 * <li>flower`term` : @'s color`adj` flower`noun`, @'s flower`noun`, @'s shape`adj` flower`noun`, color`adj` flower`noun`, color`adj` flower`noun` of @, color`adj`-leaf`noun` flower`noun`, flower`noun` of @, ground`noun` flower`noun`, sensory`adj` flower`noun` of @, sensory`adj` flower`noun`-flower`noun`, sensory`adj`-leaf`noun` flower`noun`, shape`adj` flower`noun`, shape`adj`-leaf`noun` flower`noun`</li> 252 * <li>potion`term` : a bottle`adj` bottle`noun` containing a few drops of a color`adj` liquid`noun`, a bottle`adj` bottle`noun` filled with a color`adj` liquid`noun`, a bottle`adj` bottle`noun` filled with a liquid`adj` color`adj` liquid`noun`, a bottle`adj` bottle`noun` half-filled with a liquid`adj` color`adj` liquid`noun`, a calabash`adj` filled with a color`adj` liquid`noun`</li> 253 * </ul> 254 * Capitalizing the first letter in the keyword where it appears in text you call process() on will capitalize the 255 * first letter of the produced fake word. Capitalizing the second letter will capitalize the whole produced fake 256 * word. This applies only per-instance of each keyword; it won't change the internally-stored list of words. 257 * @return this for chaining 258 */ 259 public Thesaurus addKnownCategories() 260 { 261 for(Map.Entry<String, ArrayList<String>> kv : categories.entrySet()) 262 { 263 addCategory(kv.getKey(), kv.getValue()); 264 } 265 plantTermShuffler = mappings.get("plant`term`"); 266 fruitTermShuffler = mappings.get("fruit`term`"); 267 nutTermShuffler = mappings.get("nut`term`"); 268 flowerTermShuffler = mappings.get("flower`term`"); 269 potionTermShuffler = mappings.get("potion`term`"); 270 return this; 271 } 272 273 /** 274 * Given an archive String saved by {@link #archiveCategories()} (probably from another version of SquidLib), this 275 * makes the Thesaurus class act like it did in that archive, assuming the {@link #rng} is seeded the same. This 276 * modifies the {@link #categories}, {@link #adjective}, {@link #noun}, and {@link #nouns} static fields, so it can 277 * affect other Thesaurus objects produced later (it won't change previously-made ones, probably). 278 * @param archive an archived String of categories produced by {@link #archiveCategories()} 279 * @return this Thesaurus, but static state of the class will also be modified so this may affect other Thesaurus objects 280 */ 281 public Thesaurus addArchivedCategories(String archive){ 282 final OrderedMap<String, ArrayList<String>> cat = Converters.convertOrderedMap( 283 Converters.convertString, 284 Converters.convertArrayList(Converters.convertString) 285 ).restore(archive); 286 287 categories.clear(); 288 categories.putAll(cat); 289 290 adjective.clear(); 291 adjective.putAll(cat); 292 293 noun.clear(); 294 noun.putAll(cat); 295 296 nouns.clear(); 297 nouns.putAll(cat); 298 299 Iterator<String> it = adjective.keySet().iterator(); 300 while (it.hasNext()){ 301 if(!it.next().contains("`adj`")) 302 it.remove(); 303 } 304 it = noun.keySet().iterator(); 305 while (it.hasNext()){ 306 if(!it.next().contains("`noun`")) 307 it.remove(); 308 } 309 it = nouns.keySet().iterator(); 310 while (it.hasNext()){ 311 if(!it.next().contains("`nouns`")) 312 it.remove(); 313 } 314 315 for(Map.Entry<String, ArrayList<String>> kv : categories.entrySet()) 316 { 317 addCategory(kv.getKey(), kv.getValue()); 318 } 319 plantTermShuffler = mappings.get("plant`term`"); 320 fruitTermShuffler = mappings.get("fruit`term`"); 321 nutTermShuffler = mappings.get("nut`term`"); 322 flowerTermShuffler = mappings.get("flower`term`"); 323 potionTermShuffler = mappings.get("potion`term`"); 324 325 return this; 326 327 } 328 329 /** 330 * Adds a large list of words pre-generated by FakeLanguageGen and hand-picked for fitness, and makes them 331 * accessible with a keyword based on the language. The keywords this currently knows: 332 * <br> 333 * "lovecraft`pre`", "english`pre`", "greek_romanized`pre`", 334 * "greek_authentic`pre`", "french`pre`", "russian_romanized`pre`", 335 * "russian_authentic`pre`", "japanese_romanized`pre`", "swahili`pre`", 336 * "somali`pre`", "hindi_romanized`pre`", "arabic_romanized`pre`", 337 * "inuktitut`pre`", "norse`pre`", "nahuatl`pre`", "mongolian`pre`", 338 * "fantasy`pre`", "fancy_fantasy`pre`", "goblin`pre`", "elf`pre`", 339 * "demonic`pre`", "infernal`pre`", "simplish`pre`", "alien_a`pre`", 340 * "korean_romanized`pre`", "alien_e`pre`", "alien_i`pre`", "alien_o`pre`", 341 * "alien_u`pre`", "dragon`pre`", "kobold`pre`", "insect`pre`", "maori`pre`", 342 * "spanish`pre`", "deep_speech`pre`", "norse_simplified`pre`", 343 * "hletkip`pre`", "ancient_egyptian`pre`", "crow`pre`", "imp`pre`", 344 * "malay`pre`", "celestial`pre`", "chinese_romanized`pre`", 345 * "cherokee_romanized`pre`", "vietnamese`pre`" 346 * <br> 347 * These correspond to similarly-named fields in {@link FakeLanguageGen}, just without {@code `pre`}; for instance 348 * {@code "cherokee_romanized`pre`"} corresponds to {@link FakeLanguageGen#CHEROKEE_ROMANIZED}. You can use these 349 * same keywords with {@code `gen`} instead of {@code `pre`} to generate at runtime based on the current RNG state, 350 * instead of using one of several pre-generated words, and doing that does not require addFakeWords() to be used. 351 * <br> 352 * Capitalizing the first letter in the keyword where it appears in text you call process() on will capitalize the 353 * first letter of the produced fake word, which is often desirable for things like place names. Capitalizing the 354 * second letter will capitalize the whole produced fake word. This applies only per-instance of each keyword; it 355 * won't change the internally-stored list of words. 356 * @return this for chaining 357 */ 358 public Thesaurus addFakeWords() 359 { 360 //long state = rng.getState(); 361 for(Map.Entry<CharSequence, FakeLanguageGen> kv : languages.entrySet()) 362 { 363 ArrayList<String> words = new ArrayList<>(16); 364 for (int i = 0; i < 16; i++) { 365 words.add(kv.getValue().word(rng, false, rng.between(2, 4))); 366 } 367 addCategory(StringKit.replace(kv.getKey(), "`gen", "`pre"), words); 368 } 369 //rng.setState(state); 370 return this; 371 } 372 373 private StringBuilder modify(CharSequence text) 374 { 375 Matcher m; 376 StringBuilder sb = new StringBuilder(text); 377 Replacer.StringBuilderBuffer tb, working = Replacer.wrap(sb); 378 StringBuilder tmp; 379 boolean found; 380 FakeLanguageGen.Alteration alt; 381 for (int a = 0; a < alterations.size(); a++) { 382 alt = alterations.get(a); 383 tmp = working.sb; 384 tb = Replacer.wrap(new StringBuilder(tmp.length())); 385 m = alt.replacer.getPattern().matcher(tmp); 386 387 found = false; 388 while (true) { 389 if (rng.nextDouble() < alt.chance) { 390 if (!Replacer.replaceStep(m, alt.replacer.getSubstitution(), tb)) 391 break; 392 found = true; 393 } else { 394 if (!m.find()) 395 break; 396 found = true; 397 m.getGroup(MatchResult.PREFIX, tb); 398 m.getGroup(MatchResult.MATCH, tb); 399 m.setTarget(m, MatchResult.SUFFIX); 400 } 401 } 402 if (found) { 403 m.getGroup(MatchResult.TARGET, tb); 404 working = tb; 405 } 406 } 407 return working.sb; 408 409 } 410 /** 411 * Given a String, StringBuilder, or other CharSequence that should contain words this knows synonyms for, this 412 * replaces each occurrence of such a known word with one of its synonyms, leaving unknown words untouched. Words 413 * that were learned together as synonyms with addSynonyms() will be replaced in such a way that an individual 414 * replacement word should not occur too close to a previous occurrence of the same word; that is, replacing the 415 * text "You fiend! You demon! You despoiler of creation; devil made flesh!", where "fiend", "demon", and "devil" 416 * are all synonyms, would never produce a string that contained "fiend" as the replacement for all three of those. 417 * @param text a CharSequence, such as a String, that contains words in the source language 418 * @return a String of the translated text. 419 */ 420 public String process(CharSequence text) 421 { 422// Replacer rep = wordMatch.replacer(new SynonymSubstitution()); 423 //String t = rep.replace(text); 424 Matcher m = wordMatch.matcher(text); 425 Substitution substitution = new SynonymSubstitution(); 426 Replacer.StringBuilderBuffer dest = Replacer.wrap(new StringBuilder(text.length())); 427 while (m.find()) { 428 if (m.start() > 0) m.getGroup(MatchResult.PREFIX, dest); 429 substitution.appendSubstitution(m, dest); 430 m.setTarget(m, MatchResult.SUFFIX); 431 } 432 m.getGroup(MatchResult.TARGET, dest); 433 434 m.setTarget(dest.sb); 435 dest.sb.setLength(0); 436 while (m.find()) { 437 if (m.start() > 0) m.getGroup(MatchResult.PREFIX, dest); 438 substitution.appendSubstitution(m, dest); 439 m.setTarget(m, MatchResult.SUFFIX); 440 } 441 m.getGroup(MatchResult.TARGET, dest); 442 443 if(alterations.isEmpty()) 444 return StringKit.replace(StringKit.correctABeforeVowel(dest.sb), "\t", ""); 445 else 446 return StringKit.replace(modify(StringKit.correctABeforeVowel(dest.sb)), "\t", ""); 447 } 448 449 public String lookup(String word) 450 { 451 if(word.isEmpty()) 452 return word; 453 if("@".equals(word)) 454 { 455 return defaultLanguage.word(rng, true, rng.between(2, 4)); 456 } 457 String word2 = word.toLowerCase(); 458 if(mappings.containsKey(word2)) 459 { 460 String nx = mappings.get(word2).next(); 461 if(nx.isEmpty()) 462 return nx; 463 if(word.length() > 1 && Category.Lu.contains(word.charAt(1))) 464 return nx.toUpperCase(); 465 if(Category.Lu.contains(word.charAt(0))) 466 { 467 return Character.toUpperCase(nx.charAt(0)) + nx.substring(1); 468 } 469 return nx; 470 } 471 else if(languages.containsKey(word2)) 472 { 473 if(word.length() > 1 && Category.Lu.contains(word.charAt(1))) 474 return languages.get(word2).word(rng, false, rng.between(2, 4)).toUpperCase(); 475 if(Category.Lu.contains(word.charAt(0))) 476 { 477 return languages.get(word2).word(rng, true, rng.between(2, 4)); 478 } 479 return languages.get(word2).word(rng, false, rng.between(2, 4)); 480 } 481 return word; 482 } 483 484 private class SynonymSubstitution implements Substitution 485 { 486 private final StringBuilder temp = new StringBuilder(64); 487 @Override 488 public void appendSubstitution(MatchResult match, TextBuffer dest) { 489 //dest.append(lookup(match.group(0))); 490 temp.setLength(0); 491 match.getGroup(0, temp); 492 writeLookup(dest, temp); 493 } 494 } 495 496 private void writeLookup(TextBuffer dest, StringBuilder word) { 497 if(word == null || word.length() <= 0) 498 return; 499 if(word.charAt(0) == '@' && word.length() == 1) 500 { 501 dest.append(defaultLanguage.word(rng, true, rng.between(2, 4))); 502 return; 503 } 504 else if(mappings.containsKey(word)) 505 { 506 String nx = mappings.get(word).next(); 507 if(nx.isEmpty()) 508 return; 509 if(word.length() > 1 && Category.Lu.contains(word.charAt(1))) 510 { 511 dest.append(nx.toUpperCase()); 512 return; 513 } 514 if(Category.Lu.contains(word.charAt(0))) 515 { 516 dest.append(Character.toUpperCase(nx.charAt(0))); 517 dest.append(nx.substring(1)); 518 return; 519 } 520 dest.append(nx); 521 return; 522 } 523 else if(languages.containsKey(word)) 524 { 525 if(word.length() > 1 && Category.Lu.contains(word.charAt(1))) 526 { 527 dest.append(languages.get(word).word(rng, false, rng.between(2, 4)).toUpperCase()); 528 } 529 else if(Category.Lu.contains(word.charAt(0))) 530 { 531 dest.append(languages.get(word).word(rng, true, rng.between(2, 4))); 532 } 533 else 534 { 535 dest.append(languages.get(word).word(rng, false, rng.between(2, 4))); 536 } 537 return; 538 } 539 else if(numbers.containsKey(word)) 540 { 541 if(word.length() > 1 && Category.Lu.contains(word.charAt(1))) 542 { 543 dest.append(numberWordInRange(2, numbers.get(word)).toUpperCase()); 544 } 545 else if(Category.Lu.contains(word.charAt(0))) 546 { 547 String w = numberWordInRange(2, numbers.get(word)); 548 dest.append(Character.toUpperCase(w.charAt(0))); 549 dest.append(w.substring(1)); 550 } 551 else 552 { 553 dest.append(numberWordInRange(2, numbers.get(word))); 554 } 555 return; 556 } 557 else if(numberAdjectives.containsKey(word)) 558 { 559 if(word.length() > 1 && Category.Lu.contains(word.charAt(1))) 560 { 561 dest.append(numberAdjectiveInRange(2, numberAdjectives.get(word)).toUpperCase()); 562 } 563 else if(Category.Lu.contains(word.charAt(0))) 564 { 565 String w = numberAdjectiveInRange(2, numberAdjectives.get(word)); 566 dest.append(Character.toUpperCase(w.charAt(0))); 567 dest.append(w.substring(1)); 568 } 569 else 570 { 571 dest.append(numberAdjectiveInRange(2, numberAdjectives.get(word))); 572 } 573 return; 574 } 575 if(dest instanceof Replacer.StringBuilderBuffer) 576 { 577 ((Replacer.StringBuilderBuffer)dest).sb.append(word); 578 } 579 else 580 dest.append(word.toString()); 581 582 } 583 584 private class RandomLanguageSubstitution implements Substitution 585 { 586 @Override 587 public void appendSubstitution(MatchResult match, TextBuffer dest) { 588 FakeLanguageGen lang = FakeLanguageGen.randomLanguage(rng.nextLong()); 589 randomLanguages.add(lang); 590 if(match.isCaptured(1)) 591 { 592 lang = FakeLanguageGen.randomLanguage(rng.nextLong()); 593 randomLanguages.add(lang); 594 do { 595 latestGenerated = randomLanguages.get(0).word(rng, true, Math.min(rng.between(2, 5), rng.between(1, 5))) 596 + "-" + randomLanguages.get(1).word(rng, true, Math.min(rng.between(2, 5), rng.between(1, 5))); 597 }while (latestGenerated.length() <= 5 || latestGenerated.length() >= 17); 598 dest.append(latestGenerated); 599 } 600 else 601 { 602 do{ 603 latestGenerated = lang.word(rng, true, Math.min(rng.between(2, 5), rng.between(1, 5))); 604 }while (latestGenerated.length() <= 2 || latestGenerated.length() >= 11); 605 dest.append(latestGenerated); 606 } 607 } 608 } 609 610 private class KnownLanguageSubstitution implements Substitution 611 { 612 public FakeLanguageGen language; 613 public KnownLanguageSubstitution(FakeLanguageGen lang) 614 { 615 language = lang; 616 } 617 @Override 618 public void appendSubstitution(MatchResult match, TextBuffer dest) { 619 if (match.isCaptured(1)) { 620 do 621 { 622 latestGenerated = language.word(rng, true, Math.min(rng.between(2, 5), rng.between(1, 5))) + 623 "-" + language.word(rng, true, Math.min(rng.between(2, 5), rng.between(1, 5))); 624 }while (latestGenerated.length() <= 5 || latestGenerated.length() >= 17); 625 dest.append(latestGenerated); 626 } else 627 { 628 do{ 629 latestGenerated = language.word(rng, true, Math.min(rng.between(2, 5), rng.between(1, 5))); 630 }while (latestGenerated.length() <= 2 || latestGenerated.length() >= 11); 631 dest.append(latestGenerated); 632 } 633 } 634 } 635 636 /** 637 * Generates a random possible name for a nation, such as "Iond-Gouccief Alliance" or "The Last Drayo Commonwealth". 638 * May use accented characters, as in 639 * "Thùdshù-Hyóttiálb Hegemony" or "The Glorious Chô Empire"; if you want to strip these out and replace accented 640 * chars with their un-accented counterparts, you can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which 641 * returns a CharSequence that can be converted to String if needed. Shortly after calling this method, but before 642 * calling it again, you can retrieve the generated random languages, if any were used while making nation names, by 643 * getting the FakeLanguageGen elements of this class' {@link #randomLanguages} field. Using one of these 644 * FakeLanguageGen objects, you can produce many more words with a similar style to the nation name, like "Drayo" in 645 * "The Last Drayo Commonwealth". If more than one language was used in the nation name, as in "Thùdshù-Hyóttiálb 646 * Hegemony", you will have two languages in randomLanguages, so here "Thùdshù" would be generated by the first 647 * language, and "Hyóttiálb" by the second language. Calling this method replaces the current contents of 648 * randomLanguages, so if you want to use those languages, get them while you can. This also assigns the 649 * {@link #latestGenerated} field to contain the part of the nation name without any larger titles; in the case of 650 * "The Glorious Chô Empire", the latestGenerated field would be assigned "Chô" at the same time the longer name 651 * would be returned. This field will be reassigned if this method is called again. 652 * 653 * @return a random name for a nation or a loose equivalent to a nation, as a String 654 */ 655 public String makeNationName() 656 { 657 if(!this.mappings.containsKey("empire`noun`")) 658 { 659 addKnownCategories(); 660 } 661 String working = process(rng.getRandomElement(nationTerms)); 662 int frustration = 0; 663 while (frustration++ < 8 && similarFinder.matches(working)) 664 working = process(rng.getRandomElement(nationTerms)); 665 randomLanguages.clear(); 666 RandomLanguageSubstitution sub = new RandomLanguageSubstitution(); 667 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 668 return replacer.replace(working); 669 } 670 /** 671 * Generates a random possible name for a nation, such as "Iond-Gouccief Alliance" or "The Last Drayo Commonwealth", 672 * with the FakeLanguageGen already available instead of randomly created. May use accented characters, as in 673 * "Thùdshù Hegemony" or "The Glorious Chô Empire", 674 * if the given language can produce them; if you want to strip these out and replace accented chars 675 * with their un-accented counterparts, you can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which 676 * returns a CharSequence that can be converted to String if needed, or simply get an accent-less language by 677 * calling {@link FakeLanguageGen#removeAccents()} on the FakeLanguageGen you would give this. This assigns the 678 * {@link #latestGenerated} field to contain the part of the nation name without any larger titles; in the case of 679 * "The Glorious Chô Empire", the latestGenerated field would be assigned "Chô" at the same time the longer name 680 * would be returned. This field will be reassigned if this method is called again. 681 * <br> 682 * Some nation names use a hyphenated pairing of what would normally be names in two different languages; if one of 683 * those names is produced by this it will produce two names in the same linguistic style. The randomLanguages field 684 * is not populated by this method; it is assumed that since you are passing this a FakeLanguageGen, you already 685 * have the one you want to use anyway. 686 * 687 * @param language a FakeLanguageGen that will be used to construct any non-English names 688 * @return a random name for a nation or a loose equivalent to a nation, as a String 689 */ 690 public String makeNationName(FakeLanguageGen language) 691 { 692 if(!this.mappings.containsKey("empire`noun`")) 693 { 694 addKnownCategories(); 695 } 696 String working = process(rng.getRandomElement(nationTerms)); 697 int frustration = 0; 698 while (frustration++ < 8 && similarFinder.matches(working)) 699 working = process(rng.getRandomElement(nationTerms)); 700 randomLanguages.clear(); 701 KnownLanguageSubstitution sub = new KnownLanguageSubstitution(language); 702 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 703 return replacer.replace(working); 704 } 705 706 /** 707 * Generates a random possible name for a plant or tree, such as "Ikesheha's maple" or "sugarleaf birch". 708 * May use accented characters, as in "Emôa's greenwood", if the given language can 709 * produce them; if you want to strip these out and replace accented chars with their un-accented counterparts, you 710 * can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which returns a CharSequence that can be converted 711 * to String if needed. Shortly after calling this method, but before 712 * calling it again, you can retrieve the generated random languages, if any were used while making names, by 713 * getting the FakeLanguageGen elements of this class' {@link #randomLanguages} field. Using one of these 714 * FakeLanguageGen objects, you can produce many more words with a similar style to the person or place name, like 715 * "Drayo" in "The Last Drayo Commonwealth". Calling this method replaces the current contents of 716 * randomLanguages, so if you want to use those languages, get them while you can. 717 * 718 * @return a random name for a plant, shrub, or tree, as a String 719 */ 720 public String makePlantName() 721 { 722 if(!this.mappings.containsKey("tree`noun`")) 723 { 724 addKnownCategories(); 725 } 726 String working = process(plantTermShuffler.next()); 727 int frustration = 0; 728 while (frustration++ < 8 && similarFinder.matches(working)) 729 working = process(plantTermShuffler.next()); 730 randomLanguages.clear(); 731 RandomLanguageSubstitution sub = new RandomLanguageSubstitution(); 732 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 733 return replacer.replace(working).replace("\t", ""); 734 } 735 /** 736 * Generates a random possible name for a plant or tree, such as "Ikesheha's maple" or "sugarleaf birch", 737 * with the FakeLanguageGen already available instead of randomly created. May use accented characters, as in 738 * "Emôa's greenwood", if the given language can 739 * produce them; if you want to strip these out and replace accented chars with their un-accented counterparts, you 740 * can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which 741 * returns a CharSequence that can be converted to String if needed, or simply get an accent-less language by 742 * calling {@link FakeLanguageGen#removeAccents()} on the FakeLanguageGen you would give this. 743 * 744 * @param language a FakeLanguageGen that will be used to construct any non-English names 745 * @return a random name for a plant, shrub, or tree, as a String 746 */ 747 public String makePlantName(FakeLanguageGen language) 748 { 749 if(!this.mappings.containsKey("tree`noun`")) 750 { 751 addKnownCategories(); 752 } 753 String working = process(plantTermShuffler.next()); 754 int frustration = 0; 755 while (frustration++ < 8 && similarFinder.matches(working)) 756 working = process(plantTermShuffler.next()); 757 randomLanguages.clear(); 758 KnownLanguageSubstitution sub = new KnownLanguageSubstitution(language); 759 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 760 return replacer.replace(working).replace("\t", ""); 761 } 762 763 /** 764 * Generates a random possible name for a plant or tree, such as "green lime-melon" or "Ung's date". 765 * May use accented characters, as in "Emôa's greenwood", if the given language can 766 * produce them; if you want to strip these out and replace accented chars with their un-accented counterparts, you 767 * can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which returns a CharSequence that can be converted 768 * to String if needed. Shortly after calling this method, but before 769 * calling it again, you can retrieve the generated random languages, if any were used while making names, by 770 * getting the FakeLanguageGen elements of this class' {@link #randomLanguages} field. Using one of these 771 * FakeLanguageGen objects, you can produce many more words with a similar style to the person or place name, like 772 * "Drayo" in "The Last Drayo Commonwealth". Calling this method replaces the current contents of 773 * randomLanguages, so if you want to use those languages, get them while you can. 774 * 775 * @return a random name for a plant, shrub, or tree, as a String 776 */ 777 public String makeFruitName() 778 { 779 if(!this.mappings.containsKey("fruit`noun`")) 780 { 781 addKnownCategories(); 782 } 783 String working = process(fruitTermShuffler.next()); 784 int frustration = 0; 785 while (frustration++ < 8 && similarFinder.matches(working)) 786 working = process(fruitTermShuffler.next()); 787 randomLanguages.clear(); 788 RandomLanguageSubstitution sub = new RandomLanguageSubstitution(); 789 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 790 return replacer.replace(working).replace("\t", ""); 791 } 792 /** 793 * Generates a random possible name for a plant or tree, such as "green lime-melon" or "Ung's date", 794 * with the FakeLanguageGen already available instead of randomly created. May use accented characters, as in 795 * "Emôa's greenwood", if the given language can 796 * produce them; if you want to strip these out and replace accented chars with their un-accented counterparts, you 797 * can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which 798 * returns a CharSequence that can be converted to String if needed, or simply get an accent-less language by 799 * calling {@link FakeLanguageGen#removeAccents()} on the FakeLanguageGen you would give this. 800 * 801 * @param language a FakeLanguageGen that will be used to construct any non-English names 802 * @return a random name for a plant, shrub, or tree, as a String 803 */ 804 public String makeFruitName(FakeLanguageGen language) 805 { 806 if(!this.mappings.containsKey("fruit`noun`")) 807 { 808 addKnownCategories(); 809 } 810 String working = process(fruitTermShuffler.next()); 811 int frustration = 0; 812 while (frustration++ < 8 && similarFinder.matches(working)) 813 working = process(fruitTermShuffler.next()); 814 randomLanguages.clear(); 815 KnownLanguageSubstitution sub = new KnownLanguageSubstitution(language); 816 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 817 return replacer.replace(working).replace("\t", ""); 818 } 819 820 /** 821 * Generates a random possible name for a plant or tree, such as "nut of Gikoim" or "Pelyt's cashew". 822 * May use accented characters, as in "Emôa's greenwood", if the given language can 823 * produce them; if you want to strip these out and replace accented chars with their un-accented counterparts, you 824 * can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which returns a CharSequence that can be converted 825 * to String if needed. Shortly after calling this method, but before 826 * calling it again, you can retrieve the generated random languages, if any were used while making names, by 827 * getting the FakeLanguageGen elements of this class' {@link #randomLanguages} field. Using one of these 828 * FakeLanguageGen objects, you can produce many more words with a similar style to the person or place name, like 829 * "Drayo" in "The Last Drayo Commonwealth". Calling this method replaces the current contents of 830 * randomLanguages, so if you want to use those languages, get them while you can. 831 * 832 * @return a random name for a plant, shrub, or tree, as a String 833 */ 834 public String makeNutName() 835 { 836 if(!this.mappings.containsKey("nut`noun`")) 837 { 838 addKnownCategories(); 839 } 840 String working = process(nutTermShuffler.next()); 841 int frustration = 0; 842 while (frustration++ < 8 && similarFinder.matches(working)) 843 working = process(nutTermShuffler.next()); 844 randomLanguages.clear(); 845 RandomLanguageSubstitution sub = new RandomLanguageSubstitution(); 846 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 847 return replacer.replace(working).replace("\t", ""); 848 } 849 /** 850 * Generates a random possible name for a plant or tree, such as "nut of Gikoim" or "Pelyt's cashew", 851 * with the FakeLanguageGen already available instead of randomly created. 852 * May use accented characters, as in "Emôa's greenwood", if the given language can 853 * produce them; if you want to strip these out and replace accented chars with their un-accented counterparts, you 854 * can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which 855 * returns a CharSequence that can be converted to String if needed, or simply get an accent-less language by 856 * calling {@link FakeLanguageGen#removeAccents()} on the FakeLanguageGen you would give this. 857 * 858 * @param language a FakeLanguageGen that will be used to construct any non-English names 859 * @return a random name for a plant, shrub, or tree, as a String 860 */ 861 public String makeNutName(FakeLanguageGen language) 862 { 863 if(!this.mappings.containsKey("nut`noun`")) 864 { 865 addKnownCategories(); 866 } 867 String working = process(nutTermShuffler.next()); 868 int frustration = 0; 869 while (frustration++ < 8 && similarFinder.matches(working)) 870 working = process(nutTermShuffler.next()); 871 randomLanguages.clear(); 872 KnownLanguageSubstitution sub = new KnownLanguageSubstitution(language); 873 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 874 return replacer.replace(working).replace("\t", ""); 875 } 876 877 /** 878 * Generates a random possible name for a plant or tree, such as "tulip of Jirui" or "Komert's thorny lilac". 879 * May use accented characters, as in "Emôa's greenwood", if the given language can 880 * produce them; if you want to strip these out and replace accented chars with their un-accented counterparts, you 881 * can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which returns a CharSequence that can be converted 882 * to String if needed. Shortly after calling this method, but before 883 * calling it again, you can retrieve the generated random languages, if any were used while making names, by 884 * getting the FakeLanguageGen elements of this class' {@link #randomLanguages} field. Using one of these 885 * FakeLanguageGen objects, you can produce many more words with a similar style to the person or place name, like 886 * "Drayo" in "The Last Drayo Commonwealth". Calling this method replaces the current contents of 887 * randomLanguages, so if you want to use those languages, get them while you can. 888 * 889 * @return a random name for a plant, shrub, or tree, as a String 890 */ 891 public String makeFlowerName() 892 { 893 if(!this.mappings.containsKey("flower`noun`")) 894 { 895 addKnownCategories(); 896 } 897 String working = process(flowerTermShuffler.next()); 898 int frustration = 0; 899 while (frustration++ < 8 && similarFinder.matches(working)) 900 working = process(flowerTermShuffler.next()); 901 randomLanguages.clear(); 902 RandomLanguageSubstitution sub = new RandomLanguageSubstitution(); 903 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 904 return replacer.replace(working).replace("\t", ""); 905 } 906 /** 907 * Generates a random possible name for a plant or tree, such as "tulip of Jirui" or "Komert's thorny lilac", 908 * with the FakeLanguageGen already available instead of randomly created. May use accented characters, as in 909 * "Emôa's greenwood", if the given language can 910 * produce them; if you want to strip these out and replace accented chars with their un-accented counterparts, you 911 * can use {@link FakeLanguageGen#removeAccents(CharSequence)}, which 912 * returns a CharSequence that can be converted to String if needed, or simply get an accent-less language by 913 * calling {@link FakeLanguageGen#removeAccents()} on the FakeLanguageGen you would give this. 914 * 915 * @param language a FakeLanguageGen that will be used to construct any non-English names 916 * @return a random name for a plant, shrub, or tree, as a String 917 */ 918 public String makeFlowerName(FakeLanguageGen language) 919 { 920 if(!this.mappings.containsKey("flower`noun`")) 921 { 922 addKnownCategories(); 923 } 924 String working = process(flowerTermShuffler.next()); 925 int frustration = 0; 926 while (frustration++ < 8 && similarFinder.matches(working)) 927 working = process(flowerTermShuffler.next()); 928 randomLanguages.clear(); 929 KnownLanguageSubstitution sub = new KnownLanguageSubstitution(language); 930 Replacer replacer = Pattern.compile("@(-@)?").replacer(sub); 931 return replacer.replace(working).replace("\t", ""); 932 } 933 934 /** 935 * Generates a random possible description for a potion in a container, such as "a smoky glass flask containing a 936 * few drops of an orange tonic", "a milk carton filled with a red fluid", "a shining silver bottle filled with an 937 * effervescent violet potion", or "a wineskin filled with a black serum". 938 * 939 * @return a random description for a potion or other liquid in a container 940 */ 941 public String makePotionDescription() 942 { 943 if(!this.mappings.containsKey("liquid`noun`")) 944 { 945 addKnownCategories(); 946 } 947 String working = process(potionTermShuffler.next()); 948 int frustration = 0; 949 while (frustration++ < 8 && similarFinder.matches(working)) 950 working = process(potionTermShuffler.next()); 951 return StringKit.correctABeforeVowel(working); 952 } 953 954 /** 955 * Gets an English word for a given number, if this knows it. These words are known from 0 ("zero") to 20 956 * ("twenty"), as well as some higher numbers. If a word isn't known for a number, this returns the number as a 957 * String, such as "537" or "-1". 958 * @param number the number to get a word for 959 * @return the word associated with a number as a String, or if none is known, {@code String.valueOf(number)}. 960 */ 961 public static String numberWord(final int number) 962 { 963 switch (number){ 964 case 0: return "zero"; 965 case 1: return "one"; 966 case 2: return "two"; 967 case 3: return "three"; 968 case 4: return "four"; 969 case 5: return "five"; 970 case 6: return "six"; 971 case 7: return "seven"; 972 case 8: return "eight"; 973 case 9: return "nine"; 974 case 10: return "ten"; 975 case 11: return "eleven"; 976 case 12: return "twelve"; 977 case 13: return "thirteen"; 978 case 14: return "fourteen"; 979 case 15: return "fifteen"; 980 case 16: return "sixteen"; 981 case 17: return "seventeen"; 982 case 18: return "eighteen"; 983 case 19: return "nineteen"; 984 case 20: return "twenty"; 985 case 30: return "thirty"; 986 case 40: return "fourty"; 987 case 50: return "fifty"; 988 case 60: return "sixty"; 989 case 70: return "seventy"; 990 case 80: return "eighty"; 991 case 90: return "ninety"; 992 case 100: return "hundred"; 993 case 1000: return "thousand"; 994 case 1000000: return "million"; 995 case 1000000000: return "billion"; 996 default: return String.valueOf(number); 997 } 998 } 999 1000 /** 1001 * Gets an English word that describes a numbered position in some sequence, if this knows it (such as "second" or 1002 * "eleventh"). These words are known from 1 ("first") to 20 ("twentieth"), as well as some higher numbers. If a 1003 * word isn't known for a number, this appends a suffix based on the last base-10 digit to the number, as with "0th" 1004 * or "42nd". 1005 * @param number the number to get a position adjective for 1006 * @return the word associated with a number as a String, or if none is known, {@code String.valueOf(number)} followed by a two-char suffix from the last digit. 1007 */ 1008 public static String numberAdjective(final int number) 1009 { 1010 switch (number){ 1011 case 1: return "first"; 1012 case 2: return "second"; 1013 case 3: return "third"; 1014 case 4: return "fourth"; 1015 case 5: return "fifth"; 1016 case 6: return "sixth"; 1017 case 7: return "seventh"; 1018 case 8: return "eighth"; 1019 case 9: return "ninth"; 1020 case 10: return "tenth"; 1021 case 11: return "eleventh"; 1022 case 12: return "twelfth"; 1023 case 13: return "thirteenth"; 1024 case 14: return "fourteenth"; 1025 case 15: return "fifteenth"; 1026 case 16: return "sixteenth"; 1027 case 17: return "seventeenth"; 1028 case 18: return "eighteenth"; 1029 case 19: return "nineteenth"; 1030 case 20: return "twentieth"; 1031 case 30: return "thirtieth"; 1032 case 40: return "fourtieth"; 1033 case 50: return "fiftieth"; 1034 case 60: return "sixtieth"; 1035 case 70: return "seventieth"; 1036 case 80: return "eightieth"; 1037 case 90: return "ninetieth"; 1038 case 100: return "hundredth"; 1039 case 1000: return "thousandth"; 1040 case 1000000: return "millionth"; 1041 case 1000000000: return "billionth"; 1042 default: 1043 { 1044 switch (number % 10){ 1045 case 1: return number + "st"; 1046 case 2: return number + "nd"; 1047 case 3: return number + "rd"; 1048 default:return number + "th"; 1049 } 1050 } 1051 } 1052 } 1053 1054 /** 1055 * Gets an English word for a number, if this knows it, where the number is chosen randomly between lowest and 1056 * highest, both inclusive. These words are known from 0 ("zero") to 20 ("twenty"), as well as some higher numbers. 1057 * If a word isn't known for a number, this returns the number as a String, such as "537" or "-1". 1058 * @param lowest the lower bound for numbers this can choose, inclusive 1059 * @param highest the upper bound for numbers this can choose, inclusive 1060 * @return a String word for a number in the given range, such as "six" or "eleven", if it is known 1061 */ 1062 public String numberWordInRange(int lowest, int highest) 1063 { 1064 return numberWord(rng.nextSignedInt(highest + 1 - lowest) + lowest); 1065 } 1066 /** 1067 * Gets an English word that describes a numbered position in some sequence, if this knows it (such as "second" or 1068 * "eleventh"), where the number is chosen randomly between lowest and highest, both inclusive. These words are 1069 * known from 1 ("first") to 20 ("twentieth"), as well as some output for other numbers (such as "42nd" or "23rd"). 1070 * @param lowest the lower bound for numbers this can choose, inclusive 1071 * @param highest the upper bound for numbers this can choose, inclusive 1072 * @return a String word for a number in the given range, such as "third" or "twelfth", if it is known 1073 */ 1074 public String numberAdjectiveInRange(int lowest, int highest) 1075 { 1076 return numberAdjective(rng.nextSignedInt(highest + 1 - lowest) + lowest); 1077 } 1078 1079 private static final ArrayList<String> nationTerms = Maker.makeList( 1080 "Union`adj` Union`noun` of @", "Union`adj` @ Union`noun`", "@ Union`noun`", "@ Union`noun`", "@-@ Union`noun`", "Union`adj` Union`noun` of @", 1081 "Union`adj` Duchy`nouns` of @", "The @ Duchy`noun`", "The Fancy`adj` @ Duchy`noun`", "The Sole`adj` @ Empire`noun`", 1082 "@ Empire`noun`", "@ Empire`noun`", "@ Empire`noun`", "@-@ Empire`noun`", "The Fancy`adj` @ Empire`noun`", "The Fancy`adj` @ Empire`noun`", "The Holy`adj` @ Empire`noun`"); 1083 1084 private static final ArrayList<String> plantTerms = Maker.makeList( 1085 "@'s color`adj`\tleaf`noun`", 1086 "@'s tree`noun`", 1087 "@'s color`adj` tree`noun`", 1088 "@'s flower`noun`", 1089 "@'s shape`adj` flower`noun`", 1090 "@'s color`adj` flower`noun`", 1091 "flower`noun` of @", 1092 "leaf`noun` of @", 1093 "@'s ground`noun`\tleaf`noun`", 1094 "ground`noun`\tleaf`noun` of @", 1095 "sensory`adj` tree`noun` of @", 1096 "sensory`adj` flower`noun` of @", 1097 "color`adj` flower`noun` of @", 1098 "@'s sensory`adj`-leaf`noun`", 1099 "ground`noun`\tleaf`noun`", 1100 1101 "shape`adj` flower`noun`", 1102 "color`adj` flower`noun`", 1103 "flavor`noun`\tleaf`noun` tree`noun`", 1104 "flavor`adj` fruit`noun` tree`noun`", 1105 "flavor`adj` nut`noun` tree`noun`", 1106 "color`adj` fruit`noun` tree`noun`", 1107 "color`adj` nut`noun` tree`noun`", 1108 "shape`adj`-fruit`noun` tree`noun`", 1109 "shape`adj`-leaf`noun` tree`noun`", 1110 "sensory`adj` tree`noun`-tree`noun`", 1111 "sensory`adj`-leaf`noun` tree`noun`", 1112 "color`adj`-leaf`noun` flower`noun`", 1113 "shape`adj`-leaf`noun` flower`noun`", 1114 "sensory`adj` flower`noun`-flower`noun`", 1115 "sensory`adj`-leaf`noun` flower`noun`", 1116 "ground`noun`\tflower`noun`" 1117 ); 1118 private static final ArrayList<String> fruitTerms = Maker.makeList( 1119 "fruit`noun` of @", 1120 "@'s fruit`noun`", 1121 "@'s flavor`adj` fruit`noun`", 1122 "@'s color`adj` fruit`noun`", 1123 "flavor`adj` fruit`noun`-fruit`noun`", 1124 "color`adj` fruit`noun`-fruit`noun`" 1125 ); 1126 private static final ArrayList<String> nutTerms = Maker.makeList( 1127 "nut`noun` of @", 1128 "color`adj` nut`noun` of @", 1129 "@'s nut`noun`", 1130 "@'s flavor`adj` nut`noun`", 1131 "@'s color`adj` nut`noun`", 1132 "flavor`adj` nut`noun`", 1133 "color`adj` nut`noun`", 1134 "sensory`adj` nut`noun`" 1135 ); 1136 private static final ArrayList<String> flowerTerms = Maker.makeList( 1137 "flower`noun` of @", 1138 "sensory`adj` flower`noun` of @", 1139 "color`adj` flower`noun` of @", 1140 "@'s flower`noun`", 1141 "@'s shape`adj` flower`noun`", 1142 "@'s color`adj` flower`noun`", 1143 "shape`adj` flower`noun`", 1144 "color`adj` flower`noun`", 1145 "color`adj`-leaf`noun` flower`noun`", 1146 "shape`adj`-leaf`noun` flower`noun`", 1147 "sensory`adj` flower`noun`-flower`noun`", 1148 "sensory`adj`-leaf`noun` flower`noun`", 1149 "ground`noun`\tflower`noun`" 1150 ); 1151 private static final ArrayList<String> potionTerms = Maker.makeList( 1152 "a bottle`adj` bottle`noun` filled with a liquid`adj` color`adj` liquid`noun`", 1153 "a bottle`adj` bottle`noun` filled with a color`adj` liquid`noun`", 1154 "a calabash`adj` filled with a color`adj` liquid`noun`", 1155 "a bottle`adj` bottle`noun` half-filled with a liquid`adj` color`adj` liquid`noun`", 1156 "a bottle`adj` bottle`noun` containing a few drops of a color`adj` liquid`noun`" 1157 ); 1158 public static final OrderedMap<String, ArrayList<String>> categories = makeOM( 1159 "calm`adj`", 1160 makeList("harmonious", "peaceful", "pleasant", "serene", "placid", "tranquil", "calm"), 1161 "calm`noun`", 1162 makeList("harmony", "peace", "kindness", "serenity", "tranquility", "calm"), 1163 "org`noun`", 1164 makeList("fraternity", "brotherhood", "order", "group", "foundation", "association", "guild", "fellowship", "partnership"), 1165 "org`nouns`", 1166 makeList("fraternities", "brotherhoods", "orders", "groups", "foundations", "associations", "guilds", "fellowships", "partnerships"), 1167 "empire`adj`", 1168 makeList("imperial", "prince's", "king's", "sultan's", "regal", "dynastic", "royal", "hegemonic", "monarchic", "ascendant", "emir's", "lordly"), 1169 "empire`noun`", 1170 makeList("empire", "emirate", "kingdom", "sultanate", "dominion", "dynasty", "imperium", "hegemony", "triumvirate", "ascendancy", "monarchy", "commonwealth"), 1171 "empire`nouns`", 1172 makeList("empires", "emirates", "kingdoms", "sultanates", "dominions", "dynasties", "imperia", "hegemonies", "triumvirates", "ascendancies", "monarchies", "commonwealths"), 1173 "emperor`noun`", 1174 makeList("emperor", "emir", "king", "sultan", "lord", "ruler", "pharaoh"), 1175 "emperor`nouns`", 1176 makeList("emperors", "emirs", "kings", "sultans", "lords", "rulers", "pharaohs"), 1177 "empress`noun`", 1178 makeList("empress", "emira", "queen", "sultana", "lady", "ruler", "pharaoh"), 1179 "empress`nouns`", 1180 makeList("empresses", "emiras", "queens", "sultanas", "ladies", "rulers", "pharaohs"), 1181 "union`adj`", 1182 makeList("united", "allied", "people's", "confederated", "federated", "congressional", "independent", "associated", "unified", "democratic"), 1183 "union`noun`", 1184 makeList("union", "alliance", "coalition", "confederation", "federation", "congress", "confederacy", "league", "faction", "republic"), 1185 "union`nouns`", 1186 makeList("unions", "alliances", "coalitions", "confederations", "federations", "congresses", "confederacies", "leagues", "factions", "republics"), 1187 "militia`noun`", 1188 makeList("rebellion", "resistance", "militia", "liberators", "warriors", "fighters", "militants", "front", "irregulars"), 1189 "militia`nouns`", 1190 makeList("rebellions", "resistances", "militias", "liberators", "warriors", "fighters", "militants", "fronts", "irregulars"), 1191 "gang`noun`", 1192 makeList("gang", "syndicate", "mob", "crew", "posse", "mafia", "cartel"), 1193 "gang`nouns`", 1194 makeList("gangs", "syndicates", "mobs", "crews", "posses", "mafias", "cartels"), 1195 "duke`noun`", 1196 makeList("duke", "earl", "baron", "fief", "lord", "shogun"), 1197 "duke`nouns`", 1198 makeList("dukes", "earls", "barons", "fiefs", "lords", "shoguns"), 1199 "duchy`noun`", 1200 makeList("duchy", "earldom", "barony", "fiefdom", "lordship", "shogunate"), 1201 "duchy`nouns`", 1202 makeList("duchies", "earldoms", "baronies", "fiefdoms", "lordships", "shogunates"), 1203 "magical`adj`", 1204 makeList("arcane", "enchanted", "sorcerous", "ensorcelled", "magical", "mystical"), 1205 "holy`adj`", 1206 makeList("auspicious", "divine", "holy", "sacred", "prophetic", "blessed", "godly", "virtuous"), 1207 "priest`noun`", 1208 makeList("priest", "bishop", "chaplain", "cleric", "cardinal", "preacher"), 1209 "priest`nouns`", 1210 makeList("priests", "bishops", "chaplains", "clergy", "cardinals", "preachers"), 1211 "unholy`adj`", 1212 makeList("bewitched", "occult", "unholy", "macabre", "accursed", "profane", "vile"), 1213 "witch`noun`", 1214 makeList("witch", "warlock", "necromancer", "cultist", "occultist", "defiler"), 1215 "witch`nouns`", 1216 makeList("witches", "warlocks", "necromancers", "cultists", "occultists", "defilers"), 1217 "forest`adj`", 1218 makeList("natural", "primal", "verdant", "lush", "fertile", "bountiful"), 1219 "forest`noun`", 1220 makeList("nature", "forest", "greenery", "jungle", "woodland", "grove", "copse", "glen"), 1221 "shaman`noun`", 1222 makeList("shaman", "druid", "warden", "animist"), 1223 "shaman`nouns`", 1224 makeList("shamans", "druids", "wardens", "animists"), 1225 "fancy`adj`", 1226 makeList("grand", "glorious", "magnificent", "magnanimous", "majestic", "great", "powerful"), 1227 "evil`adj`", 1228 makeList("heinous", "scurrilous", "terrible", "horrible", "debased", "wicked", "evil", "malevolent", "nefarious", "vile", "cruel", "abhorrent"), 1229 "villain`noun`", 1230 makeList("villain", "knave", "evildoer", "killer", "blasphemer", "monster", "murderer"), 1231 "villain`nouns`", 1232 makeList("villains", "knaves", "evildoers", "killers", "blasphemers", "monsters", "murderers"), 1233 "monster`noun`", 1234 makeList("fiend", "abomination", "demon", "devil", "ghoul", "monster", "beast", "creature"), 1235 "monsters`nouns`", 1236 makeList("fiends", "abominations", "demons", "devils", "ghouls", "monsters", "beasts", "creatures"), 1237 "good`adj`", 1238 makeList("righteous", "moral", "good", "pure", "compassionate", "flawless", "perfect", "kind"), 1239 "lethal`adj`", 1240 makeList("silent", "lethal", "deadly", "fatal", "venomous", "cutthroat", "murderous", "bloodstained", "stalking", "poisonous"), 1241 "lethal`noun`", 1242 makeList("silence", "killer", "assassin", "ninja", "venom", "poison", "snake", "murder", "blood", "razor", "tiger", "slayer"), 1243 "blade`noun`", // really any melee weapon 1244 makeList("blade", "knife", "sword", "axe", "stiletto", "katana", "scimitar", "hatchet", "spear", "glaive", "halberd", 1245 "hammer", "maul", "flail", "mace", "sickle", "scythe", "whip", "lance", "nunchaku", "saber", "cutlass", "trident"), 1246 "bow`noun`", // really any medieval or earlier ranged weapon 1247 makeList("bow", "longbow", "shortbow", "crossbow", "sling", "atlatl", "bolas", "javelin", "net", "shuriken", "dagger"), 1248 "weapon`noun`", // any medieval or earlier weapon (not including firearms or newer) 1249 makeList("blade", "knife", "sword", "axe", "stiletto", "katana", "scimitar", "hatchet", "spear", "glaive", "halberd", 1250 "hammer", "maul", "flail", "mace", "sickle", "scythe", "whip", "lance", "nunchaku", "saber", "cutlass", "trident", 1251 "bow", "longbow", "shortbow", "crossbow", "sling", "atlatl", "bolas", "javelin", "net", "shuriken", "dagger"), 1252 "musket`noun`", 1253 makeList("arquebus", "blunderbuss", "musket", "matchlock", "flintlock", "wheellock", "cannon"), 1254 "grenade`noun`", 1255 makeList("rocket", "grenade", "missile", "bomb", "warhead", "explosive", "flamethrower"), 1256 "rifle`noun`", 1257 makeList("pistol", "rifle", "handgun", "firearm", "longarm", "shotgun"), 1258 "blade`nouns`", 1259 makeList("blades", "knives", "swords", "axes", "stilettos", "katana", "scimitars", "hatchets", "spears", "glaives", "halberds", 1260 "hammers", "mauls", "flails", "maces", "sickles", "scythes", "whips", "lances", "nunchaku", "sabers", "cutlasses", "tridents"), 1261 "bow`nouns`", 1262 makeList("bows", "longbows", "shortbows", "crossbows", "slings", "atlatls", "bolases", "javelins", "nets", "shuriken", "daggers"), 1263 "weapon`nouns`", 1264 makeList("blades", "knives", "swords", "axes", "stilettos", "katana", "scimitars", "hatchets", "spears", "glaives", "halberds", 1265 "hammers", "mauls", "flails", "maces", "sickles", "scythes", "whips", "lances", "nunchaku", "sabers", "cutlasses", "tridents", 1266 "bows", "longbows", "shortbows", "crossbows", "slings", "atlatls", "bolases", "javelins", "nets", "shuriken", "daggers"), 1267 "musket`nouns`", 1268 makeList("arquebusses", "blunderbusses", "muskets", "matchlocks", "flintlocks", "wheellocks", "cannons"), 1269 "grenade`nouns`", 1270 makeList("rockets", "grenades", "missiles", "bombs", "warheads", "explosives", "flamethrowers"), 1271 "rifle`nouns`", 1272 makeList("pistols", "rifles", "handguns", "firearms", "longarms", "shotguns"), 1273 "scifi`adj`", 1274 makeList("plasma", "warp", "tachyonic", "phase", "gravitational", "photonic", "nanoscale", "laser", "quantum", "genetic"), 1275 "tech`adj`", 1276 makeList("cyber", "digital", "electronic", "techno", "hacker", "crypto", "turbo", "mechanical", "servo"), 1277 "sole`adj`", 1278 makeList("sole", "true", "singular", "total", "ultimate", "final", "last"), 1279 "light`adj`", 1280 makeList("bright", "glowing", "solar", "stellar", "lunar", "radiant", "luminous", "shimmering", "gleaming"), 1281 "light`noun`", 1282 makeList("light", "glow", "sun", "star", "moon", "radiance", "dawn", "torch", "shimmer", "gleam"), 1283 "light`nouns`", 1284 makeList("lights", "glimmers", "suns", "stars", "moons", "torches"), 1285 "shadow`noun`", 1286 makeList("shadow", "darkness", "gloom", "blackness", "murk", "twilight"), 1287 "shadow`nouns`", 1288 makeList("shadows", "darkness", "gloom", "blackness", "murk", "twilight"), 1289 "fire`noun`", 1290 makeList("fire", "flame", "inferno", "conflagration", "pyre", "blaze"), 1291 "fire`nouns`", 1292 makeList("fires", "flames", "infernos", "conflagrations", "pyres", "blazes"), 1293 "ice`noun`", 1294 makeList("ice", "frost", "snow", "chill", "blizzard", "cold"), 1295 "ice`nouns`", 1296 makeList("ice", "frosts", "snow", "chills", "blizzards", "cold"), 1297 "lightning`noun`", 1298 makeList("lightning", "thunder", "thunderbolt", "storm", "spark", "shock"), 1299 "lightning`nouns`", 1300 makeList("lightning", "thunder", "thunderbolts", "storms", "sparks", "shocks"), 1301 "ground`noun`", 1302 makeList("earth", "sand", "soil", "loam", "dirt", "clay", "mud", "peat"), 1303 "lake`noun`", 1304 makeList("puddle", "pond", "lake", "sea", "swamp", "bog", "fen", "glade"), 1305 "leaf`noun`", 1306 makeList("leaf", "bark", "root", "thorn", "seed", "branch", "twig", "wort", "cress", "flower", "wood", "vine", "sap", "bud", "blossom", "shoot", "stalk", "stem"), 1307 "fruit`noun`", 1308 makeList("fruit", "berry", "apple", "peach", "cherry", "melon", "lime", "fig", "date", "mango", "banana", "juniper", "grape", "papaya", "pear", "quince"), 1309 "nut`noun`", 1310 makeList("nut", "bean", "almond", "peanut", "pecan", "walnut", "cashew", "pea", "chestnut", "hazelnut"), 1311 "flower`noun`", 1312 makeList("flower", "rose", "lilac", "orchid", "peony", "oleander", "chrysanthemum", "amaryllis", "camellia", "mallow", "lily", "gardenia", "daisy", "hibiscus", "dandelion", "jasmine", "lotus", "lantana", "phlox", "petunia", "tulip"), 1313 "tree`noun`", 1314 makeList("tree", "oak", "pine", "juniper", "maple", "beech", "birch", "larch", "willow", "alder", "cedar", "palm", "magnolia", "hazel", "cactus", "mangrove", "elm"), 1315 "flavor`noun`", 1316 makeList("sugar", "spice", "acid", "herb", "salt", "grease", "smoke"), 1317 "flavor`adj`", 1318 makeList("sweet", "spicy", "sour", "bitter", "salty", "savory", "smoky"), 1319 "color`adj`", 1320 makeList("black", "white", "red", "orange", "yellow", "green", "blue", "violet", "gray", "brown"), 1321 "shape`adj`", 1322 makeList("hollow", "tufted", "drooping", "fibrous", "giant", "miniature", "delicate", "hardy", "spiny", "thorny", "fragile", "sturdy", "long", "stubby", "stiff", "yielding"), 1323 "sensory`adj`", 1324 makeList("fragrant", "pungent", "rustling", "fuzzy", "glossy", "weeping", "rough", "smooth", "soft", "aromatic"), 1325 "liquid`noun`", 1326 makeList("liquid", "elixir", "tonic", "fluid", "brew", "broth", "potion", "serum"), 1327 "liquid`adj`", 1328 makeList("bubbling", "effervescent", "swirling", "murky", "thick", "congealing", "sloshing", "slimy", "milky"), 1329 "bottle`noun`", 1330 makeList("bottle", "flask", "vial", "jug", "phial", "flagon", "canister"), 1331 "bottle`adj`", 1332 makeList("clear glass", "smoky glass", "green glass", "brown glass", "fluted crystal", "tarnished silver", "dull pewter", "shining silver", "curvaceous glass", "rough-cut glass", "sharp-edged tin"), 1333 "calabash`adj`", 1334 makeList("hollow gourd", "calabash", "milk carton", "waterskin", "wineskin"), 1335 "smart`adj`", 1336 makeList("brilliant", "smart", "genius", "wise", "clever", "cunning", "mindful", "aware"), 1337 "smart`noun`", 1338 makeList("genius", "wisdom", "cunning", "awareness", "mindfulness", "acumen", "smarts", "knowledge"), 1339 "stupid`adj`", 1340 makeList("stupid", "dumb", "idiotic", "foolish", "reckless", "careless", "sloppy", "dull", "moronic", "complacent"), 1341 "stupid`noun`", 1342 makeList("stupidity", "idiocy", "foolishness", "recklessness", "carelessness", "sloppiness", "complacency"), 1343 "bandit`noun`", 1344 makeList("thief", "raider", "bandit", "rogue", "brigand", "highwayman", "pirate"), 1345 "bandit`nouns`", 1346 makeList("thieves", "raiders", "bandits", "rogues", "brigands", "highwaymen", "pirates"), 1347 "soldier`noun`", 1348 makeList("soldier", "warrior", "fighter", "mercenary", "trooper", "combatant"), 1349 "soldier`nouns`", 1350 makeList("soldiers", "warriors", "fighters", "mercenaries", "troops", "combatants"), 1351 "guard`noun`", 1352 makeList("protector", "guardian", "warden", "defender", "guard", "shield", "sentinel", "watchman", "knight", "paladin", "templar"), 1353 "guard`nouns`", 1354 makeList("protectors", "guardians", "wardens", "defenders", "guards", "shields", "sentinels", "watchmen", "knights", "paladins", "templars"), 1355 "hunter`noun`", 1356 makeList("hunter", "poacher", "trapper", "warden", "stalker", "tracker"), 1357 "explorer`noun`", 1358 makeList("explorer", "pathfinder", "seeker", "questant", "wanderer", "nomad"), 1359 "hunter`nouns`", 1360 makeList("hunters", "poachers", "trappers", "wardens", "stalkers", "trackers"), 1361 "explorer`nouns`", 1362 makeList("explorers", "pathfinders", "seekers", "questants", "wanderers", "nomads"), 1363 "rage`noun`", 1364 makeList("rage", "fury", "anger", "wrath", "frenzy", "vengeance"), 1365 "ominous`adj`", 1366 makeList("ominous", "foreboding", "fateful", "baleful", "portentous"), 1367 "many`adj`", 1368 makeList("many", "myriad", "thousandfold", "infinite", "countless", "unlimited", "manifold"), 1369 "impossible`adj`", 1370 makeList("impossible", "forbidden", "incomprehensible", "ineffable", "unearthly", "abominable", "unspeakable", "indescribable"), 1371 "gaze`noun`", 1372 makeList("eye", "gaze", "stare", "observation", "purveyance", "watch"), 1373 "pain`noun`", 1374 makeList("pain", "agony", "misery", "excruciation", "torture"), 1375 "god`noun`", 1376 makeList("god", "deity", "ruler", "king", "father", "lord", "lordship"), 1377 "goddess`noun`", 1378 makeList("goddess", "deity", "ruler", "queen", "mother", "lady", "ladyship"), 1379 "hero`noun`", 1380 makeList("hero", "champion", "savior", "crusader", "knight"), 1381 "heroes`nouns`", 1382 makeList("heroes", "champions", "saviors", "crusaders", "knights"), 1383 "heroine`noun`", 1384 makeList("heroine", "champion", "savior", "crusader", "knight", "maiden"), 1385 "heroines`nouns`", 1386 makeList("heroines", "champions", "saviors", "crusaders", "knights", "maidens"), 1387 "popular`adj`", 1388 makeList("beloved", "adored", "revered", "worshipped"), 1389 "unpopular`adj`", 1390 makeList("reviled", "despised", "hated", "loathed"), 1391 "glyph`noun`", 1392 makeList("glyph", "sign", "symbol", "sigil", "seal", "mark"), 1393 "glyph`nouns`", 1394 makeList("glyphs", "signs", "symbols", "sigils", "seals", "marks"), 1395 "power`noun`", 1396 makeList("power", "force", "potency", "strength", "authority", "dominance"), 1397 "power`adj`", 1398 makeList("powerful", "forceful", "potent", "strong", "authoritative", "dominant"), 1399 "plant`term`", plantTerms , 1400 "fruit`term`", fruitTerms , 1401 "nut`term`", nutTerms , 1402 "flower`term`", flowerTerms, 1403 "potion`term`", potionTerms 1404 ); 1405 public static final OrderedMap<String, ArrayList<String>> 1406 adjective = new OrderedMap<>(categories), 1407 noun = new OrderedMap<>(categories), 1408 nouns = new OrderedMap<>(categories); 1409 public static final OrderedMap<CharSequence, FakeLanguageGen> languages = new OrderedMap<CharSequence, FakeLanguageGen>( 1410 FakeLanguageGen.registeredNames.length, Hashers.caseInsensitiveStringHasher); 1411 static { 1412 for (int i = 0; i < FakeLanguageGen.registeredNames.length; i++) { 1413 languages.put(FakeLanguageGen.registeredNames[i].replace(' ', '_').toLowerCase() + "`gen`", FakeLanguageGen.registered[i]); 1414 } 1415 } 1416 public static final OrderedMap<CharSequence, Integer> numbers = new OrderedMap<CharSequence, Integer>( 1417 21, Hashers.caseInsensitiveStringHasher 1418 ).putPairs( 1419 "zero`noun`", 0, 1420 "one`noun`", 1, 1421 "two`noun`", 2, 1422 "three`noun`", 3, 1423 "four`noun`", 4, 1424 "five`noun`", 5, 1425 "six`noun`", 6, 1426 "seven`noun`", 7, 1427 "eight`noun`", 8, 1428 "nine`noun`", 9, 1429 "ten`noun`", 10, 1430 "eleven`noun`", 11, 1431 "twelve`noun`", 12, 1432 "thirteen`noun`", 13, 1433 "fourteen`noun`", 14, 1434 "fifteen`noun`", 15, 1435 "sixteen`noun`", 16, 1436 "seventeen`noun`", 17, 1437 "eighteen`noun`", 18, 1438 "nineteen`noun`", 19, 1439 "twenty`noun`", 20 1440 ), 1441 numberAdjectives = new OrderedMap<CharSequence, Integer>( 1442 21, Hashers.caseInsensitiveStringHasher 1443 ).putPairs( 1444 "zero`adj`", 0, 1445 "one`adj`", 1, 1446 "two`adj`", 2, 1447 "three`adj`", 3, 1448 "four`adj`", 4, 1449 "five`adj`", 5, 1450 "six`adj`", 6, 1451 "seven`adj`", 7, 1452 "eight`adj`", 8, 1453 "nine`adj`", 9, 1454 "ten`adj`", 10, 1455 "eleven`adj`", 11, 1456 "twelve`adj`", 12, 1457 "thirteen`adj`", 13, 1458 "fourteen`adj`", 14, 1459 "fifteen`adj`", 15, 1460 "sixteen`adj`", 16, 1461 "seventeen`adj`", 17, 1462 "eighteen`adj`", 18, 1463 "nineteen`adj`", 19, 1464 "twenty`adj`", 20 1465 ); 1466 1467 /** 1468 * Thesaurus preset that changes all text to sound like this speaker: "Desaurus preset dat changez all text to sound 1469 * like dis speakah." You may be familiar with a certain sci-fi game that has orks who sound like this. 1470 */ 1471 public static Thesaurus ORK = new Thesaurus("WAAAAAGH!"); 1472 static { 1473 ORK.alterations.add(new FakeLanguageGen.Alteration("\\bth", "d")); 1474 ORK.alterations.add(new FakeLanguageGen.Alteration("th", "dd")); 1475 ORK.alterations.add(new FakeLanguageGen.Alteration("er\\b", "ah")); 1476 ORK.alterations.add(new FakeLanguageGen.Alteration("es\\b", "ez")); 1477 ORK.addReplacement("the", "da") 1478 .addReplacement("their", "deyr") 1479 .addReplacement("yes", "ya") 1480 .addReplacement("your", "youse") 1481 .addReplacement("yours", "youses") 1482 .addReplacement("going", "gon'") 1483 .addReplacement("and", "an'") 1484 .addReplacement("to", "*snort*") 1485 .addReplacement("rhythm", "riddim") 1486 .addReplacement("get", "git") 1487 .addReplacement("good", "gud"); 1488 1489 // not related to ORK; this filters out synonyms that aren't in the appropriate list 1490 Iterator<String> it = adjective.keySet().iterator(); 1491 while (it.hasNext()){ 1492 if(!it.next().contains("`adj`")) 1493 it.remove(); 1494 } 1495 it = noun.keySet().iterator(); 1496 while (it.hasNext()){ 1497 if(!it.next().contains("`noun`")) 1498 it.remove(); 1499 } 1500 it = nouns.keySet().iterator(); 1501 while (it.hasNext()){ 1502 if(!it.next().contains("`nouns`")) 1503 it.remove(); 1504 } 1505 } 1506 1507 /** 1508 * Gets a stable (large) String that stores all categories this version of Thesaurus knows, as well as all of the 1509 * words each category includes. This can be useful in conjunction with {@link #addArchivedCategories(String)} to 1510 * load a set of categories stored from an earlier version of SquidLib. 1511 * @return a category archive String that can be passed to {@link #addArchivedCategories(String)} 1512 */ 1513 public static String archiveCategories(){ 1514 return Converters.convertOrderedMap( 1515 Converters.convertString, 1516 Converters.convertArrayList(Converters.convertString) 1517 ).stringify(categories); 1518 } 1519}