You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

487 lines
9.8 KiB
JavaScript

4 years ago
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]/]
}
];