001package squidpony; 002 003import squidpony.squidmath.GapShuffler; 004import squidpony.squidmath.StatefulRNG; 005 006/** 007 * Combines {@link Messaging} with {@link Thesaurus} and optionally {@link NaturalLanguageCipher} to make variations on 008 * a sentence structure. 009 * <br> 010 * Created by Tommy Ettinger on 11/20/2017. 011 */ 012public class ProceduralMessaging { 013 /** 014 * Data class that stores a name String and one or more Strings that may be used as part of a title with that name, 015 * typically using categories from {@link Thesaurus} to add variety. 016 */ 017 public static class AssociatedName { 018 public String name; 019 public Messaging.NounTrait pronoun; 020 public GapShuffler<String> themes, titles; 021 public StatefulRNG srng; 022 023 /** 024 * Creates an AssociatedName with the being's name as a String and any associated themes and titles as String 025 * arrays, with a boolean after the name that determines whether the name should be "translated" using a 026 * NaturalLanguageCipher to some other form. If you gave this: 027 * <code>"Brunhilda", true, new String[]{"ice`noun`"}, "Goddess`noun` of Ice`nouns`", "Winter-Empress`noun`", "Heroine`noun` of the North"</code>, 028 * it could use any of the terms in {@link Thesaurus} associated with the category {@code "ice`noun`} as themes, 029 * could generate titles like "Mother of Blizzards", "Winter-Queen", and "Maiden of the North", and would not 030 * actually show the name "Brunhilda" in use, instead producing some similar-length name using the 031 * NaturalLanguageCipher that a ProceduralMessaging is created with (defaulting to generic fantasy names). 032 * This overload always treats the being as if it is being addressed directly, in second-person singular form. 033 * @param name the String name for the being, which will be changed if {@code cipherName} is true 034 * @param cipherName if true, the name will be changed using a NaturalLanguageCipher before being shown 035 * @param themes a String array (which may be null) of words that may appear more often regarding this being 036 * @param titles a String array or vararg (which should probably not be null) of special titles for the being 037 */ 038 public AssociatedName(String name, boolean cipherName, String[] themes, String... titles) 039 { 040 this.name = cipherName ? "[?]" + name + "[?]" : name; 041 this.pronoun = Messaging.NounTrait.SECOND_PERSON_SINGULAR; 042 srng = new StatefulRNG(name); 043 this.themes = (themes == null) ? null : new GapShuffler<String>(themes, srng); 044 this.titles = (titles == null) ? null : new GapShuffler<String>(titles, srng); 045 } 046 /** 047 * Creates an AssociatedName with the being's name as a String and any associated themes and titles as String 048 * arrays, with a boolean after the name that determines whether the name should be "translated" using a 049 * NaturalLanguageCipher to some other form. If you gave this: 050 * <code>"Brunhilda", true, new String[]{"ice`noun`"}, "Goddess`noun` of Ice`nouns`", "Winter-Empress`noun`", "Heroine`noun` of the North"</code>, 051 * it could use any of the terms in {@link Thesaurus} associated with the category {@code "ice`noun`} as themes, 052 * could generate titles like "Mother of Blizzards", "Winter-Queen", and "Maiden of the North", and would not 053 * actually show the name "Brunhilda" in use, instead producing some similar-length name using the 054 * NaturalLanguageCipher that a ProceduralMessaging is created with (defaulting to generic fantasy names). 055 * This overload allows the {@link Messaging.NounTrait} to be specified, which allows various ways of addressing 056 * the being (first person, second person, or third person; singular or plural; various gender options in the 057 * third person). 058 * @param name the String name for the being, which will be changed if {@code cipherName} is true 059 * @param cipherName if true, the name will be changed using a NaturalLanguageCipher before being shown 060 * @param pronoun a NounTrait enum that designates how this being should be addressed (often second person singular, but not always) 061 * @param themes a String array (which may be null) of words that may appear more often regarding this being 062 * @param titles a String array or vararg (which should probably not be null) of special titles for the being 063 */ 064 public AssociatedName(String name, boolean cipherName, Messaging.NounTrait pronoun, String[] themes, String... titles) { 065 this.name = cipherName ? "[?]" + name + "[?]" : name; 066 this.pronoun = pronoun; 067 srng = new StatefulRNG(name); 068 this.themes = (themes == null) ? null : new GapShuffler<String>(themes, srng); 069 this.titles = (titles == null) ? null : new GapShuffler<String>(titles, srng); 070 } 071 } 072 public Thesaurus thesaurus; 073 public NaturalLanguageCipher language; 074 075 public ProceduralMessaging() { 076 thesaurus = new Thesaurus(); 077 language = new NaturalLanguageCipher(FakeLanguageGen.FANTASY_NAME); 078 } 079 public ProceduralMessaging(long seed) 080 { 081 thesaurus = new Thesaurus(seed); 082 language = new NaturalLanguageCipher(FakeLanguageGen.FANTASY_NAME); 083 } 084 public ProceduralMessaging(long seed, FakeLanguageGen nameLanguage) 085 { 086 thesaurus = new Thesaurus(seed); 087 language = new NaturalLanguageCipher(nameLanguage == null ? FakeLanguageGen.FANTASY_NAME : nameLanguage); 088 } 089 public ProceduralMessaging(Thesaurus existingThesaurus, NaturalLanguageCipher existingLanguage) 090 { 091 thesaurus = (existingThesaurus == null) 092 ? new Thesaurus() 093 : existingThesaurus; 094 language = (existingLanguage == null) 095 ? new NaturalLanguageCipher(FakeLanguageGen.FANTASY_NAME) 096 : existingLanguage; 097 } 098 public String transform(CharSequence message, String userName, Messaging.NounTrait userTrait, String targetName, Messaging.NounTrait targetTrait) 099 { 100 return language.cipherMarkup(thesaurus.process(Messaging.transform(message, userName, userTrait, targetName, targetTrait))); 101 } 102 public String transform(CharSequence message, AssociatedName user, String targetName, Messaging.NounTrait targetTrait) 103 { 104 return language.cipherMarkup(thesaurus.process(Messaging.transform(message, user.name, user.pronoun, targetName, targetTrait, 105 (user.titles == null) ? "The Great" : user.titles.next(), (user.themes == null) ? "uncertainty" : user.themes.next()))); 106 } 107 public String transform(CharSequence message, String userName, Messaging.NounTrait userTrait, AssociatedName target) 108 { 109 return language.cipherMarkup(thesaurus.process(Messaging.transform(message, userName, userTrait, target.name, target.pronoun, 110 (target.titles == null) ? "The Great" : target.titles.next(), (target.themes == null) ? "uncertainty" : target.themes.next()))); 111 } 112}