function shuffled(list) { var newlist = []; for (var i = 0; i < list.length; i++) { newlist.push(list[i]); } for (var i = list.length - 1; i > 0; i--) { var tmp = newlist[i]; var j = randrange(i); newlist[i] = newlist[j]; newlist[j] = tmp; } return newlist; } function choose(list, exponent) { exponent = exponent || 1; return list[Math.floor(Math.pow(Math.random(), exponent) * list.length)]; } function randrange(lo, hi) { if (hi == undefined) { hi = lo; lo = 0; } return Math.floor(Math.random() * (hi - lo)) + lo; } function join(list, sep) { if (list.length == 0) return ''; sep = sep || ''; var s = list[0]; for (var i = 1; i < list.length; i++) { s += sep; s += list[i]; } return s; } function capitalize(word) { return word[0].toUpperCase() + word.slice(1); } function spell(lang, syll) { if (lang.noortho) return syll; var s = ''; for (var i = 0; i < syll.length; i++) { var c = syll[i]; s += lang.cortho[c] || lang.vortho[c] || defaultOrtho[c] || c; } return s; } function makeSyllable(lang) { while (true) { var syll = ""; for (var i = 0; i < lang.structure.length; i++) { var ptype = lang.structure[i]; if (lang.structure[i+1] == '?') { i++; if (Math.random() < 0.5) { continue; } } syll += choose(lang.phonemes[ptype], lang.exponent); } var bad = false; for (var i = 0; i < lang.restricts.length; i++) { if (lang.restricts[i].test(syll)) { bad = true; break; } } if (bad) continue; return spell(lang, syll); } } function getMorpheme(lang, key) { if (lang.nomorph) { return makeSyllable(lang); } key = key || ''; var list = lang.morphemes[key] || []; var extras = 10; if (key) extras = 1; while (true) { var n = randrange(list.length + extras); if (list[n]) return list[n]; var morph = makeSyllable(lang); var bad = false; for (var k in lang.morphemes) { if (lang.morphemes[k].includes(morph)) { bad = true; break; } } if (bad) continue; list.push(morph); lang.morphemes[key] = list; return morph; } } function makeWord(lang, key) { var nsylls = randrange(lang.minsyll, lang.maxsyll + 1); var w = ''; var keys = []; keys[randrange(nsylls)] = key; for (var i = 0; i < nsylls; i++) { w += getMorpheme(lang, keys[i]); } return w; } function getWord(lang, key) { key = key || ''; var ws = lang.words[key] || []; var extras = 3; if (key) extras = 2; while (true) { var n = randrange(ws.length + extras); var w = ws[n]; if (w) { return w; } w = makeWord(lang, key); var bad = false; for (var k in lang.words) { if (lang.words[k].includes(w)) { bad = true; break; } } if (bad) continue; ws.push(w); lang.words[key] = ws; return w; } } function makeName(lang, key) { key = key || ''; lang.genitive = lang.genitive || getMorpheme(lang, 'of'); lang.definite = lang.definite || getMorpheme(lang, 'the'); while (true) { var name = null; if (Math.random() < 0.5) { name = capitalize(getWord(lang, key)); } else { var w1 = capitalize(getWord(lang, Math.random() < 0.6 ? key : '')); var w2 = capitalize(getWord(lang, Math.random() < 0.6 ? key : '')); if (w1 == w2) continue; if (Math.random() > 0.5) { name = join([w1, w2], lang.joiner); } else { name = join([w1, lang.genitive, w2], lang.joiner); } } if (Math.random() < 0.1) { name = join([lang.definite, name], lang.joiner); } if ((name.length < lang.minchar) || (name.length > lang.maxchar)) continue; var used = false; for (var i = 0; i < lang.names.length; i++) { var name2 = lang.names[i]; if ((name.indexOf(name2) != -1) || (name2.indexOf(name) != -1)) { used = true; break; } } if (used) continue; lang.names.push(name); return name; } } function makeBasicLanguage() { return { phonemes: { C: "ptkmnls", V: "aeiou", S: "s", F: "mn", L: "rl" }, structure: "CVC", exponent: 2, restricts: [], cortho: {}, vortho: {}, noortho: true, nomorph: true, nowordpool: true, minsyll: 1, maxsyll: 1, morphemes: {}, words: {}, names: [], joiner: ' ', maxchar: 12, minchar: 5 }; } function makeOrthoLanguage() { var lang = makeBasicLanguage(); lang.noortho = false; return lang; } function makeRandomLanguage() { var lang = makeBasicLanguage(); lang.noortho = false; lang.nomorph = false; lang.nowordpool = false; lang.phonemes.C = shuffled(choose(consets, 2).C); lang.phonemes.V = shuffled(choose(vowsets, 2).V); lang.phonemes.L = shuffled(choose(lsets, 2).L); lang.phonemes.S = shuffled(choose(ssets, 2).S); lang.phonemes.F = shuffled(choose(fsets, 2).F); lang.structure = choose(syllstructs); lang.restricts = ressets[2].res; lang.cortho = choose(corthsets, 2).orth; lang.vortho = choose(vorthsets, 2).orth; lang.minsyll = randrange(1, 3); if (lang.structure.length < 3) lang.minsyll++; lang.maxsyll = randrange(lang.minsyll + 1, 7); lang.joiner = choose(' -'); return lang; } var defaultOrtho = { 'ʃ': 'sh', 'ʒ': 'zh', 'ʧ': 'ch', 'ʤ': 'j', 'ŋ': 'ng', 'j': 'y', 'x': 'kh', 'ɣ': 'gh', 'ʔ': '‘', A: "á", E: "é", I: "í", O: "ó", U: "ú" }; var corthsets = [ { name: "Default", orth: {} }, { name: "Slavic", orth: { 'ʃ': 'š', 'ʒ': 'ž', 'ʧ': 'č', 'ʤ': 'ǧ', 'j': 'j' } }, { name: "German", orth: { 'ʃ': 'sch', 'ʒ': 'zh', 'ʧ': 'tsch', 'ʤ': 'dz', 'j': 'j', 'x': 'ch' } }, { name: "French", orth: { 'ʃ': 'ch', 'ʒ': 'j', 'ʧ': 'tch', 'ʤ': 'dj', 'x': 'kh' } }, { name: "Chinese (pinyin)", orth: { 'ʃ': 'x', 'ʧ': 'q', 'ʤ': 'j', } } ]; var vorthsets = [ { name: "Ácutes", orth: {} }, { name: "Ümlauts", orth: { A: "ä", E: "ë", I: "ï", O: "ö", U: "ü" } }, { name: "Welsh", orth: { A: "â", E: "ê", I: "y", O: "ô", U: "w" } }, { name: "Diphthongs", orth: { A: "au", E: "ei", I: "ie", O: "ou", U: "oo" } }, { name: "Doubles", orth: { A: "aa", E: "ee", I: "ii", O: "oo", U: "uu" } } ]; var consets = [ { name: "Minimal", C: "ptkmnls" }, { name: "English-ish", C: "ptkbdgmnlrsʃzʒʧ" }, { name: "Pirahã (very simple)", C: "ptkmnh" }, { name: "Hawaiian-ish", C: "hklmnpwʔ" }, { name: "Greenlandic-ish", C: "ptkqvsgrmnŋlj" }, { name: "Arabic-ish", C: "tksʃdbqɣxmnlrwj" }, { name: "Arabic-lite", C: "tkdgmnsʃ" }, { name: "English-lite", C: "ptkbdgmnszʒʧhjw" } ]; var ssets = [ { name: "Just s", S: "s" }, { name: "s ʃ", S: "sʃ" }, { name: "s ʃ f", S: "sʃf" } ]; var lsets = [ { name: "r l", L: "rl" }, { name: "Just r", L: "r" }, { name: "Just l", L: "l" }, { name: "w j", L: "wj" }, { name: "r l w j", L: "rlwj" } ]; var fsets = [ { name: "m n", F: "mn" }, { name: "s k", F: "sk" }, { name: "m n ŋ", F: "mnŋ" }, { name: "s ʃ z ʒ", F: "sʃzʒ" } ]; var vowsets = [ { name: "Standard 5-vowel", V: "aeiou" }, { name: "3-vowel a i u", V: "aiu" }, { name: "Extra A E I", V: "aeiouAEI" }, { name: "Extra U", V: "aeiouU" }, { name: "5-vowel a i u A I", V: "aiuAI" }, { name: "3-vowel e o u", V: "eou" }, { name: "Extra A O U", V: "aeiouAOU" } ]; var syllstructs = [ "CVC", "CVV?C", "CVVC?", "CVC?", "CV", "VC", "CVF", "C?VC", "CVF?", "CL?VC", "CL?VF", "S?CVC", "S?CVF", "S?CVC?", "C?VF", "C?VC?", "C?VF?", "C?L?VC", "VC", "CVL?C?", "C?VL?C", "C?VLC?"]; var ressets = [ { name: "None", res: [] }, { name: "Double sounds", res: [/(.)\1/] }, { name: "Doubles and hard clusters", res: [/[sʃf][sʃ]/, /(.)\1/, /[rl][rl]/] } ];