glossa 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/glossa/language.rb +313 -313
  3. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2b47036eb93fd38c2ab9647f4cc24dece0ec6bfc
4
- data.tar.gz: f8535e218e76b8adee01c132bad266ed7dbcf879
3
+ metadata.gz: 5e411201740c0977631838ecc4e8e87341fa5918
4
+ data.tar.gz: 6c5ac0dbfdb66aefff3fb72c832c57748b32920c
5
5
  SHA512:
6
- metadata.gz: faf673d1bbf5f986d170d164b889edb64fedde2401b232da04ddb5866f54b98105677e9efad7f10a503675eb8a3ccd179df486ce33fe854c134191374ccb1043
7
- data.tar.gz: 95f9e6c0f064c07e06e18d27dbb3431b3c3cfe1d414e19ac7b14607bc076f1403dbd54cbb19d2d9f04b68b59bbab89c308dbdca446fd5ce211df19b22d3a368f
6
+ metadata.gz: 9cf7fc1d3f04219fee3a989f15fb20e47aa654f7f407e272698669a1408a5166d5f1a5ed721c03391e72821329dcd2576ea2858f757bbd7124c9e6c6f63c95da
7
+ data.tar.gz: d34bcaa18462414143ee0944329460ff0cfefa4684fb8879ec4138cc223e496003e8092e93acb9949bf43044eef9741b4f593967aadc782ca9daa9967824cb28
@@ -1,315 +1,315 @@
1
1
  class Glossa::Language
2
- attr_accessor :phonemes, :structure, :exponent, :restricts, :cortho, :vortho, :noortho, :nomorph, :nowordpool, :minsyll, :maxsyll, :morphemes, :words, :names, :genitive, :definitive, :joiner, :maxchar, :minchar
3
-
4
- def initialize(random = false, options = nil)
5
- if random
6
- @phonemes = {
7
- "C" => shuffle(choose(Glossa::CON_SETS, 2)[:C]),
8
- "V" => shuffle(choose(Glossa::VOW_SETS, 2)[:V]),
9
- "L" => shuffle(choose(Glossa::L_SETS, 2)[:L]),
10
- "S" => shuffle(choose(Glossa::S_SETS, 2)[:S]),
11
- "F" => shuffle(choose(Glossa::F_SETS, 2)[:F])
12
- }
13
- @noortho = false
14
- @nomorph = false
15
- @nowordpool = false
16
- @structure = choose(Glossa::SYLL_STRUCTS)
17
- @exponent = rand(1..3) # a larger exponent means less variation when randomly choosing some elements
18
- @restricts = Glossa::RESTRICT_SETS[2][:res]
19
- @cortho = choose(Glossa::C_ORTH_SETS, 2)[:orth]
20
- @vortho = choose(Glossa::V_ORTH_SETS, 2)[:orth]
21
- @morphemes = {}
22
- @words = {}
23
- @names = []
24
- @joiner = choose(' -')
25
- @maxchar = rand(10...15)
26
- @minchar = rand(3...5)
27
- @minsyll = rand(1...3)
28
- @maxsyll = rand(@minsyll + 1...7)
29
-
30
- if @structure.length < 3
31
- @minsyll += 1;
32
- end
33
- else
34
- options ||= {}
35
- @phonemes = options[:phonemes] || {
36
- "C" => "ptkmnls",
37
- "V" => "aeiou",
38
- "S" => "s",
39
- "F" => "mn",
40
- "L" => "rl"
41
- }
42
- @structure = options['structure'] || "CVC"
43
- @exponent = options['exponent'] || 2
44
- @restricts = options['restricts'] || []
45
- @cortho = options['cortho'] || {}
46
- @vortho = options['vortho'] || {}
47
- @noortho = options['noortho'] || true
48
- @nomorph = options['nomorph'] || true
49
- @nowordpool = options['nowordpool'] || true
50
- @minsyll = options['minsyll'] || 1
51
- @maxsyll = options['maxsyll'] || 1
52
- @morphemes = options['morphemes'] || {}
53
- @words = options['words'] || {}
54
- @names = options['names'] || []
55
- @joiner = options['joiner'] || ' '
56
- @maxchar = options['maxchar'] || 12
57
- @minchar = options['minchar'] || 5
58
- end
59
-
60
- @genitive = get_morpheme('of')
61
- @definitive = get_morpheme('the')
62
- end
63
-
64
- ##
65
- # Takes an array and picks a semi-random element, with the first
66
- # elements weighted more frequently the the last elements by using
67
- # the power of a given exponent.
68
- def choose(list, exponent = 1)
69
- listIndex = ((rand ** exponent) * list.length).floor
70
-
71
- list[listIndex]
72
- end
73
-
74
- ##
75
- # Takes an array or string and shuffles it into a random order.
76
- def shuffle(list)
77
- is_string = list.is_a? String
78
- l = is_string ? list.chars : list
79
- new_list = l.dup
80
-
81
- i = 0;
82
- l.each do |item|
83
- tmp = item
84
- rand_index = rand(i)
85
- new_list[i] = new_list[rand_index]
86
- new_list[rand_index] = tmp
87
- i += 1;
88
- end
89
-
90
- if is_string
91
- return new_list.join
92
- else
93
- return new_list
94
- end
95
- end
96
-
97
- ##
98
- # Takes an array of strings and an optional joiner string
99
- # and concatenates them into a single string
100
- def join(list, sep = '')
101
- return '' if list.length == 0
102
- first_word = list.shift
103
- list.each do |item|
104
- first_word << sep << item
105
- end
106
-
107
- first_word
108
- end
109
-
110
- ##
111
- # Takes an array of phonetic syllables, and spells them using the languages orthography
112
- def spell(syllables)
113
- return syllables if self.noortho
114
- s = syllables.chars
115
- word = ''
116
- s.each do |syllable|
117
- if self.cortho[syllable]
118
- word << self.cortho[syllable]
119
- elsif self.vortho[syllable]
120
- word << self.vortho[syllable]
121
- elsif Glossa::DEFAULT_ORTHO[syllable]
122
- word << Glossa::DEFAULT_ORTHO[syllable]
123
- else
124
- word << syllable
125
- end
126
- end
127
- word
128
- end
129
-
130
- ##
131
- # Creates a spelled (see the spell() method) syllable, according to
132
- # the language's syllable structure. It does this by selecting a semi-random
133
- # phonetic letter for the appropriate phoneme type in the structure, making sure
134
- # that it doesn't conflict with a restricted pattern, and then spells the
135
- # phonetic word according to the language's orthography.
136
- def make_syllable
137
- structure = self.structure.chars
138
-
139
- while true
140
- syll = ''
141
- structure.each do |ptype|
142
- # If the char is '?', skip with 50% chance to remove last character
143
- # (think RegEx usage of '?')
144
- if ptype == '?'
145
- if rand < 0.5
146
- syll = syll[0...syll.length - 1]
147
- end
148
- next
149
- end
150
-
151
- syll << choose(self.phonemes[ptype], self.exponent)
152
- end
153
-
154
- # Make sure this syllable doesn't violate a restriction
155
- bad = false
156
- self.restricts.each do |regex|
157
- if /#{regex}/ =~ syll
158
- bad = true
159
- break
160
- end
161
- end
162
- next if bad
163
-
164
- return spell(syll)
165
- end
166
- end
167
-
168
- ##
169
- # The lowest common-denominator "word" that we will store for our language.
170
- # Morphemes are the smallest unit of language that has a meaning.
171
- # A "morpheme," in this sense, is a unique syllable (spelled according to our orthography)
172
- # with a type (key). Whenever a morpheme is created, we store it in the class instance,
173
- # so as to make sure we don't create duplicates. Morphemes comprise words.
174
- def get_morpheme(key = '')
175
- return make_syllable if self.nomorph
176
-
177
- # make_word will sometimes pass in nil
178
- if key.nil?
179
- key = ''
180
- end
181
-
182
- # Use the morphemes we've already made for this kind of word if possible
183
- list = self.morphemes[key] || []
184
-
185
- # If a key is not specified, make 10 generic morphemes
186
- # otherwise, just make one new one
187
- extras = key == '' ? 10 : 1
188
-
189
- while true
190
- # As more morphemes are created, there is a
191
- # diminishing chance that a new one will be created.
192
- n = rand(list.length + extras)
193
- return list[n] if list[n]
194
-
195
- # An existing morpheme was not selected, so create a new one
196
- morph = make_syllable
197
-
198
- # No duplicates!
199
- bad = false
200
- self.morphemes.each do |k|
201
- next if self.morphemes[k].nil?
202
- if self.morphemes[k].include? morph
203
- bad = true
204
- break
205
- end
206
- end
207
- next if bad
208
- list << morph
209
- self.morphemes[key] = list
210
-
211
- return morph
212
- end
213
- end
214
-
215
- ##
216
- # Given the min- and max-syllables for our language, create a new
217
- # word out of a random number of morphemes.
218
- def make_word(key)
219
- num_sylls = rand(self.minsyll..self.maxsyll)
220
- word = ''
221
- keys = []
222
-
223
- # If a key is defined, then select one of the syllables
224
- # to have a morpheme of that type.
225
- keys[rand(num_sylls)] = key
226
- num_sylls.times { |i| word << get_morpheme(keys[i]) }
227
-
228
- word
229
- end
230
-
231
- ##
232
- # This method has a chance to use an existing word, or create a new
233
- # one of the type (key) specified using the make_word method. If a
234
- # new word is created, it will make sure that it is not duplicating
235
- # an existing word, and then add it to the list of stored words.
236
- def get_word(key = '')
237
- words = self.words[key] || []
238
- extras = key == '' ? 2 : 3
239
-
240
- while true
241
- n = rand(words.length + extras)
242
- existing_word = words[n]
243
- return existing_word if existing_word
244
-
245
- new_word = make_word(key)
246
- bad = false
247
- self.words.each do |word|
248
- if word.include? new_word
249
- bad = true
250
- break
251
- end
252
- end
253
- next if bad
254
- words << new_word
255
- self.words[key] = words
256
-
257
- return new_word
258
- end
259
- end
260
-
261
- ##
262
- # A wrapper with some additional logic around the get_word method.
263
- # make_name will create a name, using get_word (so the words created
264
- # to make up the name will be saved by the specified key), and have
265
- # a 50% chance to add an additional word, and potentially the genitive
266
- # and definitive words. After checking to make sure that it's an ok size
267
- # and isn't already used, it saves and returns the name
268
- def make_name(key = '')
269
- ## If you don't dup these, words will get concatenated onto them during the join process
270
- genitive = self.genitive.dup
271
- definitive = self.definitive.dup
272
-
273
- while true
274
- # 50% chance that the name will be a single word, 50% chance that it will be two words combined somehow
275
- name = rand < 0.5 ? get_word(key) : nil
276
- if name.nil?
277
- # 60% chance that each word will use the same key as invoked
278
- w1_key = rand < 0.6 ? key : ''
279
- w1 = get_word(w1_key).capitalize
280
- w2_key = rand < 0.6 ? key : ''
281
- w2 = get_word(w2_key).capitalize
282
-
283
- # 50% chance to be joined without the lang's genitive word
284
- if rand > 0.5
285
- name = join([w1, w2], self.joiner)
286
- else
287
- name = join([w1, genitive, w2], self.joiner)
288
- end
289
- end
290
-
291
- # 10% to prefix with definitive
292
- name = join([definitive, name], self.joiner) if rand < 0.1
293
-
294
- # Generate another one if this doesn't meet the min- or maxchar reqs
295
- next if (name.length < self.minchar) || (name.length > self.maxchar)
296
-
297
- name.capitalize!
298
-
299
- # Check to see if this string has already been generated
300
- used = false
301
- self.names.each do |lang_name|
302
- if (name.include? lang_name) || (lang_name.include? name)
303
- used = true
304
- break
305
- end
306
- end
307
-
308
- # Start over if this name exists already
309
- next if used
310
-
311
- self.names << name
312
- return name
313
- end
314
- end
2
+ attr_accessor :phonemes, :structure, :exponent, :restricts, :cortho, :vortho, :noortho, :nomorph, :nowordpool, :minsyll, :maxsyll, :morphemes, :words, :names, :genitive, :definitive, :joiner, :maxchar, :minchar
3
+
4
+ def initialize(random = false, options = nil)
5
+ if random
6
+ @phonemes = {
7
+ "C" => shuffle(choose(Glossa::CON_SETS, 2)[:C]),
8
+ "V" => shuffle(choose(Glossa::VOW_SETS, 2)[:V]),
9
+ "L" => shuffle(choose(Glossa::L_SETS, 2)[:L]),
10
+ "S" => shuffle(choose(Glossa::S_SETS, 2)[:S]),
11
+ "F" => shuffle(choose(Glossa::F_SETS, 2)[:F])
12
+ }
13
+ @noortho = false
14
+ @nomorph = false
15
+ @nowordpool = false
16
+ @structure = choose(Glossa::SYLL_STRUCTS)
17
+ @exponent = rand(1..3) # a larger exponent means less variation when randomly choosing some elements
18
+ @restricts = Glossa::RESTRICT_SETS[2][:res]
19
+ @cortho = choose(Glossa::C_ORTH_SETS, 2)[:orth]
20
+ @vortho = choose(Glossa::V_ORTH_SETS, 2)[:orth]
21
+ @morphemes = {}
22
+ @words = {}
23
+ @names = []
24
+ @joiner = choose(' -')
25
+ @maxchar = rand(10...15)
26
+ @minchar = rand(3...5)
27
+ @minsyll = rand(1...3)
28
+ @maxsyll = rand(@minsyll + 1...7)
29
+
30
+ if @structure.length < 3
31
+ @minsyll += 1;
32
+ end
33
+ else
34
+ options ||= {}
35
+ @phonemes = options[:phonemes] || {
36
+ "C" => "ptkmnls",
37
+ "V" => "aeiou",
38
+ "S" => "s",
39
+ "F" => "mn",
40
+ "L" => "rl"
41
+ }
42
+ @structure = options['structure'] || "CVC"
43
+ @exponent = options['exponent'] || 2
44
+ @restricts = options['restricts'] || []
45
+ @cortho = options['cortho'] || {}
46
+ @vortho = options['vortho'] || {}
47
+ @noortho = options['noortho'] || true
48
+ @nomorph = options['nomorph'] || true
49
+ @nowordpool = options['nowordpool'] || true
50
+ @minsyll = options['minsyll'] || 1
51
+ @maxsyll = options['maxsyll'] || 1
52
+ @morphemes = options['morphemes'] || {}
53
+ @words = options['words'] || {}
54
+ @names = options['names'] || []
55
+ @joiner = options['joiner'] || ' '
56
+ @maxchar = options['maxchar'] || 12
57
+ @minchar = options['minchar'] || 5
58
+ end
59
+
60
+ @genitive = get_morpheme('of')
61
+ @definitive = get_morpheme('the')
62
+ end
63
+
64
+ ##
65
+ # Takes an array and picks a semi-random element, with the first
66
+ # elements weighted more frequently the the last elements by using
67
+ # the power of a given exponent.
68
+ def choose(list, exponent = 1)
69
+ if !exponent.kind_of?(Integer) || !exponent.kind_of?(Float) then exponent = exponent.to_i end
70
+ listIndex = ((rand ** exponent) * list.length).floor
71
+ list[listIndex]
72
+ end
73
+
74
+ ##
75
+ # Takes an array or string and shuffles it into a random order.
76
+ def shuffle(list)
77
+ is_string = list.is_a? String
78
+ l = is_string ? list.chars : list
79
+ new_list = l.dup
80
+
81
+ i = 0;
82
+ l.each do |item|
83
+ tmp = item
84
+ rand_index = rand(i)
85
+ new_list[i] = new_list[rand_index]
86
+ new_list[rand_index] = tmp
87
+ i += 1;
88
+ end
89
+
90
+ if is_string
91
+ return new_list.join
92
+ else
93
+ return new_list
94
+ end
95
+ end
96
+
97
+ ##
98
+ # Takes an array of strings and an optional joiner string
99
+ # and concatenates them into a single string
100
+ def join(list, sep = '')
101
+ return '' if list.length == 0
102
+ first_word = list.shift
103
+ list.each do |item|
104
+ first_word << sep << item
105
+ end
106
+
107
+ first_word
108
+ end
109
+
110
+ ##
111
+ # Takes an array of phonetic syllables, and spells them using the languages orthography
112
+ def spell(syllables)
113
+ return syllables if self.noortho
114
+ s = syllables.chars
115
+ word = ''
116
+ s.each do |syllable|
117
+ if self.cortho[syllable]
118
+ word << self.cortho[syllable]
119
+ elsif self.vortho[syllable]
120
+ word << self.vortho[syllable]
121
+ elsif Glossa::DEFAULT_ORTHO[syllable]
122
+ word << Glossa::DEFAULT_ORTHO[syllable]
123
+ else
124
+ word << syllable
125
+ end
126
+ end
127
+ word
128
+ end
129
+
130
+ ##
131
+ # Creates a spelled (see the spell() method) syllable, according to
132
+ # the language's syllable structure. It does this by selecting a semi-random
133
+ # phonetic letter for the appropriate phoneme type in the structure, making sure
134
+ # that it doesn't conflict with a restricted pattern, and then spells the
135
+ # phonetic word according to the language's orthography.
136
+ def make_syllable
137
+ structure = self.structure.chars
138
+
139
+ while true
140
+ syll = ''
141
+ structure.each do |ptype|
142
+ # If the char is '?', skip with 50% chance to remove last character
143
+ # (think RegEx usage of '?')
144
+ if ptype == '?'
145
+ if rand < 0.5
146
+ syll = syll[0...syll.length - 1]
147
+ end
148
+ next
149
+ end
150
+
151
+ syll << choose(self.phonemes[ptype], self.exponent)
152
+ end
153
+
154
+ # Make sure this syllable doesn't violate a restriction
155
+ bad = false
156
+ self.restricts.each do |regex|
157
+ if /#{regex}/ =~ syll
158
+ bad = true
159
+ break
160
+ end
161
+ end
162
+ next if bad
163
+
164
+ return spell(syll)
165
+ end
166
+ end
167
+
168
+ ##
169
+ # The lowest common-denominator "word" that we will store for our language.
170
+ # Morphemes are the smallest unit of language that has a meaning.
171
+ # A "morpheme," in this sense, is a unique syllable (spelled according to our orthography)
172
+ # with a type (key). Whenever a morpheme is created, we store it in the class instance,
173
+ # so as to make sure we don't create duplicates. Morphemes comprise words.
174
+ def get_morpheme(key = '')
175
+ return make_syllable if self.nomorph
176
+
177
+ # make_word will sometimes pass in nil
178
+ if key.nil?
179
+ key = ''
180
+ end
181
+
182
+ # Use the morphemes we've already made for this kind of word if possible
183
+ list = self.morphemes[key] || []
184
+
185
+ # If a key is not specified, make 10 generic morphemes
186
+ # otherwise, just make one new one
187
+ extras = key == '' ? 10 : 1
188
+
189
+ while true
190
+ # As more morphemes are created, there is a
191
+ # diminishing chance that a new one will be created.
192
+ n = rand(list.length + extras)
193
+ return list[n] if list[n]
194
+
195
+ # An existing morpheme was not selected, so create a new one
196
+ morph = make_syllable
197
+
198
+ # No duplicates!
199
+ bad = false
200
+ self.morphemes.each do |k|
201
+ next if self.morphemes[k].nil?
202
+ if self.morphemes[k].include? morph
203
+ bad = true
204
+ break
205
+ end
206
+ end
207
+ next if bad
208
+ list << morph
209
+ self.morphemes[key] = list
210
+
211
+ return morph
212
+ end
213
+ end
214
+
215
+ ##
216
+ # Given the min- and max-syllables for our language, create a new
217
+ # word out of a random number of morphemes.
218
+ def make_word(key)
219
+ num_sylls = rand(self.minsyll..self.maxsyll)
220
+ word = ''
221
+ keys = []
222
+
223
+ # If a key is defined, then select one of the syllables
224
+ # to have a morpheme of that type.
225
+ keys[rand(num_sylls)] = key
226
+ num_sylls.times { |i| word << get_morpheme(keys[i]) }
227
+
228
+ word
229
+ end
230
+
231
+ ##
232
+ # This method has a chance to use an existing word, or create a new
233
+ # one of the type (key) specified using the make_word method. If a
234
+ # new word is created, it will make sure that it is not duplicating
235
+ # an existing word, and then add it to the list of stored words.
236
+ def get_word(key = '')
237
+ words = self.words[key] || []
238
+ extras = key == '' ? 2 : 3
239
+
240
+ while true
241
+ n = rand(words.length + extras)
242
+ existing_word = words[n]
243
+ return existing_word if existing_word
244
+
245
+ new_word = make_word(key)
246
+ bad = false
247
+ self.words.each do |word|
248
+ if word.include? new_word
249
+ bad = true
250
+ break
251
+ end
252
+ end
253
+ next if bad
254
+ words << new_word
255
+ self.words[key] = words
256
+
257
+ return new_word
258
+ end
259
+ end
260
+
261
+ ##
262
+ # A wrapper with some additional logic around the get_word method.
263
+ # make_name will create a name, using get_word (so the words created
264
+ # to make up the name will be saved by the specified key), and have
265
+ # a 50% chance to add an additional word, and potentially the genitive
266
+ # and definitive words. After checking to make sure that it's an ok size
267
+ # and isn't already used, it saves and returns the name
268
+ def make_name(key = '')
269
+ ## If you don't dup these, words will get concatenated onto them during the join process
270
+ genitive = self.genitive.dup
271
+ definitive = self.definitive.dup
272
+
273
+ while true
274
+ # 50% chance that the name will be a single word, 50% chance that it will be two words combined somehow
275
+ name = rand < 0.5 ? get_word(key) : nil
276
+ if name.nil?
277
+ # 60% chance that each word will use the same key as invoked
278
+ w1_key = rand < 0.6 ? key : ''
279
+ w1 = get_word(w1_key).capitalize
280
+ w2_key = rand < 0.6 ? key : ''
281
+ w2 = get_word(w2_key).capitalize
282
+
283
+ # 50% chance to be joined without the lang's genitive word
284
+ if rand > 0.5
285
+ name = join([w1, w2], self.joiner)
286
+ else
287
+ name = join([w1, genitive, w2], self.joiner)
288
+ end
289
+ end
290
+
291
+ # 10% to prefix with definitive
292
+ name = join([definitive, name], self.joiner) if rand < 0.1
293
+
294
+ # Generate another one if this doesn't meet the min- or maxchar reqs
295
+ next if (name.length < self.minchar) || (name.length > self.maxchar)
296
+
297
+ name.capitalize!
298
+
299
+ # Check to see if this string has already been generated
300
+ used = false
301
+ self.names.each do |lang_name|
302
+ if (name.include? lang_name) || (lang_name.include? name)
303
+ used = true
304
+ break
305
+ end
306
+ end
307
+
308
+ # Start over if this name exists already
309
+ next if used
310
+
311
+ self.names << name
312
+ return name
313
+ end
314
+ end
315
315
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glossa
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Franklin