import Random from "./Random"; const bow = Symbol("Beginning of word"); const eow = Symbol("Ending of word"); export { bow, eow }; type StringOrSymbol = symbol | string; type RawDataForMapping = Array; interface TempMapping { // @ts-ignore Waiting for https://github.com/Microsoft/TypeScript/pull/26797 [key: StringOrSymbol]: { // @ts-ignore Waiting for https://github.com/Microsoft/TypeScript/pull/26797 [key: StringOrSymbol]: number; }; } export interface Mapping { // @ts-ignore Waiting for https://github.com/Microsoft/TypeScript/pull/26797 [key: StringOrSymbol]: Array<[number, StringOrSymbol]>; } interface MarkovOptions { minLen: number; maxLen: number; } export const generateMapping = (raw: RawDataForMapping): Mapping => { const mapping: TempMapping = { [bow]: { [eow]: 0, }, }; for (const word of raw) { const lowWord = word.toLowerCase(); for (let i = 0; i < lowWord.length; ++i) { const char = lowWord[i]; if (i === 0) { if (!(char in mapping[bow])) { mapping[bow][char] = 0; } mapping[bow][char] += 1; } if (!(char in mapping)) { mapping[char] = { [eow]: 0, }; } let nextChar: StringOrSymbol = eow; if (i < lowWord.length - 1) { nextChar = lowWord[i + 1]; } if (!(nextChar in mapping[char])) { mapping[char][nextChar] = 0; } mapping[char][nextChar] += 1; } } const keys = (Object.keys(mapping) as Array).concat([bow]); for (const key of keys) { const entries: Array<[StringOrSymbol, number]> = Object.entries(mapping[key]); if (eow in mapping[key]) { entries.push([eow, mapping[key][eow]]); } mapping[key] = entries.map(entry => entry.reverse()); } return mapping; }; export const generateChain = ( mapping: Mapping, random: Random, { minLen, maxLen }: MarkovOptions, ) => { let word = ""; let curLetter: StringOrSymbol = bow; for (let i = 0; i < maxLen; ++i) { curLetter = random.weighted(mapping[curLetter]); if (curLetter === eow) { if (i >= minLen) { break; } else { curLetter = random.weighted(mapping[bow]); } } word += curLetter as string; } return word; };