lychees's picture
Upload 569 files
87b3b3a
/**
* @class (Markov process)-based string generator.
* Copied from a <a href="http://www.roguebasin.roguelikedevelopment.org/index.php?title=Names_from_a_high_order_Markov_Process_and_a_simplified_Katz_back-off_scheme">RogueBasin article</a>.
* Offers configurable order and prior.
* @param {object} [options]
* @param {bool} [options.words=false] Use word mode?
* @param {int} [options.order=3]
* @param {float} [options.prior=0.001]
*/
ROT.StringGenerator = function(options) {
this._options = {
words: false,
order: 3,
prior: 0.001
}
for (var p in options) { this._options[p] = options[p]; }
this._boundary = String.fromCharCode(0);
this._suffix = this._boundary;
this._prefix = [];
for (var i=0;i<this._options.order;i++) { this._prefix.push(this._boundary); }
this._priorValues = {};
this._priorValues[this._boundary] = this._options.prior;
this._data = {};
}
/**
* Remove all learning data
*/
ROT.StringGenerator.prototype.clear = function() {
this._data = {};
this._priorValues = {};
}
/**
* @returns {string} Generated string
*/
ROT.StringGenerator.prototype.generate = function() {
var result = [this._sample(this._prefix)];
while (result[result.length-1] != this._boundary) {
result.push(this._sample(result));
}
return this._join(result.slice(0, -1));
}
/**
* Observe (learn) a string from a training set
*/
ROT.StringGenerator.prototype.observe = function(string) {
var tokens = this._split(string);
for (var i=0; i<tokens.length; i++) {
this._priorValues[tokens[i]] = this._options.prior;
}
tokens = this._prefix.concat(tokens).concat(this._suffix); /* add boundary symbols */
for (var i=this._options.order; i<tokens.length; i++) {
var context = tokens.slice(i-this._options.order, i);
var event = tokens[i];
for (var j=0; j<context.length; j++) {
var subcontext = context.slice(j);
this._observeEvent(subcontext, event);
}
}
}
ROT.StringGenerator.prototype.getStats = function() {
var parts = [];
var priorCount = 0;
for (var p in this._priorValues) { priorCount++; }
priorCount--; /* boundary */
parts.push("distinct samples: " + priorCount);
var dataCount = 0;
var eventCount = 0;
for (var p in this._data) {
dataCount++;
for (var key in this._data[p]) {
eventCount++;
}
}
parts.push("dictionary size (contexts): " + dataCount);
parts.push("dictionary size (events): " + eventCount);
return parts.join(", ");
}
/**
* @param {string}
* @returns {string[]}
*/
ROT.StringGenerator.prototype._split = function(str) {
return str.split(this._options.words ? /\s+/ : "");
}
/**
* @param {string[]}
* @returns {string}
*/
ROT.StringGenerator.prototype._join = function(arr) {
return arr.join(this._options.words ? " " : "");
}
/**
* @param {string[]} context
* @param {string} event
*/
ROT.StringGenerator.prototype._observeEvent = function(context, event) {
var key = this._join(context);
if (!(key in this._data)) { this._data[key] = {}; }
var data = this._data[key];
if (!(event in data)) { data[event] = 0; }
data[event]++;
}
/**
* @param {string[]}
* @returns {string}
*/
ROT.StringGenerator.prototype._sample = function(context) {
context = this._backoff(context);
var key = this._join(context);
var data = this._data[key];
var available = {};
if (this._options.prior) {
for (var event in this._priorValues) { available[event] = this._priorValues[event]; }
for (var event in data) { available[event] += data[event]; }
} else {
available = data;
}
return this._pickRandom(available);
}
/**
* @param {string[]}
* @returns {string[]}
*/
ROT.StringGenerator.prototype._backoff = function(context) {
if (context.length > this._options.order) {
context = context.slice(-this._options.order);
} else if (context.length < this._options.order) {
context = this._prefix.slice(0, this._options.order - context.length).concat(context);
}
while (!(this._join(context) in this._data) && context.length > 0) { context = context.slice(1); }
return context;
}
ROT.StringGenerator.prototype._pickRandom = function(data) {
var total = 0;
for (var id in data) {
total += data[id];
}
var random = ROT.RNG.getUniform()*total;
var part = 0;
for (var id in data) {
part += data[id];
if (random < part) { return id; }
}
}