-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchain.js
112 lines (94 loc) · 2.91 KB
/
chain.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// copied from https://github.com/swang/pick-one-by-weight
export function pickOneByWeight (anObj) {
let _keys = Object.keys(anObj)
const sum = _keys.reduce((p, c) => p + anObj[c], 0)
if (!Number.isFinite(sum)) {
throw new Error('All values in object must be a numeric value')
}
let choose = ~~(Math.random() * sum)
for (let i = 0, count = 0; i < _keys.length; i++) {
count += anObj[_keys[i]]
if (count > choose) {
return _keys[i]
}
}
}
// copied from https://github.com/swang/markovchain
const isType = (t) => Object.prototype.toString.call(t).slice(8, -1).toLowerCase()
class MarkovChain {
constructor (contents, normFn = (w) => w.toLowerCase()) {
this.wordBank = Object.create(null)
this.sentence = ''
this._normalizeFn = normFn
this.parseBy = /(?:\?)/ig
this.parse(contents)
}
startFn (wordList) {
const k = Object.keys(wordList)
const l = k.length
return k[~~(Math.random() * l)]
}
endFn () {
return this.sentence.split(' ').length > 7
}
process () {
let curWord = this.startFn(this.wordBank)
this.sentence = curWord
while (this.wordBank[curWord] && !this.endFn()) {
curWord = pickOneByWeight(this.wordBank[curWord])
this.sentence += ' ' + curWord
}
return this.sentence
}
parse (text = '', parseBy = this.parseBy) {
text.split(parseBy).forEach((lines) => {
const words = lines.split(/ |$\n/).filter((w) => w.trim() !== '')
for (let i = 0; i < words.length - 1; i++) {
const curWord = this._normalize(words[i])
const nextWord = this._normalize(words[i + 1])
if (!this.wordBank[curWord]) {
this.wordBank[curWord] = Object.create(null)
}
if (!this.wordBank[curWord][nextWord]) {
this.wordBank[curWord][nextWord] = 1
} else {
this.wordBank[curWord][nextWord] += 1
}
}
})
return this
}
start (fnStr) {
const startType = isType(fnStr)
if (startType === 'string') {
this.startFn = () => fnStr
} else if (startType === 'function') {
this.startFn = (wordList) => fnStr(wordList)
} else {
throw new Error('Must pass a function, or string into start()')
}
return this
}
end (fnStrOrNum) {
const endType = isType(fnStrOrNum)
if (endType === 'function') {
this.endFn = () => fnStrOrNum(this.sentence)
} else if (endType === 'string') {
this.endFn = () => this.sentence.split(' ').slice(-1)[0] === fnStrOrNum
} else if (endType === 'number' || fnStrOrNum === undefined) {
fnStrOrNum = fnStrOrNum || Infinity
this.endFn = () => this.sentence.split(' ').length > fnStrOrNum
} else {
throw new Error('Must pass a function, string or number into end()')
}
return this
}
_normalize (word) {
return this._normalizeFn(word)
}
normalize (fn) {
this._normalizeFn = fn
return this
}
}
export default MarkovChain