Advertisement
Guest User

Untitled

a guest
Jan 2nd, 2016
279
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 88.11 KB | None | 0 0
  1.  
  2. /*===========================================================================*/
  3.  
  4. /*
  5.  *  Copyright (C) 1998 Jason Hutchens
  6.  *
  7.  *  This program is free software; you can redistribute it and/or modify it
  8.  *  under the terms of the GNU General Public License as published by the Free
  9.  *  Software Foundation; either version 2 of the license or (at your option)
  10.  *  any later version.
  11.  *
  12.  *  This program is distributed in the hope that it will be useful, but
  13.  *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  14.  *  or FITNESS FOR A PARTICULAR PURPOSE.  See the Gnu Public License for more
  15.  *  details.
  16.  *
  17.  *  You should have received a copy of the GNU General Public License along
  18.  *  with this program; if not, write to the Free Software Foundation, Inc.,
  19.  *  675 Mass Ave, Cambridge, MA 02139, USA.
  20.  */
  21.  
  22. /*===========================================================================*/
  23.  
  24. /*
  25.  *      $Id: megahal.c,v 1.10 2004/02/23 11:12:29 lfousse Exp $
  26.  *
  27.  *      File:           megahal.c
  28.  *
  29.  *      Program:        MegaHAL
  30.  *
  31.  *      Purpose:        To simulate a natural language conversation with a psychotic
  32.  *                      computer.  This is achieved by learning from the user's
  33.  *                      input using a third-order Markov model on the word level.
  34.  *                      Words are considered to be sequences of characters separated
  35.  *                      by whitespace and punctuation.  Replies are generated
  36.  *                      randomly based on a keyword, and they are scored using
  37.  *                      measures of surprise.
  38.  *
  39.  *      Author:     Mr. Jason L. Hutchens (http://www.amristar.com.au/~hutch/)
  40.  *
  41.  *      WWW:        http://megahal.sourceforge.net
  42.  *
  43.  *      Compilation Notes
  44.  *      =================
  45.  *
  46.  *      When compiling, be sure to link with the maths library so that the
  47.  *      log() function can be found.
  48.  *
  49.  *      On the Macintosh, add the library SpeechLib to your project.  It is
  50.  *      very important that you set the attributes to Import Weak.  You can
  51.  *      do this by selecting the lib and then use Project Inspector from the
  52.  *      Window menu.
  53.  *
  54.  *      CREDITS
  55.  *      =======
  56.  *
  57.  *      Amiga (AmigaOS)
  58.  *      ---------------
  59.  *      Dag Agren (dagren@ra.abo.fi)
  60.  *
  61.  *      DEC (OSF)
  62.  *      ---------
  63.  *      Jason Hutchens (hutch@ciips.ee.uwa.edu.au)
  64.  *
  65.  *      Macintosh
  66.  *      ---------
  67.  *      Paul Baxter (pbaxter@assistivetech.com)
  68.  *      Doug Turner (dturner@best.com)
  69.  *
  70.  *      PC (Linux)
  71.  *      ----------
  72.  *      Jason Hutchens (hutch@ciips.ee.uwa.edu.au)
  73.  *
  74.  *      PC (OS/2)
  75.  *      ---------
  76.  *      Bjorn Karlowsky (?)
  77.  *
  78.  *      PC (Windows 3.11)
  79.  *      -----------------
  80.  *      Jim Crawford (pfister_@hotmail.com)
  81.  *
  82.  *      PC (Windows '95)
  83.  *      ----------------
  84.  *      Jason Hutchens (hutch@ciips.ee.uwa.edu.au)
  85.  *
  86.  *      PPC (Linux)
  87.  *      -----------
  88.  *      Lucas Vergnettes (Lucasv@sdf.lonestar.org)
  89.  *
  90.  *      SGI (Irix)
  91.  *      ----------
  92.  *      Jason Hutchens (hutch@ciips.ee.uwa.edu.au)
  93.  *
  94.  *      Sun (SunOS)
  95.  *      -----------
  96.  *      Jason Hutchens (hutch@ciips.ee.uwa.edu.au)
  97.  */
  98.  
  99. /*===========================================================================*/
  100.  
  101. #include <stdio.h>
  102. #include <stdlib.h>
  103. #include <stdarg.h>
  104. #include <unistd.h>
  105. #include <getopt.h>
  106. #if !defined(AMIGA) && !defined(__mac_os)
  107. #include <malloc.h>
  108. #endif
  109. #include <string.h>
  110. #include <signal.h>
  111. #include <math.h>
  112. #include <time.h>
  113. #include <ctype.h>
  114. #if defined(__mac_os)
  115. #include <types.h>
  116. #include <Speech.h>
  117. #else
  118. #include <sys/types.h>
  119. #endif
  120. #include "megahal.h"
  121. #if defined(DEBUG)
  122. #include "debug.h"
  123. #endif
  124.  
  125. #include "../irc.h"
  126. #include "megahal-m.h"
  127.  
  128.  
  129. #define P_THINK 40
  130. #define D_KEY 100000
  131. #define V_KEY 50000
  132. #define D_THINK 500000
  133. #define V_THINK 250000
  134.  
  135. #define MIN(a,b) ((a)<(b))?(a):(b)
  136.  
  137. #define COOKIE "MegaHALv8"
  138. #define TIMEOUT 1
  139.  
  140. #define DEFAULT "."
  141.  
  142. #define COMMAND_SIZE (sizeof(command)/sizeof(command[0]))
  143.  
  144. #define BYTE1 unsigned char
  145. #define BYTE2 unsigned short
  146. #define BYTE4 unsigned long
  147.  
  148. #ifdef __mac_os
  149. #define bool Boolean
  150. #endif
  151.  
  152. #ifdef DOS
  153. #define SEP "\\"
  154. #else
  155. #define SEP "/"
  156. #endif
  157.  
  158. #ifdef AMIGA
  159. #undef toupper
  160. #define toupper(x) ToUpper(x)
  161. #undef tolower
  162. #define tolower(x) ToLower(x)
  163. #undef isalpha
  164. #define isalpha(x) IsAlpha(_AmigaLocale,x)
  165. #undef isalnum
  166. #define isalnum(x) IsAlNum(_AmigaLocale,x)
  167. #undef isdigit
  168. #define isdigit(x) IsDigit(_AmigaLocale,x)
  169. #undef isspace
  170. #define isspace(x) IsSpace(_AmigaLocale,x)
  171. #endif
  172.  
  173. #ifndef __mac_os
  174. #undef FALSE
  175. #undef TRUE
  176. typedef enum { FALSE, TRUE } bool;
  177. #endif
  178.  
  179. typedef struct {
  180.     BYTE1 length;
  181.     char *word;
  182. } STRING;
  183.  
  184. typedef struct {
  185.     BYTE4 size;
  186.     STRING *entry;
  187.     BYTE2 *index;
  188. } DICTIONARY;
  189.  
  190. typedef struct {
  191.     BYTE2 size;
  192.     STRING *from;
  193.     STRING *to;
  194. } SWAP;
  195.  
  196. typedef struct NODE {
  197.     BYTE2 symbol;
  198.     BYTE4 usage;
  199.     BYTE2 count;
  200.     BYTE2 branch;
  201.     struct NODE **tree;
  202. } TREE;
  203.  
  204. typedef struct {
  205.     BYTE1 order;
  206.     TREE *forward;
  207.     TREE *backward;
  208.     TREE **context;
  209.     DICTIONARY *dictionary;
  210. } MODEL;
  211.  
  212. typedef enum { UNKNOWN, QUIT, EXIT, SAVE, DELAY, HELP, SPEECH, VOICELIST, VOICE, BRAIN, QUIET} COMMAND_WORDS;
  213.  
  214. typedef struct {
  215.     STRING word;
  216.     char *helpstring;
  217.     COMMAND_WORDS command;
  218. } COMMAND;
  219.  
  220. /*===========================================================================*/
  221.  
  222. static int width=75;
  223. static int order=5;
  224.  
  225. static bool typing_delay=FALSE;
  226. static bool noprompt=FALSE;
  227. static bool speech=FALSE;
  228. static bool quiet=FALSE;
  229. static bool nowrap=FALSE;
  230. static bool nobanner=FALSE;
  231.  
  232. static char *errorfilename = "megahal.log";
  233. static char *statusfilename = "megahal.txt";
  234. static DICTIONARY *words=NULL;
  235. static DICTIONARY *greets=NULL;
  236. static MODEL *model=NULL;
  237.  
  238. static FILE *errorfp;
  239. static FILE *statusfp;
  240.  
  241. static DICTIONARY *ban=NULL;
  242. static DICTIONARY *aux=NULL;
  243. /*static DICTIONARY *fin=NULL; not used */
  244. static DICTIONARY *grt=NULL;
  245. static SWAP *swp=NULL;
  246. static bool used_key;
  247. static char *directory=NULL;
  248. static char *last=NULL;
  249.  
  250. static COMMAND command[] = {
  251.     { { 4, "QUIT" }, "quits the program and saves MegaHAL's brain", QUIT },
  252.     { { 4, "EXIT" }, "exits the program *without* saving MegaHAL's brain", EXIT },
  253.     { { 4, "SAVE" }, "saves the current MegaHAL brain", SAVE },
  254.     { { 5, "DELAY" }, "toggles MegaHAL's typing delay (off by default)", DELAY },
  255.     { { 6, "SPEECH" }, "toggles MegaHAL's speech (off by default)", SPEECH },
  256.     { { 6, "VOICES" }, "list available voices for speech", VOICELIST },
  257.     { { 5, "VOICE" }, "switches to voice specified", VOICE },
  258.     { { 5, "BRAIN" }, "change to another MegaHAL personality", BRAIN },
  259.     { { 4, "HELP" }, "displays this message", HELP },
  260.     { { 5, "QUIET" }, "toggles MegaHAL's responses (on by default)",QUIET},
  261.     /*
  262.       { { 5, "STATS" }, "Display stats", STATS},
  263.       { { 5, "STATS-SESSION" }, "Display stats for this session only",STATS_SESSION},
  264.       { { 5, "STATS-ALL" },"Display stats for the whole lifetime",STATS-ALL},
  265.     */
  266. };
  267.  
  268. #ifdef AMIGA
  269. struct Locale *_AmigaLocale;
  270. #endif
  271.  
  272. #ifdef __mac_os
  273. Boolean gSpeechExists = false;
  274. SpeechChannel gSpeechChannel = nil;
  275. #endif
  276.  
  277. /* FIXME - these need to be static  */
  278.  
  279. static void add_aux(MODEL *, DICTIONARY *, STRING);
  280. static void add_key(MODEL *, DICTIONARY *, STRING);
  281. static void add_node(TREE *, TREE *, int);
  282. static void add_swap(SWAP *, char *, char *);
  283. static TREE *add_symbol(TREE *, BYTE2);
  284. static BYTE2 add_word(DICTIONARY *, STRING);
  285. static int babble(MODEL *, DICTIONARY *, DICTIONARY *);
  286. static bool boundary(char *, int);
  287. static void capitalize(char *);
  288. static void changevoice(DICTIONARY *, int);
  289. static void change_personality(DICTIONARY *, unsigned int, MODEL **);
  290. static void delay(char *);
  291. static void die(int);
  292. static bool dissimilar(DICTIONARY *, DICTIONARY *);
  293. static void error(char *, char *, ...);
  294. static float evaluate_reply(MODEL *, DICTIONARY *, DICTIONARY *);
  295. static COMMAND_WORDS execute_command(DICTIONARY *, int *);
  296. static void exithal(void);
  297. static TREE *find_symbol(TREE *, int);
  298. static TREE *find_symbol_add(TREE *, int);
  299. static BYTE2 find_word(DICTIONARY *, STRING);
  300. static char *generate_reply(MODEL *, DICTIONARY *);
  301. static void help(void);
  302. static void ignore(int);
  303. static bool initialize_error(char *);
  304. #ifdef __mac_os
  305. static bool initialize_speech(void);
  306. #endif
  307. static bool initialize_status(char *);
  308. static void learn(MODEL *, DICTIONARY *);
  309. static void listvoices(void);
  310. static void make_greeting(DICTIONARY *);
  311. static void make_words(char *, DICTIONARY *);
  312. static DICTIONARY *new_dictionary(void);
  313.  
  314. static char *read_input(char *);
  315. static void save_model(char *, MODEL *);
  316. #ifdef __mac_os
  317. static char *strdup(const char *);
  318. #endif
  319. static void upper(char *);
  320. static void write_input(char *);
  321. static void write_output(char *);
  322. #if defined(DOS) || defined(__mac_os)
  323. static void usleep(int);
  324. #endif
  325.  
  326.  
  327. static char *format_output(char *);
  328. static void free_dictionary(DICTIONARY *);
  329. static void free_model(MODEL *);
  330. static void free_tree(TREE *);
  331. static void free_word(STRING);
  332. static void free_words(DICTIONARY *);
  333. static void initialize_context(MODEL *);
  334. static void initialize_dictionary(DICTIONARY *);
  335. static DICTIONARY *initialize_list(char *);
  336. static SWAP *initialize_swap(char *);
  337. static void load_dictionary(FILE *, DICTIONARY *);
  338. static bool load_model(char *, MODEL *);
  339. static void load_personality(MODEL **);
  340. static void load_tree(FILE *, TREE *);
  341. static void load_word(FILE *, DICTIONARY *);
  342. static DICTIONARY *make_keywords(MODEL *, DICTIONARY *);
  343. static char *make_output(DICTIONARY *);
  344. static MODEL *new_model(int);
  345. static TREE *new_node(void);
  346. static SWAP *new_swap(void);
  347. static bool print_header(FILE *);
  348. static bool progress(char *, int, int);
  349. static DICTIONARY *reply(MODEL *, DICTIONARY *);
  350. static void save_dictionary(FILE *, DICTIONARY *);
  351. static void save_tree(FILE *, TREE *);
  352. static void save_word(FILE *, STRING);
  353. static int search_dictionary(DICTIONARY *, STRING, bool *);
  354. static int search_node(TREE *, int, bool *);
  355. static int seed(MODEL *, DICTIONARY *);
  356. static void show_dictionary(DICTIONARY *);
  357. static void speak(char *);
  358. static bool status(char *, ...);
  359. static void train(MODEL *, char *);
  360. static void typein(char);
  361. static void update_context(MODEL *, int);
  362. static void update_model(MODEL *, int);
  363. static bool warn(char *, char *, ...);
  364. static int wordcmp(STRING, STRING);
  365. static bool word_exists(DICTIONARY *, STRING);
  366. static int rnd(int);
  367.  
  368.  
  369. /*bot*/
  370.  
  371.  
  372. char learnchans[MAXCHANS][35] = {
  373.     "#www.elektroda.pl",
  374.     "#ctsg",
  375.     "#listekklonu",
  376.     "#sylwester",
  377. };
  378. char replychans[MAXCHANS][35] = {
  379.     "#testtest",
  380.     "#sylwester",
  381. };
  382.  
  383. static const char iso[] = {'ą', 'ć', 'ę', 'ł', 'ń', 'ó', 'ś', 'ż', 'ź', 'Ą', 'Ć', 'Ę', 'Ł', 'Ń', 'Ó', 'Ś', 'Ż', 'Ź'};
  384. static const char asc[] = {'a', 'c', 'e', 'l', 'n', 'o', 's', 'z', 'z', 'A', 'C', 'E', 'L', 'N', 'O', 'S', 'Z', 'Z'};
  385.  
  386. char modname[] = "MEGAHAL";
  387.  
  388. static void de_iso(char *text){
  389.     char c;
  390.     unsigned short i;
  391.     while((c = *(text++))){
  392.         for(i=0; i<sizeof(iso); i++){
  393.             if(c == iso[i]){
  394.                 *(text-1) = asc[i];
  395.                 break;
  396.             }
  397.         }
  398.     }
  399. }
  400.  
  401. int check_links(char *text){
  402.     if(strstr(text, "http://")) return 1;
  403.     if(strstr(text, "<")) return 1;
  404.     if(strstr(text, "=")) return 1;
  405.     if(strstr(text, "/")) return 1;
  406.     return 0;
  407. }
  408.  
  409. char *strip_hilight(char *text){
  410.     if(strstr(text, ": ")) return strstr(text, ": ") + 2; //hajlajt
  411.     return text;
  412. }
  413.  
  414. char *do_megahal(char *text){
  415.     static int i;
  416.     char *otext;
  417.  
  418.     text = strip_hilight(text);
  419.     if(check_links(text)) return "Nie lubie linkow!";
  420.     de_iso(text);
  421.     otext = megahal_do_reply(text, 1);
  422.  
  423.     if(++i==20){
  424.         megahal_cleanup();
  425.         i=0;
  426.     }
  427.  
  428.     return otext;
  429. }
  430.  
  431. void do_megahal_learn(char *text){
  432.     if(check_links(text)) return;
  433.     text = strip_hilight(text);
  434.     de_iso(text);
  435.  
  436.     megahal_learn_no_reply(text, 1);
  437. }
  438.  
  439. void init(void){
  440.     megahal_setdirectory ("./megahal");
  441.     megahal_initialize();
  442. }
  443.  
  444. void cleanup(void){
  445.     megahal_cleanup();
  446. }
  447.  
  448. int chmsg(struct irc_msg msg){
  449.     char outmsg[500];
  450.  
  451.     int reply_ok = 0;
  452.     int i;
  453.  
  454.     strcpy(outmsg, msg.text);
  455.     if(!strcasecmp(get_nick(msg), "lc")) return 0;
  456. //  for(i=0;i<15;i++){
  457.         if(chan_on_list(learnchans, msg.arg1)){
  458.             do_megahal_learn(msg.text);
  459. //          break;
  460.         }
  461.         if(chan_on_list(replychans, msg.arg1)){
  462.             reply_ok = 1;
  463. //          break;
  464. //      }
  465.     }
  466.     if(reply_ok){
  467.         if(rand()>(RAND_MAX/5)) return 0;
  468.         irc_reply(msg, do_megahal(outmsg), 1);
  469.         return 1;
  470.     }
  471.     return 0;
  472. }
  473.  
  474. int ctcp(struct irc_msg msg){
  475.     if(!strncmp(msg.text+1, "ACTION", 6) && dst_chan(msg) && chan_on_list(replychans,msg.arg1)){
  476.         sock_printf("PRIVMSG %s :\x01""ACTION nie lubi, kiedy %s uzywa /me :(\x01", msg.arg1, get_nick(msg));
  477.         return 1;
  478.     }
  479.     return 0;
  480. }
  481.  
  482. /* Function: setnoprompt
  483.  
  484.    Purpose: Set noprompt variable.
  485.  
  486.  */
  487. void megahal_setnoprompt(void)
  488. {
  489.     noprompt = TRUE;
  490. }
  491.  
  492. void megahal_setnowrap (void)
  493. {
  494.     nowrap = TRUE;
  495. }
  496. void megahal_setnobanner (void)
  497. {
  498.     nobanner = TRUE;
  499. }
  500.  
  501. void megahal_seterrorfile(char *filename)
  502. {
  503.     errorfilename = filename;
  504. }
  505.  
  506. void megahal_setstatusfile(char *filename)
  507. {
  508.     statusfilename = filename;
  509. }
  510.  
  511. void megahal_setdirectory (char *dir)
  512. {
  513.     directory = dir;
  514. }
  515.  
  516. /*
  517.    megahal_initialize --
  518.  
  519.    Initialize various brains and files.
  520.  
  521.    Results:
  522.  
  523.    None.
  524. */
  525.  
  526. void megahal_initialize(void)
  527. {
  528.     errorfp = stderr;
  529.     statusfp = stdout;
  530.  
  531.     initialize_error(errorfilename);
  532.     initialize_status(statusfilename);
  533.     ignore(0);
  534.  
  535. #ifdef AMIGA
  536.     _AmigaLocale=OpenLocale(NULL);
  537. #endif
  538. #ifdef __mac_os
  539.     gSpeechExists = initialize_speech();
  540. #endif
  541.     if(!nobanner)
  542.     fprintf(stdout,
  543.         "+------------------------------------------------------------------------+\n"
  544.         "|                                                                        |\n"
  545.         "|  #    #  ######   ####     ##    #    #    ##    #                     |\n"
  546.         "|  ##  ##  #       #    #   #  #   #    #   #  #   #               ###   |\n"
  547.         "|  # ## #  #####   #       #    #  ######  #    #  #              #   #  |\n"
  548.         "|  #    #  #       #  ###  ######  #    #  ######  #       #   #   ###   |\n"
  549.         "|  #    #  #       #    #  #    #  #    #  #    #  #        # #   #   #  |\n"
  550.         "|  #    #  ######   ####   #    #  #    #  #    #  ######    #     ###r6 |\n"
  551.         "|                                                                        |\n"
  552.         "|                    Copyright(C) 1998 Jason Hutchens                    |\n"
  553.         "+------------------------------------------------------------------------+\n"
  554.         );
  555.  
  556.     words = new_dictionary();
  557.     greets = new_dictionary();
  558.     change_personality(NULL, 0, &model);
  559. }
  560.  
  561. /*
  562.    megahal_do_reply --
  563.  
  564.    Take string as input, and return allocated string as output.  The
  565.    user is responsible for freeing this memory.
  566.  
  567.   */
  568.  
  569. char *megahal_do_reply(char *input, int log)
  570. {
  571. //    static char oldinput[4100];
  572. //    char tmp[4098];
  573.     char *output = NULL;
  574.  
  575.     if (log != 0)
  576.     write_input(input);  /* log input if so desired */
  577.  
  578.     upper(input);
  579.  
  580. //    strncpy(tmp, oldinput, 2048);
  581. //    strcat(tmp, " ");
  582. //    strncat(tmp, input, 2048);
  583. //    strncpy(oldinput, input, 2048);
  584. //    strcat(oldinput, " ");
  585.  
  586.     make_words(input/*tmp*/, words);
  587.  
  588.     output = generate_reply(model, words);
  589.  
  590.     make_words(input, words);
  591.  
  592.     learn(model, words);
  593.  
  594.  //   strncat(oldinput, output, 2048);
  595.  
  596.  //   printf("oldinput: %s\n", oldinput);
  597.  
  598.     capitalize(output);
  599.     return output;
  600. }
  601.  
  602. /*
  603.    megahal_learn_no_reply --
  604.  
  605.    Take string as input, update model but don't generate reply.
  606.  
  607.   */
  608.  
  609. void megahal_learn_no_reply(char *input, int log)
  610. {
  611.     if (log != 0)
  612.     write_input(input);  /* log input if so desired */
  613.  
  614.     upper(input);
  615.  
  616.     make_words(input, words);
  617.  
  618.     learn(model, words);
  619. }
  620.  
  621. /*
  622.    megahal_initial_greeting --
  623.  
  624.    This function returns an initial greeting.  It can be used to start
  625.    Megahal conversations, but it isn't necessary.
  626.  
  627.   */
  628.  
  629. char *megahal_initial_greeting(void)
  630. {
  631.     char *output;
  632.  
  633.     make_greeting(greets);
  634.     output = generate_reply(model, greets);
  635.     return output;
  636. }
  637.  
  638. /*
  639.    megahal_output --
  640.  
  641.    This function pretty prints output.
  642.  
  643.    Wrapper function to have things in the right namespace.
  644.  
  645. */
  646.  
  647. void megahal_output(char *output)
  648. {
  649.     if(!quiet)
  650.     write_output(output);
  651. }
  652.  
  653. /*
  654.    megahal_input --
  655.  
  656.    Get a string from stdin, using a prompt.
  657.  
  658.   */
  659.  
  660. char *megahal_input(char *prompt)
  661. {
  662.     if (noprompt)
  663.     return read_input("");
  664.     else
  665.     return read_input(prompt);
  666. }
  667.  
  668. /*
  669.    megahal_command --
  670.  
  671.    Check to see if input is a megahal command, and if so, act upon it.
  672.  
  673.    Returns 1 if it is a command, 0 if it is not.
  674.  
  675.   */
  676.  
  677. int megahal_command(char *input)
  678. {
  679.     int position = 0;
  680.     char *output;
  681.  
  682.     make_words(input,words);
  683.     switch(execute_command(words, &position)) {
  684.     case EXIT:
  685.     exithal();
  686.     break;
  687.     case QUIT:
  688.     save_model("megahal.brn", model);
  689.     exithal();
  690.     break;
  691.     case SAVE:
  692.     save_model("megahal.brn", model);
  693.     break;
  694.     case DELAY:
  695.     typing_delay=!typing_delay;
  696.     printf("MegaHAL typing is now %s.\n", typing_delay?"on":"off");
  697.     return 1;
  698.     case SPEECH:
  699.     speech=!speech;
  700.     printf("MegaHAL speech is now %s.\n", speech?"on":"off");
  701.     return 1;
  702.     case HELP:
  703.     help();
  704.     return 1;
  705.     case VOICELIST:
  706.     listvoices();
  707.     return 1;
  708.     case VOICE:
  709.     changevoice(words, position);
  710.     return 1;
  711.     case BRAIN:
  712.     change_personality(words, position, &model);
  713.     make_greeting(greets);
  714.     output=generate_reply(model, greets);
  715.     write_output(output);
  716.     return 1;
  717.     case QUIET:
  718.     quiet=!quiet;
  719.     return 1;
  720.     default:
  721.     return 0;
  722.     }
  723.     return 0;
  724. }
  725.  
  726. /*
  727.    megahal_cleanup --
  728.  
  729.    Clean up everything. Prepare for exit.
  730.  
  731.   */
  732.  
  733. void megahal_cleanup(void)
  734. {
  735.     save_model("megahal.brn", model);
  736.  
  737. #ifdef AMIGA
  738.     CloseLocale(_AmigaLocale);
  739. #endif
  740. }
  741.  
  742.  
  743.  
  744. /*---------------------------------------------------------------------------*/
  745.  
  746. /*
  747.  *      Function:   Execute_Command
  748.  *
  749.  *      Purpose:        Detect whether the user has typed a command, and
  750.  *                      execute the corresponding function.
  751.  */
  752. COMMAND_WORDS execute_command(DICTIONARY *words, int *position)
  753. {
  754.     register unsigned int i;
  755.     register unsigned int j;
  756.  
  757.     /*
  758.      *      If there is only one word, then it can't be a command.
  759.      */
  760.     *position=words->size+1;
  761.     if(words->size<=1) return(UNKNOWN);
  762.  
  763.     /*
  764.      *      Search through the word array.  If a command prefix is found,
  765.      *      then try to match the following word with a command word.  If
  766.      *      a match is found, then return a command identifier.  If the
  767.      *      Following word is a number, then change the judge.  Otherwise,
  768.      *      continue the search.
  769.      */
  770.     for(i=0; i<words->size-1; ++i)
  771.     /*
  772.      *      The command prefix was found.
  773.      */
  774.     if(words->entry[i].word[words->entry[i].length - 1] == '#') {
  775.         /*
  776.          *      Look for a command word.
  777.          */
  778.         for(j = 0; j < COMMAND_SIZE; ++j)
  779.         if(wordcmp(command[j].word, words->entry[i + 1]) == 0) {
  780.             *position = i + 1;
  781.             return(command[j].command);
  782.         }
  783.     }
  784.  
  785.     return(UNKNOWN);
  786. }
  787.  
  788. /*---------------------------------------------------------------------------*/
  789.  
  790. /*
  791.  *      Function:   ExitHAL
  792.  *
  793.  *      Purpose:        Terminate the program.
  794.  */
  795. void exithal(void)
  796. {
  797. #ifdef __mac_os
  798.     /*
  799.      *      Must be called because it does use some system memory
  800.      */
  801.     if (gSpeechChannel) {
  802.     StopSpeech(gSpeechChannel);
  803.     DisposeSpeechChannel(gSpeechChannel);
  804.     gSpeechChannel = nil;
  805.     }
  806. #endif
  807.  
  808.     exit(0);
  809. }
  810.  
  811. /*---------------------------------------------------------------------------*/
  812.  
  813. /*
  814.  *      Function:   Read_Input
  815.  *
  816.  *      Purpose:        Read an input string from the user.
  817.  */
  818. char *read_input(char *prompt)
  819. {
  820.     static char *input=NULL;
  821.     bool finish;
  822.     int length;
  823.     int c;
  824.  
  825.     /*
  826.      *      Perform some initializations.  The finish boolean variable is used
  827.      *      to detect a double line-feed, while length contains the number of
  828.      *      characters in the input string.
  829.      */
  830.     finish=FALSE;
  831.     length=0;
  832.     if(input==NULL) {
  833.     input=(char *)malloc(sizeof(char));
  834.     if(input==NULL) {
  835.         error("read_input", "Unable to allocate the input string");
  836.         return(input);
  837.     }
  838.     }
  839.  
  840.     /*
  841.      *      Display the prompt to the user.
  842.      */
  843.     fprintf(stdout, prompt);
  844.     fflush(stdout);
  845.  
  846.     /*
  847.      *      Loop forever, reading characters and putting them into the input
  848.      *      string.
  849.      */
  850.     while(TRUE) {
  851.  
  852.     /*
  853.      *      Read a single character from stdin.
  854.      */
  855.     c=getc(stdin);
  856.  
  857.     /*
  858.      *      If the character is a line-feed, then set the finish variable
  859.      *      to TRUE.  If it already is TRUE, then this is a double line-feed,
  860.      *      in which case we should exit.  After a line-feed, display the
  861.      *      prompt again, and set the character to the space character, as
  862.      *      we don't permit linefeeds to appear in the input.
  863.      */
  864.     if((char)(c)=='\n') {
  865.         if(finish==TRUE) break;
  866.         fprintf(stdout, prompt);
  867.         fflush(stdout);
  868.         finish=TRUE;
  869.         c=32;
  870.     } else {
  871.         finish=FALSE;
  872.     }
  873.  
  874.     /*
  875.      *      Re-allocate the input string so that it can hold one more
  876.      *      character.
  877.      */
  878.     ++length;
  879.     input=(char *)realloc((char *)input,sizeof(char)*(length+1));
  880.     if(input==NULL) {
  881.         error("read_input", "Unable to re-allocate the input string");
  882.         return(NULL);
  883.     }
  884.  
  885.     /*
  886.      *      Add the character just read to the input string.
  887.      */
  888.     input[length-1]=(char)c;
  889.     input[length]='\0';
  890.     }
  891.  
  892.     while(isspace(input[length-1])) --length;
  893.     input[length]='\0';
  894.  
  895.     /*
  896.      *      We have finished, so return the input string.
  897.      */
  898.     return(input);
  899. }
  900.  
  901. /*---------------------------------------------------------------------------*/
  902.  
  903. /*
  904.  *      Function:   Initialize_Error
  905.  *
  906.  *      Purpose:        Close the current error file pointer, and open a new one.
  907.  */
  908. bool initialize_error(char *filename)
  909. {
  910.     if(errorfp!=stderr) fclose(errorfp);
  911.  
  912.     if(filename==NULL) return(TRUE);
  913.  
  914.     errorfp = fopen(filename, "a");
  915.     if(errorfp==NULL) {
  916.     errorfp=stderr;
  917.     return(FALSE);
  918.     }
  919.     return(print_header(errorfp));
  920. }
  921.  
  922. /*---------------------------------------------------------------------------*/
  923.  
  924. /*
  925.  *      Function:   Error
  926.  *
  927.  *      Purpose:        Print the specified message to the error file.
  928.  */
  929. void error(char *title, char *fmt, ...)
  930. {
  931.     va_list argp;
  932.  
  933.     fprintf(errorfp, "%s: ", title);
  934.     va_start(argp, fmt);
  935.     vfprintf(errorfp, fmt, argp);
  936.     va_end(argp);
  937.     fprintf(errorfp, ".\n");
  938.     fflush(errorfp);
  939.  
  940.     fprintf(stderr, "MegaHAL died for some reason; check the error log.\n");
  941.  
  942.     exit(1);
  943. }
  944.  
  945. /*---------------------------------------------------------------------------*/
  946.  
  947. bool warn(char *title, char *fmt, ...)
  948. {
  949.     va_list argp;
  950.  
  951.     fprintf(errorfp, "%s: ", title);
  952.     va_start(argp, fmt);
  953.     vfprintf(errorfp, fmt, argp);
  954.     va_end(argp);
  955.     fprintf(errorfp, ".\n");
  956.     fflush(errorfp);
  957.  
  958.     fprintf(stderr, "MegaHAL emitted a warning; check the error log.\n");
  959.  
  960.     return(TRUE);
  961. }
  962.  
  963. /*---------------------------------------------------------------------------*/
  964.  
  965. /*
  966.  *      Function:   Initialize_Status
  967.  *
  968.  *      Purpose:        Close the current status file pointer, and open a new one.
  969.  */
  970. bool initialize_status(char *filename)
  971. {
  972.     if(statusfp!=stdout) fclose(statusfp);
  973.     if(filename==NULL) return(FALSE);
  974.     statusfp=fopen(filename, "a");
  975.     if(statusfp==NULL) {
  976.     statusfp=stdout;
  977.     return(FALSE);
  978.     }
  979.     return(print_header(statusfp));
  980. }
  981.  
  982. /*---------------------------------------------------------------------------*/
  983.  
  984. /*
  985.  *      Function:   Status
  986.  *
  987.  *      Purpose:        Print the specified message to the status file.
  988.  */
  989. bool status(char *fmt, ...)
  990. {
  991.     va_list argp;
  992.  
  993.     va_start(argp, fmt);
  994.     vfprintf(statusfp, fmt, argp);
  995.     va_end(argp);
  996.     fflush(statusfp);
  997.  
  998.     return(TRUE);
  999. }
  1000.  
  1001. /*---------------------------------------------------------------------------*/
  1002.  
  1003. /*
  1004.  *      Function:   Print_Header
  1005.  *
  1006.  *      Purpose:        Display a copyright message and timestamp.
  1007.  */
  1008. bool print_header(FILE *file)
  1009. {
  1010.     time_t clock;
  1011.     char timestamp[1024];
  1012.     struct tm *local;
  1013.  
  1014.     clock=time(NULL);
  1015.     local=localtime(&clock);
  1016.     strftime(timestamp, 1024, "Start at: [%Y/%m/%d %H:%M:%S]\n", local);
  1017.  
  1018.     fprintf(file, "MegaHALv8\n");
  1019.     fprintf(file, "Copyright (C) 1998 Jason Hutchens\n");
  1020.     fprintf(file, timestamp);
  1021.     fflush(file);
  1022.  
  1023.     return(TRUE);
  1024. }
  1025.  
  1026. /*---------------------------------------------------------------------------*/
  1027.  
  1028. /*
  1029.  *    Function:   Write_Output
  1030.  *
  1031.  *    Purpose:    Display the output string.
  1032.  */
  1033. void write_output(char *output)
  1034. {
  1035.     char *formatted;
  1036.     char *bit;
  1037.  
  1038.     capitalize(output);
  1039.     speak(output);
  1040.  
  1041.     width=75;
  1042.     formatted=format_output(output);
  1043.     delay(formatted);
  1044.     width=64;
  1045.     formatted=format_output(output);
  1046.  
  1047.     bit=strtok(formatted, "\n");
  1048.     if(bit==NULL) (void)status("MegaHAL: %s\n", formatted);
  1049.     while(bit!=NULL) {
  1050.     (void)status("MegaHAL: %s\n", bit);
  1051.     bit=strtok(NULL, "\n");
  1052.     }
  1053. }
  1054.  
  1055. /*---------------------------------------------------------------------------*/
  1056.  
  1057. /*
  1058.  *      Function:   Capitalize
  1059.  *
  1060.  *      Purpose:        Convert a string to look nice.
  1061.  */
  1062. void capitalize(char *string)
  1063. {
  1064.     register unsigned int i;
  1065.     bool start=TRUE;
  1066.  
  1067.     for(i=0; i<strlen(string); ++i) {
  1068.     if(isalpha(string[i])) {
  1069.         if(start==TRUE) string[i]=(char)toupper((int)string[i]);
  1070.         else string[i]=(char)tolower((int)string[i]);
  1071.         start=FALSE;
  1072.     }
  1073.     if((i>2)&&(strchr("!.?", string[i-1])!=NULL)&&(isspace(string[i])))
  1074.         start=TRUE;
  1075.     }
  1076. }
  1077.  
  1078. /*---------------------------------------------------------------------------*/
  1079.  
  1080. /*
  1081.  *      Function:   Upper
  1082.  *
  1083.  *      Purpose:        Convert a string to its uppercase representation.
  1084.  */
  1085. void upper(char *string)
  1086. {
  1087.     register unsigned int i;
  1088.  
  1089.     for(i=0; i<strlen(string); ++i) string[i]=(char)toupper((int)string[i]);
  1090. }
  1091.  
  1092. /*---------------------------------------------------------------------------*/
  1093.  
  1094. /*
  1095.  *    Function:   Write_Input
  1096.  *
  1097.  *    Purpose:    Log the user's input
  1098.  */
  1099. void write_input(char *input)
  1100. {
  1101.     char *formatted;
  1102.     char *bit;
  1103.  
  1104.     width=64;
  1105.     formatted=format_output(input);
  1106.  
  1107.     bit=strtok(formatted, "\n");
  1108.     if(bit==NULL) (void)status("User:    %s\n", formatted);
  1109.     while(bit!=NULL) {
  1110.     (void)status("User:    %s\n", bit);
  1111.     bit=strtok(NULL, "\n");
  1112.     }
  1113. }
  1114.  
  1115. /*---------------------------------------------------------------------------*/
  1116.  
  1117. /*
  1118.  *    Function:   Format_Output
  1119.  *
  1120.  *    Purpose:    Format a string to display nicely on a terminal of a given
  1121.  *                width.
  1122.  */
  1123. static char *format_output(char *output)
  1124. {
  1125.     static char *formatted=NULL;
  1126.     register unsigned int i,j,c;
  1127.     int l;
  1128.     if(formatted==NULL) {
  1129.     formatted=(char *)malloc(sizeof(char));
  1130.     if(formatted==NULL) {
  1131.         error("format_output", "Unable to allocate formatted");
  1132.         return("ERROR");
  1133.     }
  1134.     }
  1135.  
  1136.     formatted=(char *)realloc((char *)formatted, sizeof(char)*(strlen(output)+2));
  1137.     if(formatted==NULL) {
  1138.     error("format_output", "Unable to re-allocate formatted");
  1139.     return("ERROR");
  1140.     }
  1141.  
  1142.     l=0;
  1143.     j=0;
  1144.     for(i=0; i<strlen(output); ++i) {
  1145.     if((l==0)&&(isspace(output[i]))) continue;
  1146.     formatted[j]=output[i];
  1147.     ++j;
  1148.     ++l;
  1149.     if(!nowrap)
  1150.         if(l>=width)
  1151.         for(c=j-1; c>0; --c)
  1152.             if(formatted[c]==' ') {
  1153.             formatted[c]='\n';
  1154.             l=j-c-1;
  1155.             break;
  1156.             }
  1157.     }
  1158.     if((j>0)&&(formatted[j-1]!='\n')) {
  1159.     formatted[j]='\n';
  1160.     ++j;
  1161.     }
  1162.     formatted[j]='\0';
  1163.  
  1164.     return(formatted);
  1165. }
  1166.  
  1167. /*---------------------------------------------------------------------------*/
  1168.  
  1169. /*
  1170.  *      Function:   Add_Word
  1171.  *
  1172.  *      Purpose:        Add a word to a dictionary, and return the identifier
  1173.  *                      assigned to the word.  If the word already exists in
  1174.  *                      the dictionary, then return its current identifier
  1175.  *                      without adding it again.
  1176.  */
  1177. BYTE2 add_word(DICTIONARY *dictionary, STRING word)
  1178. {
  1179.     register int i;
  1180.     int position;
  1181.     bool found;
  1182.  
  1183.     /*
  1184.      *      If the word's already in the dictionary, there is no need to add it
  1185.      */
  1186.     position=search_dictionary(dictionary, word, &found);
  1187.     if(found==TRUE) goto succeed;
  1188.  
  1189.     /*
  1190.      *      Increase the number of words in the dictionary
  1191.      */
  1192.     dictionary->size+=1;
  1193.  
  1194.     /*
  1195.      *      Allocate one more entry for the word index
  1196.      */
  1197.     if(dictionary->index==NULL) {
  1198.     dictionary->index=(BYTE2 *)malloc(sizeof(BYTE2)*
  1199.                       (dictionary->size));
  1200.     } else {
  1201.     dictionary->index=(BYTE2 *)realloc((BYTE2 *)
  1202.                        (dictionary->index),sizeof(BYTE2)*(dictionary->size));
  1203.     }
  1204.     if(dictionary->index==NULL) {
  1205.     error("add_word", "Unable to reallocate the index.");
  1206.     goto fail;
  1207.     }
  1208.  
  1209.     /*
  1210.      *      Allocate one more entry for the word array
  1211.      */
  1212.     if(dictionary->entry==NULL) {
  1213.     dictionary->entry=(STRING *)malloc(sizeof(STRING)*(dictionary->size));
  1214.     } else {
  1215.     dictionary->entry=(STRING *)realloc((STRING *)(dictionary->entry),
  1216.                         sizeof(STRING)*(dictionary->size));
  1217.     }
  1218.     if(dictionary->entry==NULL) {
  1219.     error("add_word", "Unable to reallocate the dictionary to %d elements.", dictionary->size);
  1220.     goto fail;
  1221.     }
  1222.  
  1223.     /*
  1224.      *      Copy the new word into the word array
  1225.      */
  1226.     dictionary->entry[dictionary->size-1].length=word.length;
  1227.     dictionary->entry[dictionary->size-1].word=(char *)malloc(sizeof(char)*
  1228.                                   (word.length));
  1229.     if(dictionary->entry[dictionary->size-1].word==NULL) {
  1230.     error("add_word", "Unable to allocate the word.");
  1231.     goto fail;
  1232.     }
  1233.     for(i=0; i<word.length; ++i)
  1234.     dictionary->entry[dictionary->size-1].word[i]=word.word[i];
  1235.  
  1236.     /*
  1237.      *      Shuffle the word index to keep it sorted alphabetically
  1238.      */
  1239.     for(i=(dictionary->size-1); i>position; --i)
  1240.     dictionary->index[i]=dictionary->index[i-1];
  1241.  
  1242.     /*
  1243.      *      Copy the new symbol identifier into the word index
  1244.      */
  1245.     dictionary->index[position]=dictionary->size-1;
  1246.  
  1247. succeed:
  1248.     return(dictionary->index[position]);
  1249.  
  1250. fail:
  1251.     return(0);
  1252. }
  1253.  
  1254. /*---------------------------------------------------------------------------*/
  1255.  
  1256. /*
  1257.  *      Function:   Search_Dictionary
  1258.  *
  1259.  *      Purpose:        Search the dictionary for the specified word, returning its
  1260.  *                      position in the index if found, or the position where it
  1261.  *                      should be inserted otherwise.
  1262.  */
  1263. int search_dictionary(DICTIONARY *dictionary, STRING word, bool *find)
  1264. {
  1265.     int position;
  1266.     int min;
  1267.     int max;
  1268.     int middle;
  1269.     int compar;
  1270.  
  1271.     /*
  1272.      *      If the dictionary is empty, then obviously the word won't be found
  1273.      */
  1274.     if(dictionary->size==0) {
  1275.     position=0;
  1276.     goto notfound;
  1277.     }
  1278.  
  1279.     /*
  1280.      *      Initialize the lower and upper bounds of the search
  1281.      */
  1282.     min=0;
  1283.     max=dictionary->size-1;
  1284.     /*
  1285.      *      Search repeatedly, halving the search space each time, until either
  1286.      *      the entry is found, or the search space becomes empty
  1287.      */
  1288.     while(TRUE) {
  1289.     /*
  1290.      *      See whether the middle element of the search space is greater
  1291.      *      than, equal to, or less than the element being searched for.
  1292.      */
  1293.     middle=(min+max)/2;
  1294.     compar=wordcmp(word, dictionary->entry[dictionary->index[middle]]);
  1295.     /*
  1296.      *      If it is equal then we have found the element.  Otherwise we
  1297.      *      can halve the search space accordingly.
  1298.      */
  1299.     if(compar==0) {
  1300.         position=middle;
  1301.         goto found;
  1302.     } else if(compar>0) {
  1303.         if(max==middle) {
  1304.         position=middle+1;
  1305.         goto notfound;
  1306.         }
  1307.         min=middle+1;
  1308.     } else {
  1309.         if(min==middle) {
  1310.         position=middle;
  1311.         goto notfound;
  1312.         }
  1313.         max=middle-1;
  1314.     }
  1315.     }
  1316.  
  1317. found:
  1318.     *find=TRUE;
  1319.     return(position);
  1320.  
  1321. notfound:
  1322.     *find=FALSE;
  1323.     return(position);
  1324. }
  1325.  
  1326. /*---------------------------------------------------------------------------*/
  1327.  
  1328. /*
  1329.  *      Function:   Find_Word
  1330.  *
  1331.  *      Purpose:        Return the symbol corresponding to the word specified.
  1332.  *                      We assume that the word with index zero is equal to a
  1333.  *                      NULL word, indicating an error condition.
  1334.  */
  1335. BYTE2 find_word(DICTIONARY *dictionary, STRING word)
  1336. {
  1337.     int position;
  1338.     bool found;
  1339.  
  1340.     position=search_dictionary(dictionary, word, &found);
  1341.  
  1342.     if(found==TRUE) return(dictionary->index[position]);
  1343.     else return(0);
  1344. }
  1345.  
  1346. /*---------------------------------------------------------------------------*/
  1347.  
  1348. /*
  1349.  *      Function:   Wordcmp
  1350.  *
  1351.  *      Purpose:        Compare two words, and return an integer indicating whether
  1352.  *                      the first word is less than, equal to or greater than the
  1353.  *                      second word.
  1354.  */
  1355. int wordcmp(STRING word1, STRING word2)
  1356. {
  1357.     register int i;
  1358.     int bound;
  1359.  
  1360.     bound=MIN(word1.length,word2.length);
  1361.  
  1362.     for(i=0; i<bound; ++i)
  1363.     if(toupper(word1.word[i])!=toupper(word2.word[i]))
  1364.         return((int)(toupper(word1.word[i])-toupper(word2.word[i])));
  1365.  
  1366.     if(word1.length<word2.length) return(-1);
  1367.     if(word1.length>word2.length) return(1);
  1368.  
  1369.     return(0);
  1370. }
  1371.  
  1372. /*---------------------------------------------------------------------------*/
  1373.  
  1374. /*
  1375.  *      Function:   Free_Dictionary
  1376.  *
  1377.  *      Purpose:        Release the memory consumed by the dictionary.
  1378.  */
  1379. void free_dictionary(DICTIONARY *dictionary)
  1380. {
  1381.     if(dictionary==NULL) return;
  1382.     if(dictionary->entry!=NULL) {
  1383.     free(dictionary->entry);
  1384.     dictionary->entry=NULL;
  1385.     }
  1386.     if(dictionary->index!=NULL) {
  1387.     free(dictionary->index);
  1388.     dictionary->index=NULL;
  1389.     }
  1390.     dictionary->size=0;
  1391. }
  1392.  
  1393. /*---------------------------------------------------------------------------*/
  1394.  
  1395. void free_model(MODEL *model)
  1396. {
  1397.     if(model==NULL) return;
  1398.     if(model->forward!=NULL) {
  1399.     free_tree(model->forward);
  1400.     }
  1401.     if(model->backward!=NULL) {
  1402.     free_tree(model->backward);
  1403.     }
  1404.     if(model->context!=NULL) {
  1405.     free(model->context);
  1406.     }
  1407.     if(model->dictionary!=NULL) {
  1408.     free_dictionary(model->dictionary);
  1409.     free(model->dictionary);
  1410.     }
  1411.     free(model);
  1412. }
  1413.  
  1414. /*---------------------------------------------------------------------------*/
  1415.  
  1416. void free_tree(TREE *tree)
  1417. {
  1418.     static int level=0;
  1419.     register unsigned int i;
  1420.  
  1421.     if(tree==NULL) return;
  1422.  
  1423.     if(tree->tree!=NULL) {
  1424.     if(level==0) progress("Freeing tree", 0, 1);
  1425.     for(i=0; i<tree->branch; ++i) {
  1426.         ++level;
  1427.         free_tree(tree->tree[i]);
  1428.         --level;
  1429.         if(level==0) progress(NULL, i, tree->branch);
  1430.     }
  1431.     if(level==0) progress(NULL, 1, 1);
  1432.     free(tree->tree);
  1433.     }
  1434.     free(tree);
  1435. }
  1436.  
  1437. /*---------------------------------------------------------------------------*/
  1438.  
  1439. /*
  1440.  *      Function:   Initialize_Dictionary
  1441.  *
  1442.  *      Purpose:        Add dummy words to the dictionary.
  1443.  */
  1444. void initialize_dictionary(DICTIONARY *dictionary)
  1445. {
  1446.     STRING word={ 7, "<ERROR>" };
  1447.     STRING end={ 5, "<FIN>" };
  1448.  
  1449.     (void)add_word(dictionary, word);
  1450.     (void)add_word(dictionary, end);
  1451. }
  1452.  
  1453. /*---------------------------------------------------------------------------*/
  1454.  
  1455. /*
  1456.  *      Function:   New_Dictionary
  1457.  *
  1458.  *      Purpose:        Allocate room for a new dictionary.
  1459.  */
  1460. DICTIONARY *new_dictionary(void)
  1461. {
  1462.     DICTIONARY *dictionary=NULL;
  1463.  
  1464.     dictionary=(DICTIONARY *)malloc(sizeof(DICTIONARY));
  1465.     if(dictionary==NULL) {
  1466.     error("new_dictionary", "Unable to allocate dictionary.");
  1467.     return(NULL);
  1468.     }
  1469.  
  1470.     dictionary->size=0;
  1471.     dictionary->index=NULL;
  1472.     dictionary->entry=NULL;
  1473.  
  1474.     return(dictionary);
  1475. }
  1476.  
  1477. /*---------------------------------------------------------------------------*/
  1478.  
  1479. /*
  1480.  *      Function:   Save_Dictionary
  1481.  *
  1482.  *      Purpose:        Save a dictionary to the specified file.
  1483.  */
  1484. void save_dictionary(FILE *file, DICTIONARY *dictionary)
  1485. {
  1486.     register unsigned int i;
  1487.  
  1488.     fwrite(&(dictionary->size), sizeof(BYTE4), 1, file);
  1489.     progress("Saving dictionary", 0, 1);
  1490.     for(i=0; i<dictionary->size; ++i) {
  1491.     save_word(file, dictionary->entry[i]);
  1492.     progress(NULL, i, dictionary->size);
  1493.     }
  1494.     progress(NULL, 1, 1);
  1495. }
  1496.  
  1497. /*---------------------------------------------------------------------------*/
  1498.  
  1499. /*
  1500.  *      Function:   Load_Dictionary
  1501.  *
  1502.  *      Purpose:        Load a dictionary from the specified file.
  1503.  */
  1504. void load_dictionary(FILE *file, DICTIONARY *dictionary)
  1505. {
  1506.     register int i;
  1507.     int size;
  1508.  
  1509.     fread(&size, sizeof(BYTE4), 1, file);
  1510.     progress("Loading dictionary", 0, 1);
  1511.     for(i=0; i<size; ++i) {
  1512.     load_word(file, dictionary);
  1513.     progress(NULL, i, size);
  1514.     }
  1515.     progress(NULL, 1, 1);
  1516. }
  1517.  
  1518. /*---------------------------------------------------------------------------*/
  1519.  
  1520. /*
  1521.  *      Function:   Save_Word
  1522.  *
  1523.  *      Purpose:        Save a dictionary word to a file.
  1524.  */
  1525. void save_word(FILE *file, STRING word)
  1526. {
  1527.     register unsigned int i;
  1528.  
  1529.     fwrite(&(word.length), sizeof(BYTE1), 1, file);
  1530.     for(i=0; i<word.length; ++i)
  1531.     fwrite(&(word.word[i]), sizeof(char), 1, file);
  1532. }
  1533.  
  1534. /*---------------------------------------------------------------------------*/
  1535.  
  1536. /*
  1537.  *      Function:   Load_Word
  1538.  *
  1539.  *      Purpose:        Load a dictionary word from a file.
  1540.  */
  1541. void load_word(FILE *file, DICTIONARY *dictionary)
  1542. {
  1543.     register unsigned int i;
  1544.     STRING word;
  1545.  
  1546.     fread(&(word.length), sizeof(BYTE1), 1, file);
  1547.     word.word=(char *)malloc(sizeof(char)*word.length);
  1548.     if(word.word==NULL) {
  1549.     error("load_word", "Unable to allocate word");
  1550.     return;
  1551.     }
  1552.     for(i=0; i<word.length; ++i)
  1553.     fread(&(word.word[i]), sizeof(char), 1, file);
  1554.     add_word(dictionary, word);
  1555.     free(word.word);
  1556. }
  1557.  
  1558. /*---------------------------------------------------------------------------*/
  1559.  
  1560. /*
  1561.  *      Function:   New_Node
  1562.  *
  1563.  *      Purpose:        Allocate a new node for the n-gram tree, and initialise
  1564.  *                      its contents to sensible values.
  1565.  */
  1566. TREE *new_node(void)
  1567. {
  1568.     TREE *node=NULL;
  1569.  
  1570.     /*
  1571.      *      Allocate memory for the new node
  1572.      */
  1573.     node=(TREE *)malloc(sizeof(TREE));
  1574.     if(node==NULL) {
  1575.     error("new_node", "Unable to allocate the node.");
  1576.     goto fail;
  1577.     }
  1578.  
  1579.     /*
  1580.      *      Initialise the contents of the node
  1581.      */
  1582.     node->symbol=0;
  1583.     node->usage=0;
  1584.     node->count=0;
  1585.     node->branch=0;
  1586.     node->tree=NULL;
  1587.  
  1588.     return(node);
  1589.  
  1590. fail:
  1591.     if(node!=NULL) free(node);
  1592.     return(NULL);
  1593. }
  1594.  
  1595. /*---------------------------------------------------------------------------*/
  1596.  
  1597. /*
  1598.  *      Function:   New_Model
  1599.  *
  1600.  *      Purpose:        Create and initialise a new ngram model.
  1601.  */
  1602. MODEL *new_model(int order)
  1603. {
  1604.     MODEL *model=NULL;
  1605.  
  1606.     model=(MODEL *)malloc(sizeof(MODEL));
  1607.     if(model==NULL) {
  1608.     error("new_model", "Unable to allocate model.");
  1609.     goto fail;
  1610.     }
  1611.  
  1612.     model->order=order;
  1613.     model->forward=new_node();
  1614.     model->backward=new_node();
  1615.     model->context=(TREE **)malloc(sizeof(TREE *)*(order+2));
  1616.     if(model->context==NULL) {
  1617.     error("new_model", "Unable to allocate context array.");
  1618.     goto fail;
  1619.     }
  1620.     initialize_context(model);
  1621.     model->dictionary=new_dictionary();
  1622.     initialize_dictionary(model->dictionary);
  1623.  
  1624.     return(model);
  1625.  
  1626. fail:
  1627.     return(NULL);
  1628. }
  1629.  
  1630. /*---------------------------------------------------------------------------*/
  1631.  
  1632. /*
  1633.  *      Function:   Update_Model
  1634.  *
  1635.  *      Purpose:        Update the model with the specified symbol.
  1636.  */
  1637. void update_model(MODEL *model, int symbol)
  1638. {
  1639.     register unsigned int i;
  1640.  
  1641.     /*
  1642.      *      Update all of the models in the current context with the specified
  1643.      *      symbol.
  1644.      */
  1645.     for(i=(model->order+1); i>0; --i)
  1646.     if(model->context[i-1]!=NULL)
  1647.         model->context[i]=add_symbol(model->context[i-1], (BYTE2)symbol);
  1648.  
  1649.     return;
  1650. }
  1651.  
  1652. /*---------------------------------------------------------------------------*/
  1653.  
  1654. /*
  1655.  *      Function:   Update_Context
  1656.  *
  1657.  *      Purpose:        Update the context of the model without adding the symbol.
  1658.  */
  1659. void update_context(MODEL *model, int symbol)
  1660. {
  1661.     register unsigned int i;
  1662.  
  1663.     for(i=(model->order+1); i>0; --i)
  1664.     if(model->context[i-1]!=NULL)
  1665.         model->context[i]=find_symbol(model->context[i-1], symbol);
  1666. }
  1667.  
  1668. /*---------------------------------------------------------------------------*/
  1669.  
  1670. /*
  1671.  *      Function:   Add_Symbol
  1672.  *
  1673.  *      Purpose:        Update the statistics of the specified tree with the
  1674.  *                      specified symbol, which may mean growing the tree if the
  1675.  *                      symbol hasn't been seen in this context before.
  1676.  */
  1677. TREE *add_symbol(TREE *tree, BYTE2 symbol)
  1678. {
  1679.     TREE *node=NULL;
  1680.  
  1681.     /*
  1682.      *      Search for the symbol in the subtree of the tree node.
  1683.      */
  1684.     node=find_symbol_add(tree, symbol);
  1685.  
  1686.     /*
  1687.      *      Increment the symbol counts
  1688.      */
  1689.     if((node->count<65535)) {
  1690.     node->count+=1;
  1691.     tree->usage+=1;
  1692.     }
  1693.  
  1694.     return(node);
  1695. }
  1696.  
  1697. /*---------------------------------------------------------------------------*/
  1698.  
  1699. /*
  1700.  *      Function:   Find_Symbol
  1701.  *
  1702.  *      Purpose:        Return a pointer to the child node, if one exists, which
  1703.  *                      contains the specified symbol.
  1704.  */
  1705. TREE *find_symbol(TREE *node, int symbol)
  1706. {
  1707.     register unsigned int i;
  1708.     TREE *found=NULL;
  1709.     bool found_symbol=FALSE;
  1710.  
  1711.     /*
  1712.      *      Perform a binary search for the symbol.
  1713.      */
  1714.     i=search_node(node, symbol, &found_symbol);
  1715.     if(found_symbol==TRUE) found=node->tree[i];
  1716.  
  1717.     return(found);
  1718. }
  1719.  
  1720. /*---------------------------------------------------------------------------*/
  1721.  
  1722. /*
  1723.  *      Function:   Find_Symbol_Add
  1724.  *
  1725.  *      Purpose:        This function is conceptually similar to find_symbol,
  1726.  *                      apart from the fact that if the symbol is not found,
  1727.  *                      a new node is automatically allocated and added to the
  1728.  *                      tree.
  1729.  */
  1730. TREE *find_symbol_add(TREE *node, int symbol)
  1731. {
  1732.     register unsigned int i;
  1733.     TREE *found=NULL;
  1734.     bool found_symbol=FALSE;
  1735.  
  1736.     /*
  1737.      *      Perform a binary search for the symbol.  If the symbol isn't found,
  1738.      *      attach a new sub-node to the tree node so that it remains sorted.
  1739.      */
  1740.     i=search_node(node, symbol, &found_symbol);
  1741.     if(found_symbol==TRUE) {
  1742.     found=node->tree[i];
  1743.     } else {
  1744.     found=new_node();
  1745.     found->symbol=symbol;
  1746.     add_node(node, found, i);
  1747.     }
  1748.  
  1749.     return(found);
  1750. }
  1751.  
  1752. /*---------------------------------------------------------------------------*/
  1753.  
  1754. /*
  1755.  *      Function:   Add_Node
  1756.  *
  1757.  *      Purpose:        Attach a new child node to the sub-tree of the tree
  1758.  *                      specified.
  1759.  */
  1760. void add_node(TREE *tree, TREE *node, int position)
  1761. {
  1762.     register int i;
  1763.  
  1764.     /*
  1765.      *      Allocate room for one more child node, which may mean allocating
  1766.      *      the sub-tree from scratch.
  1767.      */
  1768.     if(tree->tree==NULL) {
  1769.     tree->tree=(TREE **)malloc(sizeof(TREE *)*(tree->branch+1));
  1770.     } else {
  1771.     tree->tree=(TREE **)realloc((TREE **)(tree->tree),sizeof(TREE *)*
  1772.                     (tree->branch+1));
  1773.     }
  1774.     if(tree->tree==NULL) {
  1775.     error("add_node", "Unable to reallocate subtree.");
  1776.     return;
  1777.     }
  1778.  
  1779.     /*
  1780.      *      Shuffle the nodes down so that we can insert the new node at the
  1781.      *      subtree index given by position.
  1782.      */
  1783.     for(i=tree->branch; i>position; --i)
  1784.     tree->tree[i]=tree->tree[i-1];
  1785.  
  1786.     /*
  1787.      *      Add the new node to the sub-tree.
  1788.      */
  1789.     tree->tree[position]=node;
  1790.     tree->branch+=1;
  1791. }
  1792.  
  1793. /*---------------------------------------------------------------------------*/
  1794.  
  1795. /*
  1796.  *      Function:   Search_Node
  1797.  *
  1798.  *      Purpose:        Perform a binary search for the specified symbol on the
  1799.  *                      subtree of the given node.  Return the position of the
  1800.  *                      child node in the subtree if the symbol was found, or the
  1801.  *                      position where it should be inserted to keep the subtree
  1802.  *                      sorted if it wasn't.
  1803.  */
  1804. int search_node(TREE *node, int symbol, bool *found_symbol)
  1805. {
  1806.     register unsigned int position;
  1807.     int min;
  1808.     int max;
  1809.     int middle;
  1810.     int compar;
  1811.  
  1812.     /*
  1813.      *      Handle the special case where the subtree is empty.
  1814.      */
  1815.     if(node->branch==0) {
  1816.     position=0;
  1817.     goto notfound;
  1818.     }
  1819.  
  1820.     /*
  1821.      *      Perform a binary search on the subtree.
  1822.      */
  1823.     min=0;
  1824.     max=node->branch-1;
  1825.     while(TRUE) {
  1826.     middle=(min+max)/2;
  1827.     compar=symbol-node->tree[middle]->symbol;
  1828.     if(compar==0) {
  1829.         position=middle;
  1830.         goto found;
  1831.     } else if(compar>0) {
  1832.         if(max==middle) {
  1833.         position=middle+1;
  1834.         goto notfound;
  1835.         }
  1836.         min=middle+1;
  1837.     } else {
  1838.         if(min==middle) {
  1839.         position=middle;
  1840.         goto notfound;
  1841.         }
  1842.         max=middle-1;
  1843.     }
  1844.     }
  1845.  
  1846. found:
  1847.     *found_symbol=TRUE;
  1848.     return(position);
  1849.  
  1850. notfound:
  1851.     *found_symbol=FALSE;
  1852.     return(position);
  1853. }
  1854.  
  1855. /*---------------------------------------------------------------------------*/
  1856.  
  1857. /*
  1858.  *      Function:   Initialize_Context
  1859.  *
  1860.  *      Purpose:        Set the context of the model to a default value.
  1861.  */
  1862. void initialize_context(MODEL *model)
  1863. {
  1864.     register unsigned int i;
  1865.  
  1866.     for(i=0; i<=model->order; ++i) model->context[i]=NULL;
  1867. }
  1868.  
  1869. /*---------------------------------------------------------------------------*/
  1870.  
  1871. /*
  1872.  *      Function:   Learn
  1873.  *
  1874.  *      Purpose:        Learn from the user's input.
  1875.  */
  1876. void learn(MODEL *model, DICTIONARY *words)
  1877. {
  1878.     register unsigned int i;
  1879.     register int j;
  1880.     BYTE2 symbol;
  1881.  
  1882.     /*
  1883.      *      We only learn from inputs which are long enough
  1884.      */
  1885.     if(words->size<=(model->order)) return;
  1886.  
  1887.     /*
  1888.      *      Train the model in the forwards direction.  Start by initializing
  1889.      *      the context of the model.
  1890.      */
  1891.     initialize_context(model);
  1892.     model->context[0]=model->forward;
  1893.     for(i=0; i<words->size; ++i) {
  1894.     /*
  1895.      *      Add the symbol to the model's dictionary if necessary, and then
  1896.      *      update the forward model accordingly.
  1897.      */
  1898.     symbol=add_word(model->dictionary, words->entry[i]);
  1899.     update_model(model, symbol);
  1900.     }
  1901.     /*
  1902.      *      Add the sentence-terminating symbol.
  1903.      */
  1904.     update_model(model, 1);
  1905.  
  1906.     /*
  1907.      *      Train the model in the backwards direction.  Start by initializing
  1908.      *      the context of the model.
  1909.      */
  1910.     initialize_context(model);
  1911.     model->context[0]=model->backward;
  1912.     for(j=words->size-1; j>=0; --j) {
  1913.     /*
  1914.      *      Find the symbol in the model's dictionary, and then update
  1915.      *      the backward model accordingly.
  1916.      */
  1917.     symbol=find_word(model->dictionary, words->entry[j]);
  1918.     update_model(model, symbol);
  1919.     }
  1920.     /*
  1921.      *      Add the sentence-terminating symbol.
  1922.      */
  1923.     update_model(model, 1);
  1924.  
  1925.     return;
  1926. }
  1927.  
  1928. /*---------------------------------------------------------------------------*/
  1929.  
  1930. /*
  1931.  *      Function:   Train
  1932.  *
  1933.  *      Purpose:        Infer a MegaHAL brain from the contents of a text file.
  1934.  */
  1935. void train(MODEL *model, char *filename)
  1936. {
  1937.     FILE *file;
  1938.     char buffer[1024];
  1939.     DICTIONARY *words=NULL;
  1940.     int length;
  1941.  
  1942.     if(filename==NULL) return;
  1943.  
  1944.     file=fopen(filename, "r");
  1945.     if(file==NULL) {
  1946.     printf("Unable to find the personality %s\n", filename);
  1947.     return;
  1948.     }
  1949.  
  1950.     fseek(file, 0, 2);
  1951.     length=ftell(file);
  1952.     rewind(file);
  1953.  
  1954.     words=new_dictionary();
  1955.  
  1956.     progress("Training from file", 0, 1);
  1957.     while(!feof(file)) {
  1958.  
  1959.     if(fgets(buffer, 1024, file)==NULL) break;
  1960.     if(buffer[0]=='#') continue;
  1961.  
  1962.     buffer[strlen(buffer)-1]='\0';
  1963.  
  1964.     upper(buffer);
  1965.     make_words(buffer, words);
  1966.     learn(model, words);
  1967.  
  1968.     progress(NULL, ftell(file), length);
  1969.  
  1970.     }
  1971.     progress(NULL, 1, 1);
  1972.  
  1973.     free_dictionary(words);
  1974.     fclose(file);
  1975. }
  1976.  
  1977. /*---------------------------------------------------------------------------*/
  1978.  
  1979. /*
  1980.  *      Function:   Show_Dictionary
  1981.  *
  1982.  *      Purpose:        Display the dictionary for training purposes.
  1983.  */
  1984. void show_dictionary(DICTIONARY *dictionary)
  1985. {
  1986.     register unsigned int i;
  1987.     register unsigned int j;
  1988.     FILE *file;
  1989.  
  1990.     file=fopen("megahal.dic", "w");
  1991.     if(file==NULL) {
  1992.     warn("show_dictionary", "Unable to open file");
  1993.     return;
  1994.     }
  1995.  
  1996.     for(i=0; i<dictionary->size; ++i) {
  1997.     for(j=0; j<dictionary->entry[i].length; ++j)
  1998.         fprintf(file, "%c", dictionary->entry[i].word[j]);
  1999.     fprintf(file, "\n");
  2000.     }
  2001.  
  2002.     fclose(file);
  2003. }
  2004.  
  2005. /*---------------------------------------------------------------------------*/
  2006.  
  2007. /*
  2008.  *      Function:   Save_Model
  2009.  *
  2010.  *      Purpose:        Save the current state to a MegaHAL brain file.
  2011.  */
  2012. void save_model(char *modelname, MODEL *model)
  2013. {
  2014.     FILE *file;
  2015.     static char *filename=NULL;
  2016.  
  2017.     if(filename==NULL) filename=(char *)malloc(sizeof(char)*1);
  2018.  
  2019.     /*
  2020.      *    Allocate memory for the filename
  2021.      */
  2022.     filename=(char *)realloc(filename,
  2023.                  sizeof(char)*(strlen(directory)+strlen(SEP)+12));
  2024.     if(filename==NULL) error("save_model","Unable to allocate filename");
  2025.  
  2026.     show_dictionary(model->dictionary);
  2027.     if(filename==NULL) return;
  2028.  
  2029.     sprintf(filename, "%s%smegahal.brn", directory, SEP);
  2030.     file=fopen(filename, "wb");
  2031.     if(file==NULL) {
  2032.     warn("save_model", "Unable to open file `%s'", filename);
  2033.     return;
  2034.     }
  2035.  
  2036.     fwrite(COOKIE, sizeof(char), strlen(COOKIE), file);
  2037.     fwrite(&(model->order), sizeof(BYTE1), 1, file);
  2038.     save_tree(file, model->forward);
  2039.     save_tree(file, model->backward);
  2040.     save_dictionary(file, model->dictionary);
  2041.  
  2042.     fclose(file);
  2043. }
  2044.  
  2045. /*---------------------------------------------------------------------------*/
  2046.  
  2047. /*
  2048.  *      Function:   Save_Tree
  2049.  *
  2050.  *      Purpose:        Save a tree structure to the specified file.
  2051.  */
  2052. void save_tree(FILE *file, TREE *node)
  2053. {
  2054.     static int level=0;
  2055.     register unsigned int i;
  2056.  
  2057.     fwrite(&(node->symbol), sizeof(BYTE2), 1, file);
  2058.     fwrite(&(node->usage), sizeof(BYTE4), 1, file);
  2059.     fwrite(&(node->count), sizeof(BYTE2), 1, file);
  2060.     fwrite(&(node->branch), sizeof(BYTE2), 1, file);
  2061.  
  2062.     if(level==0) progress("Saving tree", 0, 1);
  2063.     for(i=0; i<node->branch; ++i) {
  2064.     ++level;
  2065.     save_tree(file, node->tree[i]);
  2066.     --level;
  2067.     if(level==0) progress(NULL, i, node->branch);
  2068.     }
  2069.     if(level==0) progress(NULL, 1, 1);
  2070. }
  2071.  
  2072. /*---------------------------------------------------------------------------*/
  2073.  
  2074. /*
  2075.  *      Function:   Load_Tree
  2076.  *
  2077.  *      Purpose:        Load a tree structure from the specified file.
  2078.  */
  2079. void load_tree(FILE *file, TREE *node)
  2080. {
  2081.     static int level=0;
  2082.     register unsigned int i;
  2083.  
  2084.     fread(&(node->symbol), sizeof(BYTE2), 1, file);
  2085.     fread(&(node->usage), sizeof(BYTE4), 1, file);
  2086.     fread(&(node->count), sizeof(BYTE2), 1, file);
  2087.     fread(&(node->branch), sizeof(BYTE2), 1, file);
  2088.  
  2089.     if(node->branch==0) return;
  2090.  
  2091.     node->tree=(TREE **)malloc(sizeof(TREE *)*(node->branch));
  2092.     if(node->tree==NULL) {
  2093.     error("load_tree", "Unable to allocate subtree");
  2094.     return;
  2095.     }
  2096.  
  2097.     if(level==0) progress("Loading tree", 0, 1);
  2098.     for(i=0; i<node->branch; ++i) {
  2099.     node->tree[i]=new_node();
  2100.     ++level;
  2101.     load_tree(file, node->tree[i]);
  2102.     --level;
  2103.     if(level==0) progress(NULL, i, node->branch);
  2104.     }
  2105.     if(level==0) progress(NULL, 1, 1);
  2106. }
  2107.  
  2108. /*---------------------------------------------------------------------------*/
  2109.  
  2110. /*
  2111.  *      Function:   Load_Model
  2112.  *
  2113.  *      Purpose:        Load a model into memory.
  2114.  */
  2115. bool load_model(char *filename, MODEL *model)
  2116. {
  2117.     FILE *file;
  2118.     char cookie[16];
  2119.  
  2120.  
  2121.     if(filename==NULL) return(FALSE);
  2122.  
  2123.     file=fopen(filename, "rb");
  2124.  
  2125.     if(file==NULL) {
  2126.     warn("load_model", "Unable to open file `%s'", filename);
  2127.     return(FALSE);
  2128.     }
  2129.  
  2130.  
  2131.     fread(cookie, sizeof(char), strlen(COOKIE), file);
  2132.     if(strncmp(cookie, COOKIE, strlen(COOKIE))!=0) {
  2133.     warn("load_model", "File `%s' is not a MegaHAL brain", filename);
  2134.     goto fail;
  2135.     }
  2136.  
  2137.     fread(&(model->order), sizeof(BYTE1), 1, file);
  2138.     load_tree(file, model->forward);
  2139.     load_tree(file, model->backward);
  2140.     load_dictionary(file, model->dictionary);
  2141.  
  2142.     return(TRUE);
  2143. fail:
  2144.     fclose(file);
  2145.  
  2146.     return(FALSE);
  2147. }
  2148.  
  2149. /*---------------------------------------------------------------------------*/
  2150.  
  2151. /*
  2152.  *    Function:   Make_Words
  2153.  *
  2154.  *    Purpose:    Break a string into an array of words.
  2155.  */
  2156. void make_words(char *input, DICTIONARY *words)
  2157. {
  2158.     int offset=0;
  2159.  
  2160.     /*
  2161.      *      Clear the entries in the dictionary
  2162.      */
  2163.     free_dictionary(words);
  2164.  
  2165.     /*
  2166.      *      If the string is empty then do nothing, for it contains no words.
  2167.      */
  2168.     if(strlen(input)==0) return;
  2169.  
  2170.     /*
  2171.      *      Loop forever.
  2172.      */
  2173.     while(1) {
  2174.  
  2175.     /*
  2176.      *      If the current character is of the same type as the previous
  2177.      *      character, then include it in the word.  Otherwise, terminate
  2178.      *      the current word.
  2179.      */
  2180.     if(boundary(input, offset)) {
  2181.         /*
  2182.          *      Add the word to the dictionary
  2183.          */
  2184.         if(words->entry==NULL)
  2185.         words->entry=(STRING *)malloc((words->size+1)*sizeof(STRING));
  2186.         else
  2187.         words->entry=(STRING *)realloc(words->entry, (words->size+1)*sizeof(STRING));
  2188.  
  2189.         if(words->entry==NULL) {
  2190.         error("make_words", "Unable to reallocate dictionary");
  2191.         return;
  2192.         }
  2193.  
  2194.         words->entry[words->size].length=offset;
  2195.         words->entry[words->size].word=input;
  2196.         words->size+=1;
  2197.  
  2198.         if(offset==(int)strlen(input)) break;
  2199.         input+=offset;
  2200.         offset=0;
  2201.     } else {
  2202.         ++offset;
  2203.     }
  2204.     }
  2205.  
  2206.     /*
  2207.      *      If the last word isn't punctuation, then replace it with a
  2208.      *      full-stop character.
  2209.      */
  2210.     if(isalnum(words->entry[words->size-1].word[0])) {
  2211.     if(words->entry==NULL)
  2212.         words->entry=(STRING *)malloc((words->size+1)*sizeof(STRING));
  2213.     else
  2214.         words->entry=(STRING *)realloc(words->entry, (words->size+1)*sizeof(STRING));
  2215.     if(words->entry==NULL) {
  2216.         error("make_words", "Unable to reallocate dictionary");
  2217.         return;
  2218.     }
  2219.  
  2220.     words->entry[words->size].length=1;
  2221.     words->entry[words->size].word=".";
  2222.     ++words->size;
  2223.     }
  2224.     else if(strchr("!.?", words->entry[words->size-1].word[words->entry[words->size-1].length-1])==NULL) {
  2225.     words->entry[words->size-1].length=1;
  2226.     words->entry[words->size-1].word=".";
  2227.     }
  2228.  
  2229.     return;
  2230. }
  2231.  
  2232. /*---------------------------------------------------------------------------*/
  2233. /*
  2234.  *      Function:   Boundary
  2235.  *
  2236.  *      Purpose:        Return whether or not a word boundary exists in a string
  2237.  *                      at the specified location.
  2238.  */
  2239. bool boundary(char *string, int position)
  2240. {
  2241.     if(position==0)
  2242.     return(FALSE);
  2243.  
  2244.     if(position==(int)strlen(string))
  2245.     return(TRUE);
  2246.  
  2247.     if(
  2248.     (string[position]=='\'')&&
  2249.     (isalpha(string[position-1])!=0)&&
  2250.     (isalpha(string[position+1])!=0)
  2251.     )
  2252.     return(FALSE);
  2253.  
  2254.     if(
  2255.     (position>1)&&
  2256.     (string[position-1]=='\'')&&
  2257.     (isalpha(string[position-2])!=0)&&
  2258.     (isalpha(string[position])!=0)
  2259.     )
  2260.     return(FALSE);
  2261.  
  2262.     if(
  2263.     (isalpha(string[position])!=0)&&
  2264.     (isalpha(string[position-1])==0)
  2265.     )
  2266.     return(TRUE);
  2267.  
  2268.     if(
  2269.     (isalpha(string[position])==0)&&
  2270.     (isalpha(string[position-1])!=0)
  2271.     )
  2272.     return(TRUE);
  2273.  
  2274.     if(isdigit(string[position])!=isdigit(string[position-1]))
  2275.     return(TRUE);
  2276.  
  2277.     return(FALSE);
  2278. }
  2279.  
  2280. /*---------------------------------------------------------------------------*/
  2281. /*
  2282.  *      Function:   Make_Greeting
  2283.  *
  2284.  *      Purpose:        Put some special words into the dictionary so that the
  2285.  *                      program will respond as if to a new judge.
  2286.  */
  2287. void make_greeting(DICTIONARY *words)
  2288. {
  2289.     register unsigned int i;
  2290.  
  2291.     for(i=0; i<words->size; ++i) free(words->entry[i].word);
  2292.     free_dictionary(words);
  2293.     if(grt->size>0) (void)add_word(words, grt->entry[rnd(grt->size)]);
  2294. }
  2295.  
  2296. /*---------------------------------------------------------------------------*/
  2297. /*
  2298.  *    Function:   Generate_Reply
  2299.  *
  2300.  *    Purpose:    Take a string of user input and return a string of output
  2301.  *                which may vaguely be construed as containing a reply to
  2302.  *                whatever is in the input string.
  2303.  */
  2304. char *generate_reply(MODEL *model, DICTIONARY *words)
  2305. {
  2306.     static DICTIONARY *dummy=NULL;
  2307.     DICTIONARY *replywords;
  2308.     DICTIONARY *keywords;
  2309.     float surprise;
  2310.     float max_surprise;
  2311.     char *output;
  2312.     static char *output_none=NULL;
  2313.     int count;
  2314.     int basetime;
  2315.     int timeout = TIMEOUT;
  2316.  
  2317.     /*
  2318.      *      Create an array of keywords from the words in the user's input
  2319.      */
  2320.     keywords=make_keywords(model, words);
  2321.  
  2322.     /*
  2323.      *      Make sure some sort of reply exists
  2324.      */
  2325.     if(output_none==NULL) {
  2326.     output_none=malloc(40);
  2327.     if(output_none!=NULL)
  2328.         strcpy(output_none, "I don't know enough to answer you yet!");
  2329.     }
  2330.     output=output_none;
  2331.     if(dummy == NULL) dummy = new_dictionary();
  2332.     replywords = reply(model, dummy);
  2333.     if(dissimilar(words, replywords) == TRUE) output = make_output(replywords);
  2334.  
  2335.     /*
  2336.      *      Loop for the specified waiting period, generating and evaluating
  2337.      *      replies
  2338.      */
  2339.     max_surprise=(float)-1.0;
  2340.     count=0;
  2341.     basetime=time(NULL);
  2342. /*     progress("Generating reply", 0, 1);  */
  2343.     do {
  2344.     replywords=reply(model, keywords);
  2345.     surprise=evaluate_reply(model, keywords, replywords);
  2346.     ++count;
  2347.     if((surprise>max_surprise)&&(dissimilar(words, replywords)==TRUE)) {
  2348.         max_surprise=surprise;
  2349.         output=make_output(replywords);
  2350.     }
  2351. /*      progress(NULL, (time(NULL)-basetime),timeout); */
  2352.     } while((time(NULL)-basetime)<timeout);
  2353.     progress(NULL, 1, 1);
  2354.  
  2355.     /*
  2356.      *      Return the best answer we generated
  2357.      */
  2358.     return(output);
  2359. }
  2360.  
  2361. /*---------------------------------------------------------------------------*/
  2362.  
  2363. /*
  2364.  *      Function:   Dissimilar
  2365.  *
  2366.  *      Purpose:        Return TRUE or FALSE depending on whether the dictionaries
  2367.  *                      are the same or not.
  2368.  */
  2369. bool dissimilar(DICTIONARY *words1, DICTIONARY *words2)
  2370. {
  2371.     register unsigned int i;
  2372.  
  2373.     if(words1->size!=words2->size) return(TRUE);
  2374.     for(i=0; i<words1->size; ++i)
  2375.     if(wordcmp(words1->entry[i], words2->entry[i])!=0) return(TRUE);
  2376.     return(FALSE);
  2377. }
  2378.  
  2379. /*---------------------------------------------------------------------------*/
  2380.  
  2381. /*
  2382.  *      Function:   Make_Keywords
  2383.  *
  2384.  *      Purpose:        Put all the interesting words from the user's input into
  2385.  *                      a keywords dictionary, which will be used when generating
  2386.  *                      a reply.
  2387.  */
  2388. DICTIONARY *make_keywords(MODEL *model, DICTIONARY *words)
  2389. {
  2390.     static DICTIONARY *keys=NULL;
  2391.     register unsigned int i;
  2392.     register unsigned int j;
  2393.     int c;
  2394.  
  2395.     if(keys==NULL) keys=new_dictionary();
  2396.     for(i=0; i<keys->size; ++i) free(keys->entry[i].word);
  2397.     free_dictionary(keys);
  2398.  
  2399.     for(i=0; i<words->size; ++i) {
  2400.     /*
  2401.      *      Find the symbol ID of the word.  If it doesn't exist in
  2402.      *      the model, or if it begins with a non-alphanumeric
  2403.      *      character, or if it is in the exclusion array, then
  2404.      *      skip over it.
  2405.      */
  2406.     c=0;
  2407.     for(j=0; j<swp->size; ++j)
  2408.         if(wordcmp(swp->from[j], words->entry[i])==0) {
  2409.         add_key(model, keys, swp->to[j]);
  2410.         ++c;
  2411.         }
  2412.     if(c==0) add_key(model, keys, words->entry[i]);
  2413.     }
  2414.  
  2415.     if(keys->size>0) for(i=0; i<words->size; ++i) {
  2416.  
  2417.     c=0;
  2418.     for(j=0; j<swp->size; ++j)
  2419.         if(wordcmp(swp->from[j], words->entry[i])==0) {
  2420.         add_aux(model, keys, swp->to[j]);
  2421.         ++c;
  2422.         }
  2423.     if(c==0) add_aux(model, keys, words->entry[i]);
  2424.     }
  2425.  
  2426.     return(keys);
  2427. }
  2428.  
  2429. /*---------------------------------------------------------------------------*/
  2430.  
  2431. /*
  2432.  *      Function:   Add_Key
  2433.  *
  2434.  *      Purpose:        Add a word to the keyword dictionary.
  2435.  */
  2436. void add_key(MODEL *model, DICTIONARY *keys, STRING word)
  2437. {
  2438.     int symbol;
  2439.  
  2440.     symbol=find_word(model->dictionary, word);
  2441.     if(symbol==0) return;
  2442.     if(isalnum(word.word[0])==0) return;
  2443.     symbol=find_word(ban, word);
  2444.     if(symbol!=0) return;
  2445.     symbol=find_word(aux, word);
  2446.     if(symbol!=0) return;
  2447.  
  2448.     add_word(keys, word);
  2449. }
  2450.  
  2451. /*---------------------------------------------------------------------------*/
  2452.  
  2453. /*
  2454.  *      Function:   Add_Aux
  2455.  *
  2456.  *      Purpose:        Add an auxilliary keyword to the keyword dictionary.
  2457.  */
  2458. void add_aux(MODEL *model, DICTIONARY *keys, STRING word)
  2459. {
  2460.     int symbol;
  2461.  
  2462.     symbol=find_word(model->dictionary, word);
  2463.     if(symbol==0) return;
  2464.     if(isalnum(word.word[0])==0) return;
  2465.     symbol=find_word(aux, word);
  2466.     if(symbol==0) return;
  2467.  
  2468.     add_word(keys, word);
  2469. }
  2470.  
  2471. /*---------------------------------------------------------------------------*/
  2472.  
  2473. /*
  2474.  *      Function:   Reply
  2475.  *
  2476.  *      Purpose:        Generate a dictionary of reply words appropriate to the
  2477.  *                      given dictionary of keywords.
  2478.  */
  2479. DICTIONARY *reply(MODEL *model, DICTIONARY *keys)
  2480. {
  2481.     static DICTIONARY *replies=NULL;
  2482.     register int i;
  2483.     int symbol;
  2484.     bool start=TRUE;
  2485.  
  2486.     if(replies==NULL) replies=new_dictionary();
  2487.     free_dictionary(replies);
  2488.  
  2489.     /*
  2490.      *      Start off by making sure that the model's context is empty.
  2491.      */
  2492.     initialize_context(model);
  2493.     model->context[0]=model->forward;
  2494.     used_key=FALSE;
  2495.  
  2496.     /*
  2497.      *      Generate the reply in the forward direction.
  2498.      */
  2499.     while(TRUE) {
  2500.     /*
  2501.      *      Get a random symbol from the current context.
  2502.      */
  2503.     if(start==TRUE) symbol=seed(model, keys);
  2504.     else symbol=babble(model, keys, replies);
  2505.     if((symbol==0)||(symbol==1)) break;
  2506.     start=FALSE;
  2507.  
  2508.     /*
  2509.      *      Append the symbol to the reply dictionary.
  2510.      */
  2511.     if(replies->entry==NULL)
  2512.         replies->entry=(STRING *)malloc((replies->size+1)*sizeof(STRING));
  2513.     else
  2514.         replies->entry=(STRING *)realloc(replies->entry, (replies->size+1)*sizeof(STRING));
  2515.     if(replies->entry==NULL) {
  2516.         error("reply", "Unable to reallocate dictionary");
  2517.         return(NULL);
  2518.     }
  2519.  
  2520.     replies->entry[replies->size].length=
  2521.         model->dictionary->entry[symbol].length;
  2522.     replies->entry[replies->size].word=
  2523.         model->dictionary->entry[symbol].word;
  2524.     replies->size+=1;
  2525.  
  2526.     /*
  2527.      *      Extend the current context of the model with the current symbol.
  2528.      */
  2529.     update_context(model, symbol);
  2530.     }
  2531.  
  2532.     /*
  2533.      *      Start off by making sure that the model's context is empty.
  2534.      */
  2535.     initialize_context(model);
  2536.     model->context[0]=model->backward;
  2537.  
  2538.     /*
  2539.      *      Re-create the context of the model from the current reply
  2540.      *      dictionary so that we can generate backwards to reach the
  2541.      *      beginning of the string.
  2542.      */
  2543.     if(replies->size>0) for(i=MIN(replies->size-1, model->order); i>=0; --i) {
  2544.     symbol=find_word(model->dictionary, replies->entry[i]);
  2545.     update_context(model, symbol);
  2546.     }
  2547.  
  2548.     /*
  2549.      *      Generate the reply in the backward direction.
  2550.      */
  2551.     while(TRUE) {
  2552.     /*
  2553.      *      Get a random symbol from the current context.
  2554.      */
  2555.     symbol=babble(model, keys, replies);
  2556.     if((symbol==0)||(symbol==1)) break;
  2557.  
  2558.     /*
  2559.      *      Prepend the symbol to the reply dictionary.
  2560.      */
  2561.     if(replies->entry==NULL)
  2562.         replies->entry=(STRING *)malloc((replies->size+1)*sizeof(STRING));
  2563.     else
  2564.         replies->entry=(STRING *)realloc(replies->entry, (replies->size+1)*sizeof(STRING));
  2565.     if(replies->entry==NULL) {
  2566.         error("reply", "Unable to reallocate dictionary");
  2567.         return(NULL);
  2568.     }
  2569.  
  2570.     /*
  2571.      *      Shuffle everything up for the prepend.
  2572.      */
  2573.     for(i=replies->size; i>0; --i) {
  2574.         replies->entry[i].length=replies->entry[i-1].length;
  2575.         replies->entry[i].word=replies->entry[i-1].word;
  2576.     }
  2577.  
  2578.     replies->entry[0].length=model->dictionary->entry[symbol].length;
  2579.     replies->entry[0].word=model->dictionary->entry[symbol].word;
  2580.     replies->size+=1;
  2581.  
  2582.     /*
  2583.      *      Extend the current context of the model with the current symbol.
  2584.      */
  2585.     update_context(model, symbol);
  2586.     }
  2587.  
  2588.     return(replies);
  2589. }
  2590.  
  2591. /*---------------------------------------------------------------------------*/
  2592.  
  2593. /*
  2594.  *      Function:   Evaluate_Reply
  2595.  *
  2596.  *      Purpose:        Measure the average surprise of keywords relative to the
  2597.  *                      language model.
  2598.  */
  2599. float evaluate_reply(MODEL *model, DICTIONARY *keys, DICTIONARY *words)
  2600. {
  2601.     register unsigned int i;
  2602.     register int j;
  2603.     register int k;
  2604.     int symbol;
  2605.     float probability;
  2606.     int count;
  2607.     float entropy=(float)0.0;
  2608.     TREE *node;
  2609.     int num=0;
  2610.  
  2611.     if(words->size<=0) return((float)0.0);
  2612.     initialize_context(model);
  2613.     model->context[0]=model->forward;
  2614.     for(i=0; i<words->size; ++i) {
  2615.     symbol=find_word(model->dictionary, words->entry[i]);
  2616.  
  2617.     if(find_word(keys, words->entry[i])!=0) {
  2618.         probability=(float)0.0;
  2619.         count=0;
  2620.         ++num;
  2621.         for(j=0; j<model->order; ++j) if(model->context[j]!=NULL) {
  2622.  
  2623.         node=find_symbol(model->context[j], symbol);
  2624.         probability+=(float)(node->count)/
  2625.             (float)(model->context[j]->usage);
  2626.         ++count;
  2627.  
  2628.         }
  2629.  
  2630.         if(count>0.0) entropy-=(float)log(probability/(float)count);
  2631.     }
  2632.  
  2633.     update_context(model, symbol);
  2634.     }
  2635.  
  2636.     initialize_context(model);
  2637.     model->context[0]=model->backward;
  2638.     for(k=words->size-1; k>=0; --k) {
  2639.     symbol=find_word(model->dictionary, words->entry[k]);
  2640.  
  2641.     if(find_word(keys, words->entry[k])!=0) {
  2642.         probability=(float)0.0;
  2643.         count=0;
  2644.         ++num;
  2645.         for(j=0; j<model->order; ++j) if(model->context[j]!=NULL) {
  2646.  
  2647.         node=find_symbol(model->context[j], symbol);
  2648.         probability+=(float)(node->count)/
  2649.             (float)(model->context[j]->usage);
  2650.         ++count;
  2651.  
  2652.         }
  2653.  
  2654.         if(count>0.0) entropy-=(float)log(probability/(float)count);
  2655.     }
  2656.  
  2657.     update_context(model, symbol);
  2658.     }
  2659.  
  2660.     if(num>=8) entropy/=(float)sqrt(num-1);
  2661.     if(num>=16) entropy/=(float)num;
  2662.  
  2663.     return(entropy);
  2664. }
  2665.  
  2666. /*---------------------------------------------------------------------------*/
  2667.  
  2668. /*
  2669.  *      Function:   Make_Output
  2670.  *
  2671.  *      Purpose:        Generate a string from the dictionary of reply words.
  2672.  */
  2673. char *make_output(DICTIONARY *words)
  2674. {
  2675.     static char *output=NULL;
  2676.     register unsigned int i;
  2677.     register int j;
  2678.     int length;
  2679.     static char *output_none=NULL;
  2680.  
  2681.     if(output_none==NULL) output_none=malloc(40);
  2682.  
  2683.     if(output==NULL) {
  2684.     output=(char *)malloc(sizeof(char));
  2685.     if(output==NULL) {
  2686.         error("make_output", "Unable to allocate output");
  2687.         return(output_none);
  2688.     }
  2689.     }
  2690.  
  2691.     if(words->size==0) {
  2692.     if(output_none!=NULL)
  2693.         strcpy(output_none, "I am utterly speechless!");
  2694.     return(output_none);
  2695.     }
  2696.  
  2697.     length=1;
  2698.     for(i=0; i<words->size; ++i) length+=words->entry[i].length;
  2699.  
  2700.     output=(char *)realloc(output, sizeof(char)*length);
  2701.     if(output==NULL) {
  2702.     error("make_output", "Unable to reallocate output.");
  2703.     if(output_none!=NULL)
  2704.         strcpy(output_none, "I forgot what I was going to say!");
  2705.     return(output_none);
  2706.     }
  2707.  
  2708.     length=0;
  2709.     for(i=0; i<words->size; ++i)
  2710.     for(j=0; j<words->entry[i].length; ++j)
  2711.         output[length++]=words->entry[i].word[j];
  2712.  
  2713.     output[length]='\0';
  2714.  
  2715.     return(output);
  2716. }
  2717.  
  2718. /*---------------------------------------------------------------------------*/
  2719.  
  2720. /*
  2721.  *      Function:   Babble
  2722.  *
  2723.  *      Purpose:        Return a random symbol from the current context, or a
  2724.  *                      zero symbol identifier if we've reached either the
  2725.  *                      start or end of the sentence.  Select the symbol based
  2726.  *                      on probabilities, favouring keywords.  In all cases,
  2727.  *                      use the longest available context to choose the symbol.
  2728.  */
  2729. int babble(MODEL *model, DICTIONARY *keys, DICTIONARY *words)
  2730. {
  2731.     TREE *node;
  2732.     register int i;
  2733.     int count;
  2734.     int symbol = 0;
  2735.  
  2736.     node = NULL;
  2737.  
  2738.     /*
  2739.      *      Select the longest available context.
  2740.      */
  2741.     for(i=0; i<=model->order; ++i)
  2742.     if(model->context[i]!=NULL)
  2743.         node=model->context[i];
  2744.  
  2745.     if(node->branch==0) return(0);
  2746.  
  2747.     /*
  2748.      *      Choose a symbol at random from this context.
  2749.      */
  2750.     i=rnd(node->branch);
  2751.     count=rnd(node->usage);
  2752.     while(count>=0) {
  2753.     /*
  2754.      *      If the symbol occurs as a keyword, then use it.  Only use an
  2755.      *      auxilliary keyword if a normal keyword has already been used.
  2756.      */
  2757.     symbol=node->tree[i]->symbol;
  2758.  
  2759.     if(
  2760.         (find_word(keys, model->dictionary->entry[symbol])!=0)&&
  2761.         ((used_key==TRUE)||
  2762.          (find_word(aux, model->dictionary->entry[symbol])==0))&&
  2763.         (word_exists(words, model->dictionary->entry[symbol])==FALSE)
  2764.         ) {
  2765.         used_key=TRUE;
  2766.         break;
  2767.     }
  2768.     count-=node->tree[i]->count;
  2769.     i=(i>=(node->branch-1))?0:i+1;
  2770.     }
  2771.  
  2772.     return(symbol);
  2773. }
  2774.  
  2775. /*---------------------------------------------------------------------------*/
  2776.  
  2777. /*
  2778.  *      Function:   Word_Exists
  2779.  *
  2780.  *      Purpose:        A silly brute-force searcher for the reply string.
  2781.  */
  2782. bool word_exists(DICTIONARY *dictionary, STRING word)
  2783. {
  2784.     register unsigned int i;
  2785.  
  2786.     for(i=0; i<dictionary->size; ++i)
  2787.     if(wordcmp(dictionary->entry[i], word)==0)
  2788.         return(TRUE);
  2789.     return(FALSE);
  2790. }
  2791.  
  2792. /*---------------------------------------------------------------------------*/
  2793.  
  2794. /*
  2795.  *      Function:   Seed
  2796.  *
  2797.  *      Purpose:        Seed the reply by guaranteeing that it contains a
  2798.  *                      keyword, if one exists.
  2799.  */
  2800. int seed(MODEL *model, DICTIONARY *keys)
  2801. {
  2802.     register unsigned int i;
  2803.     int symbol;
  2804.     unsigned int stop;
  2805.  
  2806.     /*
  2807.      *      Fix, thanks to Mark Tarrabain
  2808.      */
  2809.     if(model->context[0]->branch==0) symbol=0;
  2810.     else symbol=model->context[0]->tree[rnd(model->context[0]->branch)]->symbol;
  2811.  
  2812.     if(keys->size>0) {
  2813.     i=rnd(keys->size);
  2814.     stop=i;
  2815.     while(TRUE) {
  2816.         if(
  2817.         (find_word(model->dictionary, keys->entry[i])!=0)&&
  2818.         (find_word(aux, keys->entry[i])==0)
  2819.         ) {
  2820.         symbol=find_word(model->dictionary, keys->entry[i]);
  2821.         return(symbol);
  2822.         }
  2823.         ++i;
  2824.         if(i==keys->size) i=0;
  2825.         if(i==stop) return(symbol);
  2826.     }
  2827.     }
  2828.  
  2829.     return(symbol);
  2830. }
  2831.  
  2832. /*---------------------------------------------------------------------------*/
  2833.  
  2834. /*
  2835.  *      Function:   New_Swap
  2836.  *
  2837.  *      Purpose:        Allocate a new swap structure.
  2838.  */
  2839. SWAP *new_swap(void)
  2840. {
  2841.     SWAP *list;
  2842.  
  2843.     list=(SWAP *)malloc(sizeof(SWAP));
  2844.     if(list==NULL) {
  2845.     error("new_swap", "Unable to allocate swap");
  2846.     return(NULL);
  2847.     }
  2848.     list->size=0;
  2849.     list->from=NULL;
  2850.     list->to=NULL;
  2851.  
  2852.     return(list);
  2853. }
  2854.  
  2855. /*---------------------------------------------------------------------------*/
  2856.  
  2857. /*
  2858.  *      Function:   Add_Swap
  2859.  *
  2860.  *      Purpose:        Add a new entry to the swap structure.
  2861.  */
  2862. void add_swap(SWAP *list, char *s, char *d)
  2863. {
  2864.     list->size+=1;
  2865.  
  2866.     if(list->from==NULL) {
  2867.     list->from=(STRING *)malloc(sizeof(STRING));
  2868.     if(list->from==NULL) {
  2869.         error("add_swap", "Unable to allocate list->from");
  2870.         return;
  2871.     }
  2872.     }
  2873.  
  2874.     if(list->to==NULL) {
  2875.     list->to=(STRING *)malloc(sizeof(STRING));
  2876.     if(list->to==NULL) {
  2877.         error("add_swap", "Unable to allocate list->to");
  2878.         return;
  2879.     }
  2880.     }
  2881.  
  2882.     list->from=(STRING *)realloc(list->from, sizeof(STRING)*(list->size));
  2883.     if(list->from==NULL) {
  2884.     error("add_swap", "Unable to reallocate from");
  2885.     return;
  2886.     }
  2887.  
  2888.     list->to=(STRING *)realloc(list->to, sizeof(STRING)*(list->size));
  2889.     if(list->to==NULL) {
  2890.     error("add_swap", "Unable to reallocate to");
  2891.     return;
  2892.     }
  2893.  
  2894.     list->from[list->size-1].length=strlen(s);
  2895.     list->from[list->size-1].word=strdup(s);
  2896.     list->to[list->size-1].length=strlen(d);
  2897.     list->to[list->size-1].word=strdup(d);
  2898. }
  2899.  
  2900. /*---------------------------------------------------------------------------*/
  2901.  
  2902. /*
  2903.  *      Function:   Initialize_Swap
  2904.  *
  2905.  *      Purpose:        Read a swap structure from a file.
  2906.  */
  2907. SWAP *initialize_swap(char *filename)
  2908. {
  2909.     SWAP *list;
  2910.     FILE *file=NULL;
  2911.     char buffer[1024];
  2912.     char *from;
  2913.     char *to;
  2914.  
  2915.     list=new_swap();
  2916.  
  2917.     if(filename==NULL) return(list);
  2918.  
  2919.     file=fopen(filename, "r");
  2920.     if(file==NULL) return(list);
  2921.  
  2922.     while(!feof(file)) {
  2923.  
  2924.     if(fgets(buffer, 1024, file)==NULL) break;
  2925.     if(buffer[0]=='#') continue;
  2926.     from=strtok(buffer, "\t ");
  2927.     to=strtok(NULL, "\t \n#");
  2928.  
  2929.     add_swap(list, from, to);
  2930.     }
  2931.  
  2932.     fclose(file);
  2933.     return(list);
  2934. }
  2935.  
  2936. /*---------------------------------------------------------------------------*/
  2937.  
  2938. void free_swap(SWAP *swap)
  2939. {
  2940.     register int i;
  2941.  
  2942.     if(swap==NULL) return;
  2943.  
  2944.     for(i=0; i<swap->size; ++i) {
  2945.     free_word(swap->from[i]);
  2946.     free_word(swap->to[i]);
  2947.     }
  2948.     free(swap->from);
  2949.     free(swap->to);
  2950.     free(swap);
  2951. }
  2952.  
  2953. /*---------------------------------------------------------------------------*/
  2954.  
  2955. /*
  2956.  *      Function:   Initialize_List
  2957.  *
  2958.  *      Purpose:        Read a dictionary from a file.
  2959.  */
  2960. DICTIONARY *initialize_list(char *filename)
  2961. {
  2962.     DICTIONARY *list;
  2963.     FILE *file=NULL;
  2964.     STRING word;
  2965.     char *string;
  2966.     char buffer[1024];
  2967.  
  2968.     list=new_dictionary();
  2969.  
  2970.     if(filename==NULL) return(list);
  2971.  
  2972.     file=fopen(filename, "r");
  2973.     if(file==NULL) return(list);
  2974.  
  2975.     while(!feof(file)) {
  2976.  
  2977.     if(fgets(buffer, 1024, file)==NULL) break;
  2978.     if(buffer[0]=='#') continue;
  2979.     string=strtok(buffer, "\t \n#");
  2980.  
  2981.     if((string!=NULL)&&(strlen(string)>0)) {
  2982.         word.length=strlen(string);
  2983.         word.word=strdup(buffer);
  2984.         add_word(list, word);
  2985.     }
  2986.     }
  2987.  
  2988.     fclose(file);
  2989.     return(list);
  2990. }
  2991.  
  2992. /*---------------------------------------------------------------------------*/
  2993.  
  2994. /*
  2995.  *      Function:   Delay
  2996.  *
  2997.  *      Purpose:        Display the string to stdout as if it was typed by a human.
  2998.  */
  2999. void delay(char *string)
  3000. {
  3001.     register int i;
  3002.  
  3003.     /*
  3004.      *      Don't simulate typing if the feature is turned off
  3005.      */
  3006.     if(typing_delay==FALSE) {
  3007.     fprintf(stdout, string);
  3008.     return;
  3009.     }
  3010.  
  3011.     /*
  3012.      *      Display the entire string, one character at a time
  3013.      */
  3014.     for(i=0; i<(int)strlen(string)-1; ++i) typein(string[i]);
  3015.     usleep((D_THINK+rnd(V_THINK)-rnd(V_THINK))/2);
  3016.     typein(string[i]);
  3017. }
  3018.  
  3019. /*---------------------------------------------------------------------------*/
  3020.  
  3021. /*
  3022.  *      Function:   Typein
  3023.  *
  3024.  *      Purpose:        Display a character to stdout as if it was typed by a human.
  3025.  */
  3026. void typein(char c)
  3027. {
  3028.     /*
  3029.      *      Standard keyboard delay
  3030.      */
  3031.     usleep(D_KEY+rnd(V_KEY)-rnd(V_KEY));
  3032.     fprintf(stdout, "%c", c);
  3033.     fflush(stdout);
  3034.  
  3035.     /*
  3036.      *      A random thinking delay
  3037.      */
  3038.     if((!isalnum(c))&&((rnd(100))<P_THINK))
  3039.     usleep(D_THINK+rnd(V_THINK)-rnd(V_THINK));
  3040. }
  3041.  
  3042. /*---------------------------------------------------------------------------*/
  3043.  
  3044. /*
  3045.  *      Function:   Ignore
  3046.  *
  3047.  *      Purpose:        Log the occurrence of a signal, but ignore it.
  3048.  */
  3049. void ignore(int sig)
  3050. {
  3051.     if(sig!=0) warn("ignore", "MegaHAL received signal %d", sig);
  3052.  
  3053. #if !defined(DOS)
  3054.     //    signal(SIGINT, saveandexit);
  3055.     //    signal(SIGILL, die);
  3056.     //    signal(SIGSEGV, die);
  3057. #endif
  3058.     //    signal(SIGFPE, die);
  3059. }
  3060.  
  3061.  
  3062. /*---------------------------------------------------------------------------*/
  3063.  
  3064. /*
  3065.  *      Function:   Die
  3066.  *
  3067.  *      Purpose:        Log the occurrence of a signal, and exit.
  3068.  */
  3069. void die(int sig)
  3070. {
  3071.     error("die", "MegaHAL received signal %d", sig);
  3072.     exithal();
  3073. }
  3074.  
  3075. /*---------------------------------------------------------------------------*/
  3076.  
  3077. /*
  3078.  *      Function:   Rnd
  3079.  *
  3080.  *      Purpose:        Return a random integer between 0 and range-1.
  3081.  */
  3082. int rnd(int range)
  3083. {
  3084.     static bool flag=FALSE;
  3085.  
  3086.     if(flag==FALSE) {
  3087. #if defined(__mac_os) || defined(DOS)
  3088.     srand(time(NULL));
  3089. #else
  3090.     srand48(time(NULL));
  3091. #endif
  3092.     }
  3093.     flag=TRUE;
  3094. #if defined(__mac_os) || defined(DOS)
  3095.     return(rand()%range);
  3096. #else
  3097.     return(floor(drand48()*(double)(range)));
  3098. #endif
  3099. }
  3100.  
  3101. /*---------------------------------------------------------------------------*/
  3102.  
  3103. /*
  3104.  *      Function:   Usleep
  3105.  *
  3106.  *      Purpose:        Simulate the Un*x function usleep.  Necessary because
  3107.  *                      Microsoft provide no similar function.  Performed via
  3108.  *                      a busy loop, which unnecessarily chews up the CPU.
  3109.  *                      But Windows '95 isn't properly multitasking anyway, so
  3110.  *                      no-one will notice.  Modified from a real Microsoft
  3111.  *                      example, believe it or not!
  3112.  */
  3113. #if defined(DOS) || defined(__mac_os)
  3114. void usleep(int period)
  3115. {
  3116.     clock_t goal;
  3117.  
  3118.     goal=(clock_t)(period*CLOCKS_PER_SEC)/(clock_t)1000000+clock();
  3119.     while(goal>clock());
  3120. }
  3121. #endif
  3122.  
  3123. /*---------------------------------------------------------------------------*/
  3124.  
  3125. /*
  3126.  *      Function:   Strdup
  3127.  *
  3128.  *      Purpose:        Provide the strdup() function for Macintosh.
  3129.  */
  3130. #ifdef __mac_os
  3131. char *strdup(const char *str)
  3132. {
  3133.     char *rval=(char *)malloc(strlen(str)+1);
  3134.  
  3135.     if(rval!=NULL) strcpy(rval, str);
  3136.  
  3137.     return(rval);
  3138. }
  3139. #endif
  3140.  
  3141. /*---------------------------------------------------------------------------*/
  3142.  
  3143. /*
  3144.  *      Function:   Initialize_Speech
  3145.  *
  3146.  *      Purpose:        Initialize speech output.
  3147.  */
  3148. #ifdef __mac_os
  3149. bool initialize_speech(void)
  3150. {
  3151.     bool speechExists = false;
  3152.     long response;
  3153.     OSErr err;
  3154.  
  3155.     err = Gestalt(gestaltSpeechAttr, &response);
  3156.  
  3157.     if(!err) {
  3158.     if(response & (1L << gestaltSpeechMgrPresent)) {
  3159.         speechExists = true;
  3160.     }
  3161.     }
  3162.     return speechExists;
  3163. }
  3164. #endif
  3165.  
  3166. /*---------------------------------------------------------------------------*/
  3167.  
  3168. /*
  3169.  *      Function:   changevoice
  3170.  *
  3171.  *      Purpose:        change voice of speech output.
  3172.  */
  3173. void changevoice(DICTIONARY* words, int position)
  3174. {
  3175. #ifdef __mac_os
  3176.     register int i, index;
  3177.     STRING word={ 1, "#" };
  3178.     char buffer[80];
  3179.     VoiceSpec voiceSpec;
  3180.     VoiceDescription info;
  3181.     short count, voiceCount;
  3182.     unsigned char* temp;
  3183.     OSErr err;
  3184.     /*
  3185.      *      If there is less than 4 words, no voice specified.
  3186.      */
  3187.     if(words->size<=4) return;
  3188.  
  3189.     for(i=0; i<words->size-4; ++i)
  3190.     if(wordcmp(word, words->entry[i])==0) {
  3191.  
  3192.         err = CountVoices(&voiceCount);
  3193.         if (!err && voiceCount) {
  3194.         for (count = 1; count <= voiceCount; count++) {
  3195.             err = GetIndVoice(count, &voiceSpec);
  3196.             if (err) continue;
  3197.             err = GetVoiceDescription(&voiceSpec, &info,
  3198.                           sizeof(VoiceDescription));
  3199.             if (err) continue;
  3200.  
  3201.  
  3202.             for (temp= info.name; *temp; temp++) {
  3203.             if (*temp == ' ')
  3204.                 *temp = '_';
  3205.             }
  3206.  
  3207.             /*
  3208.              *      skip command and get voice name
  3209.              */
  3210.             index = i + 3;
  3211.             strcpy(buffer, words->entry[index].word);
  3212.             c2pstr(buffer);
  3213.             // compare ignoring case
  3214.             if (EqualString((StringPtr)buffer, info.name, false, false)) {
  3215.             if (gSpeechChannel) {
  3216.                 StopSpeech(gSpeechChannel);
  3217.                 DisposeSpeechChannel(gSpeechChannel);
  3218.                 gSpeechChannel = nil;
  3219.             }
  3220.             err = NewSpeechChannel(&voiceSpec, &gSpeechChannel);
  3221.             if (!err) {
  3222.                 p2cstr((StringPtr)buffer);
  3223.                 printf("Now using %s voice\n", buffer);
  3224.                 c2pstr(buffer);
  3225.                 err = SpeakText(gSpeechChannel, &buffer[1], buffer[0]);
  3226.             }
  3227.             }
  3228.         }
  3229.         }
  3230.     }
  3231. #endif
  3232. }
  3233.  
  3234. /*---------------------------------------------------------------------------*/
  3235.  
  3236. /*
  3237.  *      Function:   listvoices
  3238.  *
  3239.  *      Purpose:        Display the names of voices for speech output.
  3240.  */
  3241. void listvoices(void)
  3242. {
  3243. #ifdef __mac_os
  3244.     VoiceSpec voiceSpec;
  3245.     VoiceDescription info;
  3246.     short count, voiceCount;
  3247.     unsigned char* temp;
  3248.     OSErr err;
  3249.  
  3250.     if(gSpeechExists) {
  3251.     err = CountVoices(&voiceCount);
  3252.     if (!err && voiceCount) {
  3253.         for (count = 1; count <= voiceCount; count++) {
  3254.         err = GetIndVoice(count, &voiceSpec);
  3255.         if (err) continue;
  3256.  
  3257.         err = GetVoiceDescription(&voiceSpec, &info,
  3258.                       sizeof(VoiceDescription));
  3259.         if (err) continue;
  3260.  
  3261.         p2cstr(info.name);
  3262.         for (temp= info.name; *temp; temp++)
  3263.             if (*temp == ' ')
  3264.             *temp = '_';
  3265.         printf("%s\n",info.name);
  3266.         }
  3267.     }
  3268.     }
  3269. #endif
  3270. }
  3271.  
  3272. /*---------------------------------------------------------------------------*/
  3273.  
  3274. /*
  3275.  *      Function:   Speak
  3276.  */
  3277. void speak(char *output)
  3278. {
  3279.     if(speech==FALSE) return;
  3280. #ifdef __mac_os
  3281.     if(gSpeechExists) {
  3282.     OSErr err;
  3283.  
  3284.     if (gSpeechChannel)
  3285.         err = SpeakText(gSpeechChannel, output, strlen(output));
  3286.     else {
  3287.         c2pstr(output);
  3288.         SpeakString((StringPtr)output);
  3289.         p2cstr((StringPtr)output);
  3290.     }
  3291.     }
  3292. #endif
  3293. }
  3294.  
  3295. /*---------------------------------------------------------------------------*/
  3296.  
  3297. /*
  3298.  *      Function:   Progress
  3299.  *
  3300.  *      Purpose:        Display a progress indicator as a percentage.
  3301.  */
  3302. bool progress(char *message, int done, int total)
  3303. {
  3304.     static int last=0;
  3305.     static bool first=FALSE;
  3306.  
  3307.     /*
  3308.      *    We have already hit 100%, and a newline has been printed, so nothing
  3309.      *    needs to be done.
  3310.      */
  3311.     if((done*100/total==100)&&(first==FALSE)) return(TRUE);
  3312.  
  3313.     /*
  3314.      *    Nothing has changed since the last time this function was called,
  3315.      *    so do nothing, unless it's the first time!
  3316.      */
  3317.     if(done*100/total==last) {
  3318.     if((done==0)&&(first==FALSE)) {
  3319.         fprintf(stderr, "%s: %3d%%", message, done*100/total);
  3320.         first=TRUE;
  3321.     }
  3322.     return(TRUE);
  3323.     }
  3324.  
  3325.     /*
  3326.      *    Erase what we printed last time, and print the new percentage.
  3327.      */
  3328.     last=done*100/total;
  3329.  
  3330.     if(done>0) fprintf(stderr, "%c%c%c%c", 8, 8, 8, 8);
  3331.     fprintf(stderr, "%3d%%", done*100/total);
  3332.  
  3333.     /*
  3334.      *    We have hit 100%, so reset static variables and print a newline.
  3335.      */
  3336.     if(last==100) {
  3337.     first=FALSE;
  3338.     last=0;
  3339.     fprintf(stderr, "\n");
  3340.     }
  3341.  
  3342.     return(TRUE);
  3343. }
  3344.  
  3345. /*---------------------------------------------------------------------------*/
  3346.  
  3347. void help(void)
  3348. {
  3349.     unsigned int j;
  3350.  
  3351.     for(j=0; j<COMMAND_SIZE; ++j) {
  3352.     printf("#%-7s: %s\n", command[j].word.word, command[j].helpstring);
  3353.     }
  3354. }
  3355.  
  3356. /*---------------------------------------------------------------------------*/
  3357.  
  3358. void load_personality(MODEL **model)
  3359. {
  3360.     FILE *file;
  3361.     static char *filename=NULL;
  3362.  
  3363.     if(filename==NULL) filename=(char *)malloc(sizeof(char)*1);
  3364.  
  3365.     /*
  3366.      *      Allocate memory for the filename
  3367.      */
  3368.     filename=(char *)realloc(filename,
  3369.                  sizeof(char)*(strlen(directory)+strlen(SEP)+12));
  3370.     if(filename==NULL) error("load_personality","Unable to allocate filename");
  3371.  
  3372.     /*
  3373.      *      Check to see if the brain exists
  3374.      */
  3375.  
  3376.     if(strcmp(directory, last)!=0) {
  3377.     sprintf(filename, "%s%smegahal.brn", directory, SEP);
  3378.     file=fopen(filename, "r");
  3379.     if(file==NULL) {
  3380.         sprintf(filename, "%s%smegahal.trn", directory, SEP);
  3381.         file=fopen(filename, "r");
  3382.         if(file==NULL) {
  3383.         fprintf(stdout, "Unable to change MegaHAL personality to \"%s\".\n"
  3384.             "Reverting to MegaHAL personality \"%s\".\n", directory, last);
  3385.         free(directory);
  3386.         directory=strdup(last);
  3387.         return;
  3388.         }
  3389.     }
  3390.     fclose(file);
  3391.     fprintf(stdout, "Changing to MegaHAL personality \"%s\".\n", directory);
  3392.     }
  3393.  
  3394.     /*
  3395.      *      Free the current personality
  3396.      */
  3397.     free_model(*model);
  3398.     free_words(ban);
  3399.     free_dictionary(ban);
  3400.     free_words(aux);
  3401.     free_dictionary(aux);
  3402.     free_words(grt);
  3403.     free_dictionary(grt);
  3404.     free_swap(swp);
  3405.  
  3406.     /*
  3407.      *      Create a language model.
  3408.      */
  3409.     *model=new_model(order);
  3410.  
  3411.     /*
  3412.      *      Train the model on a text if one exists
  3413.      */
  3414.  
  3415.     sprintf(filename, "%s%smegahal.brn", directory, SEP);
  3416.     if(load_model(filename, *model)==FALSE) {
  3417.     sprintf(filename, "%s%smegahal.trn", directory, SEP);
  3418.     train(*model, filename);
  3419.     }
  3420.  
  3421.     /*
  3422.      *      Read a dictionary containing banned keywords, auxiliary keywords,
  3423.      *      greeting keywords and swap keywords
  3424.      */
  3425.     sprintf(filename, "%s%smegahal.ban", directory, SEP);
  3426.     ban=initialize_list(filename);
  3427.     sprintf(filename, "%s%smegahal.aux", directory, SEP);
  3428.     aux=initialize_list(filename);
  3429.     sprintf(filename, "%s%smegahal.grt", directory, SEP);
  3430.     grt=initialize_list(filename);
  3431.     sprintf(filename, "%s%smegahal.swp", directory, SEP);
  3432.     swp=initialize_swap(filename);
  3433. }
  3434.  
  3435. /*---------------------------------------------------------------------------*/
  3436.  
  3437. void change_personality(DICTIONARY *command, unsigned int position, MODEL **model)
  3438. {
  3439.  
  3440.     if(directory == NULL) {
  3441.     directory = (char *)malloc(sizeof(char)*(strlen(DEFAULT)+1));
  3442.     if(directory == NULL) {
  3443.         error("change_personality", "Unable to allocate directory");
  3444.     } else {
  3445.         strcpy(directory, DEFAULT);
  3446.     }
  3447.     }
  3448.  
  3449.     if(last == NULL) {
  3450.     last = strdup(directory);
  3451.     }
  3452.  
  3453.     if((command == NULL)||((position+2)>=command->size)) {
  3454.     /* no dir set, so we leave it to whatever was set above */
  3455.     } else {
  3456.         directory=(char *)realloc(directory,
  3457.                                   sizeof(char)*(command->entry[position+2].length+1));
  3458.         if(directory == NULL)
  3459.             error("change_personality", "Unable to allocate directory");
  3460.         strncpy(directory, command->entry[position+2].word,
  3461.                 command->entry[position+2].length);
  3462.         directory[command->entry[position+2].length]='\0';
  3463.     }
  3464.  
  3465.     load_personality(model);
  3466. }
  3467.  
  3468. /*---------------------------------------------------------------------------*/
  3469.  
  3470. void free_words(DICTIONARY *words)
  3471. {
  3472.     register unsigned int i;
  3473.  
  3474.     if(words == NULL) return;
  3475.  
  3476.     if(words->entry != NULL)
  3477.     for(i=0; i<words->size; ++i) free_word(words->entry[i]);
  3478. }
  3479.  
  3480. /*---------------------------------------------------------------------------*/
  3481.  
  3482. void free_word(STRING word)
  3483. {
  3484.     free(word.word);
  3485. }
  3486.  
  3487. /*===========================================================================*/
  3488.  
  3489. /*
  3490.  *      $Log: megahal.c,v $
  3491.  *      Revision 1.10  2004/02/23 11:12:29  lfousse
  3492.  *      Changed default working directory and added options to change it.
  3493.  *     
  3494.  *      Revision 1.9  2004/01/13 10:59:20  lfousse
  3495.  *      * Applied code cleaning already shipped with the debian package.
  3496.  *      * Removed pure debian stuff.
  3497.  *      * Added lacking setup.py file for python module.
  3498.  *     
  3499.  *      Revision 1.8  2003/08/26 12:49:16  lfousse
  3500.  *      * Added the perl interface
  3501.  *      * cleaned up the python interface a bit (but this
  3502.  *        still need some work by a python "expert")
  3503.  *      * Added a learn_no_reply function.
  3504.  *     
  3505.  *      Revision 1.7  2003/08/18 21:45:23  lfousse
  3506.  *      Added megahal_learn_no_reply function for quick learning, and
  3507.  *      corresponding python interface.
  3508.  *     
  3509.  *      Revision 1.6  2002/10/16 04:32:53  davidw
  3510.  *      * megahal.c (change_personality): [ 541667 ] Added patch from Andrew
  3511.  *        Burke to rework logic in change_personality.
  3512.  *     
  3513.  *      * megahal.c: Trailing white space cleanup.
  3514.  *     
  3515.  *      * python-interface.c: [ 546397 ] Change python include path to a more
  3516.  *        recent version.  This should really be fixed by using some of
  3517.  *        Python's build automation tools.
  3518.  *     
  3519.  *      Revision 1.5  2000/11/08 11:07:11  davidw
  3520.  *      Moved README to docs directory.
  3521.  *
  3522.  *      Changes to debian files.
  3523.  *
  3524.  *      Revision 1.4  2000/09/07 21:51:12  davidw
  3525.  *      Created some library functions that I think are workable, and moved
  3526.  *      everything else into megahal.c as static variables/functions.
  3527.  *
  3528.  *      Revision 1.3  2000/09/07 11:43:43  davidw
  3529.  *      Started hacking:
  3530.  *
  3531.  *      Reduced makefile targets, eliminating non-Linux OS's.  There should be
  3532.  *      a cleaner way to do this.
  3533.  *
  3534.  *      Added Tcl and Python C level interfaces.
  3535.  *
  3536.  *      Revision 1.23  1998/05/19 03:02:02  hutch
  3537.  *      Removed a small malloc() bug, and added a progress display for
  3538.  *      generate_reply().
  3539.  *
  3540.  *      Revision 1.22  1998/04/24 03:47:03  hutch
  3541.  *      Quick bug fix to get sunos version to work.
  3542.  *
  3543.  *      Revision 1.21  1998/04/24 03:39:51  hutch
  3544.  *      Added the BRAIN command, to allow user to change MegaHAL personalities
  3545.  *      on the fly.
  3546.  *
  3547.  *      Revision 1.20  1998/04/22 07:12:37  hutch
  3548.  *      A few small changes to get the DOS version to compile.
  3549.  *
  3550.  *      Revision 1.19  1998/04/21 10:10:56  hutch
  3551.  *      Fixed a few little errors.
  3552.  *
  3553.  *      Revision 1.18  1998/04/06 08:02:01  hutch
  3554.  *      Added debugging stuff, courtesy of Paul Baxter.
  3555.  *
  3556.  *      Revision 1.17  1998/04/02 01:34:20  hutch
  3557.  *      Added the help function and fixed a few errors.
  3558.  *
  3559.  *      Revision 1.16  1998/04/01 05:42:57  hutch
  3560.  *      Incorporated Mac code, including speech synthesis, and attempted
  3561.  *      to tidy up the code for multi-platform support.
  3562.  *
  3563.  *      Revision 1.15  1998/03/27 03:43:15  hutch
  3564.  *      Added AMIGA specific changes, thanks to Dag Agren.
  3565.  *
  3566.  *      Revision 1.14  1998/02/20 06:40:13  hutch
  3567.  *      Tidied up transcript file format.
  3568.  *
  3569.  *      Revision 1.13  1998/02/20 06:26:19  hutch
  3570.  *      Fixed random number generator and Seed() function (thanks to Mark
  3571.  *      Tarrabain), removed redundant code left over from the Loebner entry,
  3572.  *      prettied things up a little and destroyed several causes of memory
  3573.  *      leakage (although probably not all).
  3574.  *
  3575.  *      Revision 1.12  1998/02/04 02:55:11  hutch
  3576.  *      Fixed up memory allocation error which caused SunOS versions to crash.
  3577.  *
  3578.  *      Revision 1.11  1998/01/22 03:16:30  hutch
  3579.  *      Fixed several memory leaks, and the frustrating bug in the
  3580.  *      Write_Input routine.
  3581.  *
  3582.  *      Revision 1.10  1998/01/19 06:44:36  hutch
  3583.  *      Fixed MegaHAL to compile under Linux with a small patch credited
  3584.  *      to Joey Hess (joey@kitenet.net).  MegaHAL may now be included as
  3585.  *      part of the Debian Linux distribution.
  3586.  *
  3587.  *      Revision 1.9  1998/01/19 06:37:32  hutch
  3588.  *      Fixed a minor bug with end-of-sentence punctuation.
  3589.  *
  3590.  *      Revision 1.8  1997/12/24 03:17:01  hutch
  3591.  *      More bug fixes, and hopefully the final contest version!
  3592.  *
  3593.  *      Revision 1.7  1997/12/22  13:18:09  hutch
  3594.  *      A few more bug fixes, and non-repeating implemented.
  3595.  *
  3596.  *      Revision 1.6  1997/12/22 04:27:04  hutch
  3597.  *      A few minor bug fixes.
  3598.  *
  3599.  *      Revision 1.5  1997/12/15 04:35:59  hutch
  3600.  *      Final Loebner version!
  3601.  *
  3602.  *      Revision 1.4  1997/12/11 05:45:29  hutch
  3603.  *      The almost finished version.
  3604.  *
  3605.  *      Revision 1.3  1997/12/10 09:08:09  hutch
  3606.  *      Now Loebner complient (tm).
  3607.  *
  3608.  *      Revision 1.2  1997/12/08 06:22:32  hutch
  3609.  *      Tidied up.
  3610.  *
  3611.  *      Revision 1.1  1997/12/05  07:11:44  hutch
  3612.  *      Initial revision (lots of files were merged into one, RCS re-started)
  3613.  *
  3614.  *      Revision 1.7  1997/12/04 07:07:13  hutch
  3615.  *      Added load and save functions, and tidied up some code.
  3616.  *
  3617.  *      Revision 1.6  1997/12/02 08:34:47  hutch
  3618.  *      Added the ban, aux and swp functions.
  3619.  *
  3620.  *      Revision 1.5  1997/12/02 06:03:04  hutch
  3621.  *      Updated to use a special terminating symbol, and to store only
  3622.  *      branches of maximum depth, as they are the only ones used in
  3623.  *      the reply.
  3624.  *
  3625.  *      Revision 1.4  1997/10/28 09:23:12  hutch
  3626.  *      MegaHAL is babbling nicely, but without keywords.
  3627.  *
  3628.  *      Revision 1.3  1997/10/15  09:04:03  hutch
  3629.  *      MegaHAL can parrot back whatever the user says.
  3630.  *
  3631.  *      Revision 1.2  1997/07/21 04:03:28  hutch
  3632.  *      Fully working.
  3633.  *
  3634.  *      Revision 1.1  1997/07/15 01:55:25  hutch
  3635.  *      Initial revision.
  3636.  */
  3637.  
  3638. /*===========================================================================*/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement