File Coverage

libmegahal.c
Criterion Covered Total %
statement 639 1068 59.8
branch 302 580 52.0
condition n/a
subroutine n/a
pod n/a
total 941 1648 57.1


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