AI-MegaHAL
view release on metacpan or search on metacpan
libmegahal.c view on Meta::CPAN
static DICTIONARY *words=NULL;
static DICTIONARY *greets=NULL;
static MODEL *model=NULL;
static FILE *errorfp;
static FILE *statusfp;
static DICTIONARY *ban=NULL;
static DICTIONARY *aux=NULL;
static DICTIONARY *fin=NULL;
static DICTIONARY *grt=NULL;
static SWAP *swp=NULL;
static bool used_key;
static char *directory=NULL;
static char *last=NULL;
static COMMAND command[] = {
{ { 4, "QUIT" }, "quits the program and saves MegaHAL's brain", QUIT },
{ { 4, "EXIT" }, "exits the program *without* saving MegaHAL's brain", EXIT },
{ { 4, "SAVE" }, "saves the current MegaHAL brain", SAVE },
{ { 5, "DELAY" }, "toggles MegaHAL's typing delay (off by default)", DELAY },
{ { 6, "SPEECH" }, "toggles MegaHAL's speech (off by default)", SPEECH },
{ { 6, "VOICES" }, "list available voices for speech", VOICELIST },
{ { 5, "VOICE" }, "switches to voice specified", VOICE },
{ { 5, "BRAIN" }, "change to another MegaHAL personality", BRAIN },
{ { 4, "HELP" }, "displays this message", HELP },
{ { 5, "QUIET" }, "toggles MegaHAL's responses (on by default)",QUIET},
/*
{ { 5, "STATS" }, "Display stats", STATS},
{ { 5, "STATS-SESSION" }, "Display stats for this session only",STATS_SESSION},
{ { 5, "STATS-ALL" },"Display stats for the whole lifetime",STATS-ALL},
*/
};
#ifdef AMIGA
struct Locale *_AmigaLocale;
#endif
#ifdef __mac_os
Boolean gSpeechExists = false;
SpeechChannel gSpeechChannel = nil;
#endif
/* FIXME - these need to be static */
static void add_aux(MODEL *, DICTIONARY *, STRING);
static void add_key(MODEL *, DICTIONARY *, STRING);
static void add_node(TREE *, TREE *, int);
static void add_swap(SWAP *, char *, char *);
static TREE *add_symbol(TREE *, BYTE2);
static BYTE2 add_word(DICTIONARY *, STRING);
static int babble(MODEL *, DICTIONARY *, DICTIONARY *);
static bool boundary(char *, int);
static void capitalize(char *);
static void changevoice(DICTIONARY *, int);
static void change_personality(DICTIONARY *, int, MODEL **);
static void delay(char *);
static void die(int);
static bool dissimilar(DICTIONARY *, DICTIONARY *);
static void error(char *, char *, ...);
static float evaluate_reply(MODEL *, DICTIONARY *, DICTIONARY *);
static COMMAND_WORDS execute_command(DICTIONARY *, int *);
static void exithal(void);
static TREE *find_symbol(TREE *, int);
static TREE *find_symbol_add(TREE *, int);
static BYTE2 find_word(DICTIONARY *, STRING);
static char *generate_reply(MODEL *, DICTIONARY *);
static void help(void);
static void ignore(int);
static bool initialize_error(char *);
#ifdef __mac_os
static bool initialize_speech(void);
#endif
static bool initialize_status(char *);
static void learn(MODEL *, DICTIONARY *);
static void listvoices(void);
static void make_greeting(DICTIONARY *);
static void make_words(char *, DICTIONARY *);
static DICTIONARY *new_dictionary(void);
static char *read_input(char *);
static void save_model(char *, MODEL *);
#ifdef __mac_os
static char *strdup(const char *);
#endif
static void upper(char *);
static void write_input(char *);
static void write_output(char *);
#if defined(DOS) || defined(__mac_os)
static void usleep(int);
#endif
#if defined(_MSC_VER) || defined(__MINGW32_VERSION)
#include <windows.h>
#define usleep(i) Sleep(i)
#endif
static char *format_output(char *);
static void free_dictionary(DICTIONARY *);
static void free_model(MODEL *);
static void free_tree(TREE *);
static void free_word(STRING);
static void free_words(DICTIONARY *);
static void initialize_context(MODEL *);
static void initialize_dictionary(DICTIONARY *);
static DICTIONARY *initialize_list(char *);
static SWAP *initialize_swap(char *);
static void load_dictionary(FILE *, DICTIONARY *);
static bool load_model(char *, MODEL *);
static void load_personality(MODEL **);
static void load_tree(FILE *, TREE *);
static void load_word(FILE *, DICTIONARY *);
static DICTIONARY *make_keywords(MODEL *, DICTIONARY *);
static char *make_output(DICTIONARY *);
static MODEL *new_model(int);
static TREE *new_node(void);
static SWAP *new_swap(void);
static bool print_header(FILE *);
static bool progress(char *, int, int);
static DICTIONARY *reply(MODEL *, DICTIONARY *);
libmegahal.c view on Meta::CPAN
return(FALSE);
}
/*---------------------------------------------------------------------------*/
/*
* Function: Make_Greeting
*
* Purpose: Put some special words into the dictionary so that the
* program will respond as if to a new judge.
*/
void make_greeting(DICTIONARY *words)
{
register int i;
for(i=0; i<words->size; ++i) free(words->entry[i].word);
free_dictionary(words);
if(grt->size>0) (void)add_word(words, grt->entry[rnd(grt->size)]);
}
/*---------------------------------------------------------------------------*/
/*
* Function: Generate_Reply
*
* Purpose: Take a string of user input and return a string of output
* which may vaguely be construed as containing a reply to
* whatever is in the input string.
*/
char *generate_reply(MODEL *model, DICTIONARY *words)
{
static DICTIONARY *dummy=NULL;
DICTIONARY *replywords;
DICTIONARY *keywords;
float surprise;
float max_surprise;
char *output;
static char *output_none=NULL;
int count;
int basetime;
int timeout = TIMEOUT;
/*
* Create an array of keywords from the words in the user's input
*/
keywords=make_keywords(model, words);
/*
* Make sure some sort of reply exists
*/
if(output_none==NULL) {
output_none=malloc(40);
if(output_none!=NULL)
strcpy(output_none, "I don't know enough to answer you yet!");
}
output=output_none;
if(dummy == NULL) dummy = new_dictionary();
replywords = reply(model, dummy);
if(dissimilar(words, replywords) == TRUE) output = make_output(replywords);
/*
* Loop for the specified waiting period, generating and evaluating
* replies
*/
max_surprise=(float)-1.0;
count=0;
basetime=time(NULL);
/* progress("Generating reply", 0, 1); */
do {
replywords=reply(model, keywords);
surprise=evaluate_reply(model, keywords, replywords);
++count;
if((surprise>max_surprise)&&(dissimilar(words, replywords)==TRUE)) {
max_surprise=surprise;
output=make_output(replywords);
}
/* progress(NULL, (time(NULL)-basetime),timeout); */
} while((time(NULL)-basetime)<timeout);
progress(NULL, 1, 1);
/*
* Return the best answer we generated
*/
return(output);
}
/*---------------------------------------------------------------------------*/
/*
* Function: Dissimilar
*
* Purpose: Return TRUE or FALSE depending on whether the dictionaries
* are the same or not.
*/
bool dissimilar(DICTIONARY *words1, DICTIONARY *words2)
{
register int i;
if(words1->size!=words2->size) return(TRUE);
for(i=0; i<words1->size; ++i)
if(wordcmp(words1->entry[i], words2->entry[i])!=0) return(TRUE);
return(FALSE);
}
/*---------------------------------------------------------------------------*/
/*
* Function: Make_Keywords
*
* Purpose: Put all the interesting words from the user's input into
* a keywords dictionary, which will be used when generating
* a reply.
*/
DICTIONARY *make_keywords(MODEL *model, DICTIONARY *words)
{
static DICTIONARY *keys=NULL;
register int i;
register int j;
int c;
if(keys==NULL) keys=new_dictionary();
for(i=0; i<keys->size; ++i) free(keys->entry[i].word);
free_dictionary(keys);
for(i=0; i<words->size; ++i) {
/*
* Find the symbol ID of the word. If it doesn't exist in
* the model, or if it begins with a non-alphanumeric
* character, or if it is in the exclusion array, then
* skip over it.
*/
libmegahal.c view on Meta::CPAN
* Re-create the context of the model from the current reply
* dictionary so that we can generate backwards to reach the
* beginning of the string.
*/
if(replies->size>0) for(i=MIN(replies->size-1, model->order); i>=0; --i) {
symbol=find_word(model->dictionary, replies->entry[i]);
update_context(model, symbol);
}
/*
* Generate the reply in the backward direction.
*/
while(TRUE) {
/*
* Get a random symbol from the current context.
*/
symbol=babble(model, keys, replies);
if((symbol==0)||(symbol==1)) break;
/*
* Prepend the symbol to the reply dictionary.
*/
if(replies->entry==NULL)
replies->entry=(STRING *)malloc((replies->size+1)*sizeof(STRING));
else
replies->entry=(STRING *)realloc(replies->entry, (replies->size+1)*sizeof(STRING));
if(replies->entry==NULL) {
error("reply", "Unable to reallocate dictionary");
return(NULL);
}
/*
* Shuffle everything up for the prepend.
*/
for(i=replies->size; i>0; --i) {
replies->entry[i].length=replies->entry[i-1].length;
replies->entry[i].word=replies->entry[i-1].word;
}
replies->entry[0].length=model->dictionary->entry[symbol].length;
replies->entry[0].word=model->dictionary->entry[symbol].word;
replies->size+=1;
/*
* Extend the current context of the model with the current symbol.
*/
update_context(model, symbol);
}
return(replies);
}
/*---------------------------------------------------------------------------*/
/*
* Function: Evaluate_Reply
*
* Purpose: Measure the average surprise of keywords relative to the
* language model.
*/
float evaluate_reply(MODEL *model, DICTIONARY *keys, DICTIONARY *words)
{
register int i;
register int j;
int symbol;
float probability;
int count;
float entropy=(float)0.0;
TREE *node;
int num=0;
if(words->size<=0) return((float)0.0);
initialize_context(model);
model->context[0]=model->forward;
for(i=0; i<words->size; ++i) {
symbol=find_word(model->dictionary, words->entry[i]);
if(find_word(keys, words->entry[i])!=0) {
probability=(float)0.0;
count=0;
++num;
for(j=0; j<model->order; ++j) if(model->context[j]!=NULL) {
node=find_symbol(model->context[j], symbol);
probability+=(float)(node->count)/
(float)(model->context[j]->usage);
++count;
}
if(count>0.0) entropy-=(float)log(probability/(float)count);
}
update_context(model, symbol);
}
initialize_context(model);
model->context[0]=model->backward;
for(i=words->size-1; i>=0; --i) {
symbol=find_word(model->dictionary, words->entry[i]);
if(find_word(keys, words->entry[i])!=0) {
probability=(float)0.0;
count=0;
++num;
for(j=0; j<model->order; ++j) if(model->context[j]!=NULL) {
node=find_symbol(model->context[j], symbol);
probability+=(float)(node->count)/
(float)(model->context[j]->usage);
++count;
}
if(count>0.0) entropy-=(float)log(probability/(float)count);
}
update_context(model, symbol);
}
if(num>=8) entropy/=(float)sqrt(num-1);
( run in 0.627 second using v1.01-cache-2.11-cpan-98e64b0badf )