/*
Copyright Paul James Mutton, 2001-2004, http://www.jibble.org/
This file is part of JMegaHal.
This software is dual-licensed, allowing you to choose between the GNU
General Public License (GPL) and the www.jibble.org Commercial License.
Since the GPL may be too restrictive for use in a proprietary application,
a commercial license is also provided. Full license information can be
found at http://www.jibble.org/licenses/
$Author: pjm2 $
$Id: JMegaHal.java,v 1.4 2004/02/01 13:24:06 pjm2 Exp $
*/
package org.jibble.jmegahal;
import java.util.*;
import java.net.*;
import java.io.*;
public class JMegaHal implements Serializable {
// These are valid chars for words. Anything else is treated as punctuation.
public static final String WORD_CHARS = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789";
public static final String END_CHARS = ".!?";
/**
* Construct an instance of JMegaHal with an empty brain.
*/
public JMegaHal() {
}
/**
* Adds an entire documents to the 'brain'. Useful for feeding in
* stray theses, but be careful not to put too much in, or you may
* run out of memory!
*/
public void addDocument(String uri) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(uri).openStream()));
StringBuffer buffer = new StringBuffer();
int ch = 0;
while ((ch = reader.read()) != -1) {
buffer.append((char) ch);
if (END_CHARS.indexOf((char) ch) >= 0) {
String sentence = buffer.toString();
sentence = sentence.replace('\r', ' ');
sentence = sentence.replace('\n', ' ');
add(sentence);
buffer = new StringBuffer();
}
}
add(buffer.toString());
reader.close();
}
/**
* Adds a new sentence to the 'brain'
*/
public void add(String sentence) {
sentence = sentence.trim();
ArrayList parts = new ArrayList();
char[] chars = sentence.toCharArray();
int i = 0;
boolean punctuation = false;
StringBuffer buffer = new StringBuffer();
while (i < chars.length) {
char ch = chars[i];
if ((WORD_CHARS.indexOf(ch) >= 0) == punctuation) {
punctuation = !punctuation;
String token = buffer.toString();
if (token.length() > 0) {
parts.add(token);
}
buffer = new StringBuffer();
//i++;
continue;
}
buffer.append(ch);
i++;
}
String lastToken = buffer.toString();
if (lastToken.length() > 0) {
parts.add(lastToken);
}
if (parts.size() >= 4) {
for (i = 0; i < parts.size() - 3; i++) {
//System.out.println("\"" + parts.get(i) + "\"");
Quad quad = new Quad((String) parts.get(i), (String) parts.get(i + 1), (String) parts.get(i + 2), (String) parts.get(i + 3));
if (quads.containsKey(quad)) {
quad = (Quad) quads.get(quad);
}
else {
quads.put(quad, quad);
}
if (i == 0) {
quad.setCanStart(true);
}
//else if (i == parts.size() - 4) {
if (i == parts.size() - 4) {
quad.setCanEnd(true);
}
for (int n = 0; n < 4; n++) {
String token = (String) parts.get(i + n);
if (!words.containsKey(token)) {
words.put(token, new HashSet(1));
}
HashSet set = (HashSet) words.get(token);
set.add(quad);
}
if (i > 0) {
String previousToken = (String) parts.get(i - 1);
if (!previous.containsKey(quad)) {
previous.put(quad, new HashSet(1));
}
HashSet set = (HashSet) previous.get(quad);
set.add(previousToken);
}
if (i < parts.size() - 4) {
String nextToken = (String) parts.get(i + 4);
if (!next.containsKey(quad)) {
next.put(quad, new HashSet(1));
}
HashSet set = (HashSet) next.get(quad);
set.add(nextToken);
}
}
}
else {
// Didn't learn anything.
}
}
/**
* Generate a random sentence from the brain.
*/
public String getSentence() {
return getSentence(null);
}
/**
* Generate a sentence that includes (if possible) the specified word.
*/
public String getSentence(String word) {
LinkedList parts = new LinkedList();
Quad[] quads;
if (words.containsKey(word)) {
quads = (Quad[]) ((HashSet) words.get(word)).toArray(new Quad[0]);
}
else {
quads = (Quad[]) this.quads.keySet().toArray(new Quad[0]);
}
if (quads.length == 0) {
return "";
}
Quad middleQuad = quads[rand.nextInt(quads.length)];
Quad quad = middleQuad;
for (int i = 0; i < 4; i++) {
parts.add(quad.getToken(i));
}
while (quad.canEnd() == false) {
String[] nextTokens = (String[]) ((HashSet) next.get(quad)).toArray(new String[0]);
String nextToken = nextTokens[rand.nextInt(nextTokens.length)];
quad = (Quad) this.quads.get(new Quad(quad.getToken(1), quad.getToken(2), quad.getToken(3), nextToken));
parts.add(nextToken);
}
quad = middleQuad;
while (quad.canStart() == false) {
String[] previousTokens = (String[]) ((HashSet) previous.get(quad)).toArray(new String[0]);
String previousToken = previousTokens[rand.nextInt(previousTokens.length)];
quad = (Quad) this.quads.get(new Quad(previousToken, quad.getToken(0), quad.getToken(1), quad.getToken(2)));
parts.addFirst(previousToken);
}
StringBuffer sentence = new StringBuffer();
Iterator it = parts.iterator();
while (it.hasNext()) {
String token = (String) it.next();
sentence.append(token);
}
return sentence.toString();
}
// This maps a single word to a HashSet of all the Quads it is in.
private HashMap words = new HashMap();
// A self-referential HashMap of Quads.
private HashMap quads = new HashMap();
// This maps a Quad onto a Set of Strings that may come next.
private HashMap next = new HashMap();
// This maps a Quad onto a Set of Strings that may come before it.
private HashMap previous = new HashMap();
private Random rand = new Random();
}