export interface MarkovOptions { minLength?: number maxLength?: number order?: number startWith?: string maxAttempts?: number } export const buildMarkovChain = (words: string[], order: number, startWith?: string): Map => { const chain: Map = new Map() for (const word of words) { const paddedWord = startWith ? startWith + word : word for (let i = 0; i <= paddedWord.length - order; i++) { const state = paddedWord.slice(i, i + order) const nextChar = i + order < paddedWord.length ? paddedWord.charAt(i + order) : "" if (!chain.has(state)) { chain.set(state, []) } ;(chain.get(state) as string[]).push(nextChar) } } return chain } export const generateWordFromChain = ( chain: Map, minLength: number, maxLength: number, maxAttempts: number ): string | null => { let attempts = 0 while (attempts < maxAttempts) { attempts++ // Choose random starting state const states = Array.from(chain.keys()) if (states.length === 0) return null const currentIndex = Math.floor(Math.random() * states.length) let current = states[currentIndex] as string let word = current // Generate until we reach desired length while (word.length < maxLength) { const possibilities = chain.get(current) || [] if (possibilities.length === 0) break const nextIndex = Math.floor(Math.random() * possibilities.length) const next = possibilities[nextIndex] as string if (next === "") break // End of word word += next current = current.slice(1) + next } // Check if word meets length requirements if (word.length >= minLength && word.length <= maxLength) { return word } } return null // Could not generate valid word }