mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-15 04:00:12 +01:00
646 lines
16 KiB
JavaScript
646 lines
16 KiB
JavaScript
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is JSIRC Library.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* New Dimensions Consulting, Inc.
|
|
* Portions created by the Initial Developer are Copyright (C) 1999
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Robert Ginda, rginda@ndcico.com, original author
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
* Dissociated Press javascript for the jsbot
|
|
* see: http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?query=dissociated%20press
|
|
*/
|
|
|
|
DP_DEBUG = false;
|
|
|
|
if (DP_DEBUG)
|
|
dpprint = dd;
|
|
else
|
|
dpprint = (function () {});
|
|
|
|
function CDPressMachine()
|
|
{
|
|
|
|
this.wordPivots = new Object();
|
|
this.cleanCounter = 0;
|
|
this.cleanCount = 0;
|
|
|
|
}
|
|
|
|
CDPressMachine.CLEAN_CYCLE = 1000; // list will be trimmed after this many
|
|
// or never if < 1 addPhrase()s
|
|
CDPressMachine.CLEAN_THRESHOLD = 2; // anything <= this will be trimmed
|
|
CDPressMachine.RANDOMIZE_DEPTH = 10; // not used yet
|
|
CDPressMachine.MIN_PHRASE_LENGTH = 3; // requested minimum phrase length
|
|
CDPressMachine.MAX_PHRASE_LENGTH = 8; // requested maximum phrase length
|
|
CDPressMachine.LENGTH_RETRIES = 3 // number of retries per word
|
|
// (to reach maxlen)
|
|
CDPressMachine.WORD_PATTERN = /[\x21-\x7e]+/; // pattern for words
|
|
|
|
/**
|
|
* Adds a phrase to the engine
|
|
*/
|
|
CDPressMachine.prototype.addPhrase =
|
|
function DPM_addPhrase (strPhrase, weight)
|
|
{
|
|
if (strPhrase == "")
|
|
return;
|
|
|
|
this.cleanCounter++;
|
|
if ((CDPressMachine.CLEAN_CYCLE >= 1) &&
|
|
(this.cleanCounter >= CDPressMachine.CLEAN_CYCLE))
|
|
{
|
|
dpprint ("** cleaning list");
|
|
|
|
this.cleanCounter = 0;
|
|
this.trimList (CDPressMachine.CLEAN_THRESHOLD);
|
|
this.cleancount++;
|
|
}
|
|
|
|
strPhrase = strPhrase.toLowerCase();
|
|
|
|
/* split the phrase */
|
|
var aryWordMatches = strPhrase.split (" ");
|
|
var previousWord = aryWordMatches[aryWordMatches.length - 1];
|
|
previousWord = previousWord.match(CDPressMachine.WORD_PATTERN);
|
|
var nextWord = "";
|
|
|
|
/* loop through each word */
|
|
for (var i=-1; i < aryWordMatches.length; i++)
|
|
{
|
|
var currentWord = nextWord;
|
|
var currentWordPivot = this.wordPivots[currentWord];
|
|
|
|
if (typeof currentWordPivot == "undefined")
|
|
currentWordPivot =
|
|
(this.wordPivots[currentWord] = new CWordPivot (currentWord));
|
|
|
|
currentWordPivot.previousList.addLink (previousWord, weight);
|
|
|
|
if (i < aryWordMatches.length - 1)
|
|
{
|
|
nextWord = aryWordMatches[i + 1];
|
|
if (nextWord == (String.fromCharCode(1) + "action"))
|
|
nextWord = escape(nextWord.toUpperCase());
|
|
else
|
|
nextWord = nextWord.match(CDPressMachine.WORD_PATTERN);
|
|
|
|
if (nextWord == null)
|
|
nextWord = ""; //this is weak
|
|
|
|
currentWordPivot.nextList.addLink (nextWord, weight);
|
|
}
|
|
else
|
|
currentWordPivot.nextList.addLink ("");
|
|
|
|
previousWord = currentWord;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CDPressMachine.prototype.addPhrases =
|
|
function DPM_addPhrases(phrases)
|
|
{
|
|
|
|
for (var i in phrases)
|
|
this.addPhrase (phrases[i]);
|
|
|
|
}
|
|
|
|
/**
|
|
* Gets a phrase from the engine, starting from seedWord.
|
|
* if dir is greater than 0, then seedWord will be the first in
|
|
* the phrase, otherwise it will be the last
|
|
*/
|
|
CDPressMachine.prototype.getPhraseDirected =
|
|
function DPM_getPhraseDirected(seedWord, dir)
|
|
{
|
|
var word = (typeof seedWord != "undefined") ? seedWord : "";
|
|
var tempword = word;
|
|
var rval = "";
|
|
var c = 0, retry = 0;
|
|
|
|
dpprint ("DPM_getPhraseDirected: '" + word + "' " + dir);
|
|
|
|
if (typeof this.wordPivots[word] == "undefined")
|
|
return;
|
|
|
|
do
|
|
{
|
|
if (typeof this.wordPivots[word] == "undefined")
|
|
{
|
|
dd ("** DP Error: Word '" + word + "' is not a pivot **");
|
|
return;
|
|
}
|
|
|
|
if (dir > 0) // pick a word
|
|
word= this.wordPivots[word].nextList.getRandomLink().link;
|
|
else
|
|
word= this.wordPivots[word].previousList.getRandomLink().link;
|
|
|
|
if (word != "") // if it isn't blank
|
|
{
|
|
dpprint ("DPM_getPhraseDirected: got word '" + word + "'");
|
|
|
|
if (c < CDPressMachine.MIN_PHRASE_LENGTH)
|
|
retry = 0;
|
|
|
|
if (c > CDPressMachine.MAX_PHRASE_LENGTH)
|
|
if (((dir > 0) && (this.wordPivots[word].nextList.list[""])) ||
|
|
((dir <= 0) &&
|
|
(this.wordPivots[word].previousList.list[""])))
|
|
{
|
|
dpprint ("DPM_getPhraseDirected: forcing last word");
|
|
word="";
|
|
rval = rval.substring (0, rval.length - 1);
|
|
break;
|
|
}
|
|
|
|
if (dir > 0)
|
|
rval += word + " "; // put it in the rslt
|
|
else
|
|
rval = word + " " + rval;
|
|
|
|
c++; // count the word
|
|
}
|
|
else // otherwise
|
|
{
|
|
dpprint ("DPM_getPhraseDirected: last word");
|
|
// if it's too short
|
|
// and were not out of retrys
|
|
if ((c < CDPressMachine.MIN_PHRASE_LENGTH) &&
|
|
(retry++ < CDPressMachine.LENGTH_RETRIES))
|
|
word = tempword; // try again
|
|
else
|
|
// otherwise, we're done
|
|
rval = rval.substring (0, rval.length - 1);
|
|
}
|
|
|
|
tempword = word;
|
|
|
|
} while (word != "");
|
|
|
|
rval = unescape (rval);
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
CDPressMachine.prototype.getPhraseForward =
|
|
function DPM_getPhraseForward(firstWord)
|
|
{
|
|
return this.getPhraseDirected (firstWord, 1)
|
|
}
|
|
|
|
CDPressMachine.prototype.getPhraseReverse =
|
|
function DPM_getPhraseReverse(lastWord)
|
|
{
|
|
return this.getPhraseDirected (lastWord, -1)
|
|
}
|
|
|
|
/**
|
|
* locates a random pivot by following CDPressMachine.RANDOMIZE_DEPTH
|
|
* links from |word|.
|
|
*/
|
|
CDPressMachine.prototype.getRandomPivot =
|
|
function DPM_getRandomPivot (word)
|
|
{
|
|
|
|
/**
|
|
* XXXrgg: erm, this is currently pointless, but could be neat later
|
|
* if max phrase length's were implemented.
|
|
*/
|
|
if (false)
|
|
{
|
|
var depth = parseInt (Math.round
|
|
(CDPressMachine.RANDOMIZE_DEPTH * Math.random()));
|
|
word = "";
|
|
for (var i = 0;
|
|
i < depth, word =
|
|
this.wordPivots[word].nextList.getRandomLink().link;
|
|
i++); /* empty loop */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CDPressMachine.prototype.getPhrase =
|
|
function DPM_getPhrase(word)
|
|
{
|
|
var rval = this.getPhraseContaining (word);
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns a phrase with |word| somewhere in it.
|
|
*/
|
|
CDPressMachine.prototype.getPhraseContaining =
|
|
function DPM_getPhraseContaining(word)
|
|
{
|
|
if (typeof word == "undefined")
|
|
word = "";
|
|
else
|
|
word = word.toString();
|
|
|
|
dpprint ("* DPM_getPhraseContaining: '" + word + "'");
|
|
|
|
var rval, spc;
|
|
var post, pre = this.getPhraseReverse (word);
|
|
if (word != "")
|
|
var post = this.getPhraseForward (word);
|
|
|
|
dpprint ("* DPM_getPhraseContaining: pre = '" + pre + "' post = '" +
|
|
post + "'");
|
|
dpprint ("* DPM_getPhraseContaining: " + (post == "" && pre == ""));
|
|
|
|
if (word)
|
|
{
|
|
word = unescape (word);
|
|
spc = " ";
|
|
}
|
|
else
|
|
spc = "";
|
|
|
|
if (pre)
|
|
{
|
|
if (post)
|
|
rval = pre + spc + word + spc + post;
|
|
else
|
|
rval = pre + spc + word;
|
|
}
|
|
else
|
|
{
|
|
if (post)
|
|
rval = word + spc + post;
|
|
else
|
|
if (post == "" && pre == "")
|
|
rval = word;
|
|
}
|
|
|
|
if (rval && (rval.charCodeAt(0) == 1))
|
|
rval += String.fromCharCode(1);
|
|
|
|
dpprint ("* DPM_getPhraseContaining: returning '" + rval + "'");
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
CDPressMachine.prototype.getPhraseWeight =
|
|
function DPM_getPhraseWeight (phrase)
|
|
{
|
|
var ary = this.getPhraseWeights (phrase);
|
|
var w = 0;
|
|
|
|
while (ary.length > 0)
|
|
w += ary.pop();
|
|
|
|
return w;
|
|
}
|
|
|
|
CDPressMachine.prototype.getPhraseWeights =
|
|
function DPM_getPhraseWeights (phrase)
|
|
{
|
|
var words, ary = new Array();
|
|
var lastword = "";
|
|
var link, pivot;
|
|
|
|
if (!phrase)
|
|
return ary;
|
|
|
|
words = phrase.split (" ");
|
|
|
|
for (var i = 0; i < words.length; i++)
|
|
{
|
|
|
|
if (i == 0)
|
|
{
|
|
lastWord = "";
|
|
nextWord = words[i + 1];
|
|
}
|
|
else if (i == words.length - 1)
|
|
{
|
|
lastWord = words[i - 1];
|
|
nextWord = "";
|
|
}
|
|
else
|
|
{
|
|
lastWord = words[i - 1];
|
|
nextWord = words[i + 1];
|
|
}
|
|
|
|
pivot = this.wordPivots[words[i]];
|
|
|
|
if (pivot)
|
|
{
|
|
link = pivot.previousList.list[lastWord];
|
|
|
|
if (link)
|
|
ary.push(link.weight);
|
|
else
|
|
ary.push(0);
|
|
|
|
link = pivot.nextList.list[nextWord];
|
|
|
|
if (link)
|
|
ary.push(link.weight);
|
|
else
|
|
ary.push(0);
|
|
}
|
|
else
|
|
{
|
|
ary.push(0);
|
|
ary.push(0);
|
|
}
|
|
|
|
}
|
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
CDPressMachine.prototype.getPivot =
|
|
function DPM_getPivot(word)
|
|
{
|
|
|
|
return this.wordPivots[word];
|
|
|
|
}
|
|
|
|
CDPressMachine.prototype.trimList =
|
|
function DPM_trimList(threshold)
|
|
{
|
|
var el;
|
|
var c;
|
|
|
|
for (el in this.wordPivots)
|
|
{
|
|
c = this.wordPivots[el].nextList.trimList (threshold);
|
|
if (c == 0)
|
|
delete this.wordPivots[el];
|
|
else
|
|
{
|
|
c = this.wordPivots[el].previousList.trimList (threshold);
|
|
if (c == 0)
|
|
delete this.wordPivots[el];
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CDPressMachine.prototype.getMachineStatus =
|
|
function DPM_getMachineStatus()
|
|
{
|
|
var o = new Object();
|
|
|
|
o.pivotcount = 0;
|
|
o.linkcount = 0;
|
|
o.linksperpivot = 0;
|
|
o.maxweight = 0;
|
|
o.minweight = Number.MAX_VALUE;
|
|
o.averageweight = 0;
|
|
o.cleancounter = this.cleanCounter;
|
|
o.cleancount = this.cleanCount;
|
|
|
|
for (var pivot in this.wordPivots)
|
|
{
|
|
o.pivotcount++;
|
|
|
|
for (var link in this.wordPivots[pivot].previousList.list)
|
|
{
|
|
var l = this.wordPivots[pivot].previousList.list[link];
|
|
|
|
o.linkcount++;
|
|
|
|
o.maxweight = Math.max (o.maxweight, l.weight);
|
|
o.minweight = Math.min (o.minweight, l.weight);
|
|
|
|
(o.averageweight == 0) ?
|
|
o.averageweight = l.weight :
|
|
o.averageweight = (l.weight + o.averageweight) / 2;
|
|
|
|
}
|
|
}
|
|
|
|
o.linksperpivot = o.linkcount / o.pivotcount;
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
////////////////////////
|
|
function CWordPivot (word)
|
|
{
|
|
|
|
dpprint ("* new pivot : '" + word + "'");
|
|
this.word = word;
|
|
this.nextList = new CPhraseLinkList(word, "next");
|
|
this.previousList = new CPhraseLinkList(word, "previous");
|
|
|
|
}
|
|
|
|
///////////////////////
|
|
|
|
function CPhraseLinkList (parentWord, listID)
|
|
{
|
|
|
|
if (DP_DEBUG)
|
|
{
|
|
this.parentWord = parentWord;
|
|
this.listID = listID;
|
|
}
|
|
|
|
this.list = new Object();
|
|
|
|
}
|
|
|
|
CPhraseLinkList.prototype.addLink =
|
|
function PLL_addLink (link, weight)
|
|
{
|
|
var existingLink = this.list[link];
|
|
|
|
dpprint ("* adding link to '" + link + "' from '" + this.parentWord +
|
|
"' in list '" + this.listID + "'");
|
|
|
|
if (typeof weight == "undefined")
|
|
weight = 1;
|
|
|
|
if (typeof existingLink == "undefined")
|
|
this.list[link] = new CPhraseLink (link, weight);
|
|
else
|
|
if (!(typeof existingLink.adjust == "function"))
|
|
dd("existingLink.adjust is a '" + existingLink.adjust + "' " +
|
|
"not a function! link is '" + link +"'");
|
|
else
|
|
existingLink.adjust (weight);
|
|
|
|
}
|
|
|
|
CPhraseLinkList.prototype.getRandomLink =
|
|
function PLL_getRandomLink ()
|
|
{
|
|
var tot = 0;
|
|
var lastMatch = "";
|
|
var aryChoices = new Array();
|
|
var fDone = false;
|
|
|
|
dpprint ("* PLL_getRandomLink: from '" + this.parentWord + "'");
|
|
|
|
for (el in this.list)
|
|
{
|
|
tot += this.list[el].weight;
|
|
|
|
for (var i = 0; i< aryChoices.length; i++)
|
|
if (this.list[el].weight <= aryChoices[i].weight)
|
|
break;
|
|
|
|
arrayInsertAt (aryChoices, i, this.list[el]);
|
|
}
|
|
|
|
if (DP_DEBUG)
|
|
for (var i = 0; i < aryChoices.length; i++)
|
|
dpprint ("** potential word '" + aryChoices[i].link + "', weight " +
|
|
aryChoices[i].weight);
|
|
|
|
var choice = parseInt (Math.round(((tot - 1) * Math.random()) + 1));
|
|
|
|
dpprint ("* PLL_getRandomLink: tot = " + tot + ", choice = " + choice);
|
|
|
|
tot = 0;
|
|
for (i = 0; i < aryChoices.length; i++)
|
|
{
|
|
if ((tot += aryChoices[i].weight) >= choice)
|
|
{
|
|
lastMatch = aryChoices[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (lastMatch == "")
|
|
lastMatch = aryChoices[aryChoices.length - 1];
|
|
|
|
if (!lastMatch)
|
|
lastMatch = {link: ""}
|
|
|
|
dpprint ("* PLL_getRandomLink: returning: " + lastMatch);
|
|
|
|
return lastMatch;
|
|
|
|
}
|
|
|
|
CPhraseLinkList.prototype.getListWeights =
|
|
function PLL_getListWeights ()
|
|
{
|
|
var ary = new Array();
|
|
|
|
for (var el in this.list)
|
|
ary.push (this.list[el].weight);
|
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
CPhraseLinkList.prototype.getListLinks =
|
|
function PLL_getListLinks ()
|
|
{
|
|
var ary = new Array();
|
|
|
|
for (var el in this.list)
|
|
ary.push (this.list[el].link);
|
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
CPhraseLinkList.prototype.trimList =
|
|
function PLL_trimList (threshold)
|
|
{
|
|
var el;
|
|
var c;
|
|
|
|
dpprint ("trimming '" + this.parentWord + "'s list to " + threshold);
|
|
|
|
for (el in this.list)
|
|
{
|
|
c++;
|
|
|
|
if (this.list[el].weight <= threshold)
|
|
{
|
|
dpprint ("removing '" + el + "' from '" + this.parentWord + "'s '" +
|
|
this.listID + "' list, because it's weight is " +
|
|
this.list[el].weight);
|
|
|
|
delete this.list[el];
|
|
c--;
|
|
}
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
////////////////////////
|
|
|
|
function CPhraseLink (link, weight)
|
|
{
|
|
if (typeof weight == "undefined")
|
|
this.weight = 1;
|
|
else
|
|
this.weight = weight;
|
|
|
|
this.link = link;
|
|
|
|
}
|
|
|
|
CPhraseLink.prototype.adjust =
|
|
function PL_adjust(weight)
|
|
{
|
|
|
|
if ((this.weight += weight) < 1)
|
|
this.weight = 1;
|
|
|
|
}
|
|
|
|
CPhraseLink.prototype.weight =
|
|
function PL_weight ()
|
|
{
|
|
|
|
return this.weight;
|
|
|
|
}
|