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 )