Skip to content

Instantly share code, notes, and snippets.

@lostfictions
Last active January 25, 2024 05:03
Show Gist options
  • Save lostfictions/520a64a801ab7e6cd4035d1a3c67ea6d to your computer and use it in GitHub Desktop.
Save lostfictions/520a64a801ab7e6cd4035d1a3c67ea6d to your computer and use it in GitHub Desktop.
tiny tracery, take two
const randomInArray = arr => arr[Math.floor(Math.random() * arr.length)]
const isVowel = char => /^[aeiou]$/i.test(char)
const matcher = /\[([^\[\]]+)\]/g
export default function trace({concepts, concept, maxCycles = 10, seen = {}, modifierList = defaultModifiers}) {
const [resolvedConcept, ...modifierChunks] = concept.split('|')
const modifiers = modifierChunks
.map(chunk => {
const [modifierName, ...args] = chunk.split(' ')
return [modifierList[modifierName], args]
})
.filter(resolved => resolved[0])
if(!concepts[resolvedConcept]) {
return `{error: unknown concept "${resolvedConcept}"}`
}
const traceResult = randomInArray(concepts[resolvedConcept])
.replace(matcher, (_, nextConcept) => {
if(seen[nextConcept] > maxCycles) {
return '{error: max cycles exceeded}'
}
const nextSeen = Object.assign({}, seen)
nextSeen[nextConcept] = nextSeen[nextConcept] + 1 || 1
return trace({
concepts,
concept: nextConcept,
maxCycles,
seen: nextSeen
})
})
return modifiers.reduce((result, m) => m[0](result, ...m[1]), traceResult)
}
var defaultModifiers = {
s: word => {
switch(word[word.length - 1].toLowerCase()) {
case 's':
case 'h':
case 'x':
return word + 'es'
case 'y':
return !isVowel(word[word.length - 2])
? word.substring(0, word.length - 1) + 'ies'
: word + 's'
default:
return word + 's'
}
},
a: word => {
switch(true) {
case word[0].toLowerCase() === 'u' && word.length > 2 && word[2].toLowerCase() === 'i':
default:
return 'a ' + word
case isVowel(word[0]):
return 'an ' + word
}
},
ed : word => {
switch(word[word.length - 1]) {
case 'e':
return word + 'd'
case 'y':
return word.length > 1 && !isVowel(word[word.length - 2])
? word.substring(0, word.length - 1) + 'ied'
: word + 'd'
default:
return word + 'ed'
}
},
ing : word => {
if(word[word.length - 1].toLowerCase() === 'e')
return word.substring(0, word.length - 1) + 'ing'
return word + 'ing'
},
upper : word => word.toUpperCase(),
cap : word => word[0].toUpperCase() + word.substring(1),
swap : (word, search, replacement) => word.split(search).join(replacement)
}
defaultModifiers['an'] = defaultModifiers['a']
defaultModifiers['es'] = defaultModifiers['s']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment