📄 megahal.c
字号:
/*===========================================================================*//* * Copyright (C) 1998 Jason Hutchens * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the license or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the Gnu Public License for more * details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. *//*===========================================================================*//* * $Id: megahal.c,v 1.2 2004/02/25 20:19:40 lfousse Exp $ * * File: megahal.c * * Program: MegaHAL * * Purpose: To simulate a natural language conversation with a psychotic * computer. This is achieved by learning from the user's * input using a third-order Markov model on the word level. * Words are considered to be sequences of characters separated * by whitespace and punctuation. Replies are generated * randomly based on a keyword, and they are scored using * measures of surprise. * * Author: Mr. Jason L. Hutchens (http://www.amristar.com.au/~hutch/) * * WWW: http://megahal.sourceforge.net * * Compilation Notes * ================= * * When compiling, be sure to link with the maths library so that the * log() function can be found. * * On the Macintosh, add the library SpeechLib to your project. It is * very important that you set the attributes to Import Weak. You can * do this by selecting the lib and then use Project Inspector from the * Window menu. * * CREDITS * ======= * * Amiga (AmigaOS) * --------------- * Dag Agren (dagren@ra.abo.fi) * * DEC (OSF) * --------- * Jason Hutchens (hutch@ciips.ee.uwa.edu.au) * * Macintosh * --------- * Paul Baxter (pbaxter@assistivetech.com) * Doug Turner (dturner@best.com) * * PC (Linux) * ---------- * Jason Hutchens (hutch@ciips.ee.uwa.edu.au) * * PC (OS/2) * --------- * Bjorn Karlowsky (?) * * PC (Windows 3.11) * ----------------- * Jim Crawford (pfister_@hotmail.com) * * PC (Windows '95) * ---------------- * Jason Hutchens (hutch@ciips.ee.uwa.edu.au) * * PPC (Linux) * ----------- * Lucas Vergnettes (Lucasv@sdf.lonestar.org) * * SGI (Irix) * ---------- * Jason Hutchens (hutch@ciips.ee.uwa.edu.au) * * Sun (SunOS) * ----------- * Jason Hutchens (hutch@ciips.ee.uwa.edu.au) *//*===========================================================================*/#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <unistd.h>#include <getopt.h>#if !defined(AMIGA) && !defined(__mac_os)#include <malloc.h>#endif#include <string.h>#include <signal.h>#include <math.h>#include <time.h>#include <ctype.h>#if defined(__mac_os)#include <types.h>#include <Speech.h>#else#include <sys/types.h>#endif#include "megahal.h"#if defined(DEBUG)#include "debug.h"#endif#define P_THINK 40#define D_KEY 100000#define V_KEY 50000#define D_THINK 500000#define V_THINK 250000#define MIN(a,b) ((a)<(b))?(a):(b)#define COOKIE "MegaHALv8"#define TIMEOUT 1#define DEFAULT "."#define COMMAND_SIZE (sizeof(command)/sizeof(command[0]))#define BYTE1 unsigned char#define BYTE2 unsigned short#define BYTE4 unsigned long#ifdef __mac_os#define bool Boolean#endif#ifdef DOS#define SEP "\\"#else#define SEP "/"#endif#ifdef AMIGA#undef toupper#define toupper(x) ToUpper(x)#undef tolower#define tolower(x) ToLower(x)#undef isalpha#define isalpha(x) IsAlpha(_AmigaLocale,x)#undef isalnum#define isalnum(x) IsAlNum(_AmigaLocale,x)#undef isdigit#define isdigit(x) IsDigit(_AmigaLocale,x)#undef isspace#define isspace(x) IsSpace(_AmigaLocale,x)#endif#ifndef __mac_os#undef FALSE#undef TRUEtypedef enum { FALSE, TRUE } bool;#endiftypedef struct { BYTE1 length; char *word;} STRING;typedef struct { BYTE4 size; STRING *entry; BYTE2 *index;} DICTIONARY;typedef struct { BYTE2 size; STRING *from; STRING *to;} SWAP;typedef struct NODE { BYTE2 symbol; BYTE4 usage; BYTE2 count; BYTE2 branch; struct NODE **tree;} TREE;typedef struct { BYTE1 order; TREE *forward; TREE *backward; TREE **context; DICTIONARY *dictionary;} MODEL;typedef enum { UNKNOWN, QUIT, EXIT, SAVE, DELAY, HELP, SPEECH, VOICELIST, VOICE, BRAIN, QUIET} COMMAND_WORDS;typedef struct { STRING word; char *helpstring; COMMAND_WORDS command;} COMMAND;/*===========================================================================*/static int width=75;static int order=5;static bool typing_delay=FALSE;static bool noprompt=FALSE;static bool speech=FALSE;static bool quiet=FALSE;static bool nowrap=FALSE;static bool nobanner=FALSE;static char *errorfilename = "megahal.log";static char *statusfilename = "megahal.txt";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; not used */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 AMIGAstruct Locale *_AmigaLocale;#endif#ifdef __mac_osBoolean 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 *, unsigned 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_osstatic bool initialize_speech(void);#endifstatic 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_osstatic char *strdup(const char *);#endifstatic void upper(char *);static void write_input(char *);static void write_output(char *);#if defined(DOS) || defined(__mac_os)static void usleep(int);#endifstatic 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 *);static void save_dictionary(FILE *, DICTIONARY *);static void save_tree(FILE *, TREE *);static void save_word(FILE *, STRING);static int search_dictionary(DICTIONARY *, STRING, bool *);static int search_node(TREE *, int, bool *);static int seed(MODEL *, DICTIONARY *);static void show_dictionary(DICTIONARY *);static void speak(char *);static bool status(char *, ...);static void train(MODEL *, char *);static void typein(char);static void update_context(MODEL *, int);static void update_model(MODEL *, int);static bool warn(char *, char *, ...);static int wordcmp(STRING, STRING);static bool word_exists(DICTIONARY *, STRING);static int rnd(int);/* Function: setnoprompt Purpose: Set noprompt variable. */void megahal_setnoprompt(void){ noprompt = TRUE;}void megahal_setnowrap (void){ nowrap = TRUE;}void megahal_setnobanner (void){ nobanner = TRUE;}void megahal_seterrorfile(char *filename){ errorfilename = filename;}void megahal_setstatusfile(char *filename){ statusfilename = filename;}void megahal_setdirectory (char *dir){ directory = dir;}/* megahal_initialize -- Initialize various brains and files. Results: None.*/void megahal_initialize(void){ errorfp = stderr; statusfp = stdout; initialize_error(errorfilename); initialize_status(statusfilename); ignore(0);#ifdef AMIGA _AmigaLocale=OpenLocale(NULL);#endif#ifdef __mac_os gSpeechExists = initialize_speech();#endif if(!nobanner) fprintf(stdout, "+------------------------------------------------------------------------+\n" "| |\n" "| # # ###### #### ## # # ## # |\n" "| ## ## # # # # # # # # # # ### |\n" "| # ## # ##### # # # ###### # # # # # |\n" "| # # # # ### ###### # # ###### # # # ### |\n" "| # # # # # # # # # # # # # # # # |\n" "| # # ###### #### # # # # # # ###### # ###r6 |\n" "| |\n" "| Copyright(C) 1998 Jason Hutchens |\n" "+------------------------------------------------------------------------+\n" ); words = new_dictionary(); greets = new_dictionary(); change_personality(NULL, 0, &model);}/* megahal_do_reply -- Take string as input, and return allocated string as output. The user is responsible for freeing this memory. */char *megahal_do_reply(char *input, int log){ char *output = NULL; if (log != 0) write_input(input); /* log input if so desired */ upper(input); make_words(input, words); learn(model, words); output = generate_reply(model, words); capitalize(output); return output;}/* megahal_learn_no_reply -- Take string as input, update model but don't generate reply. */void megahal_learn_no_reply(char *input, int log){ if (log != 0) write_input(input); /* log input if so desired */ upper(input); make_words(input, words); learn(model, words);}/* megahal_initial_greeting -- This function returns an initial greeting. It can be used to start Megahal conversations, but it isn't necessary. */char *megahal_initial_greeting(void){ char *output; make_greeting(greets); output = generate_reply(model, greets); return output;}/* megahal_output -- This function pretty prints output. Wrapper function to have things in the right namespace.*/void megahal_output(char *output){ if(!quiet) write_output(output);}/* megahal_input -- Get a string from stdin, using a prompt. */char *megahal_input(char *prompt){ if (noprompt) return read_input(""); else return read_input(prompt);}/* megahal_command -- Check to see if input is a megahal command, and if so, act upon it. Returns 1 if it is a command, 0 if it is not. */int megahal_command(char *input){ int position = 0; char *output; make_words(input,words); switch(execute_command(words, &position)) { case EXIT: exithal(); break; case QUIT: save_model("megahal.brn", model);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -