/*  agil.c-- initialization and  the main execution loop  */
/* Copyright 1996,97    Robert Masenten                   */
/*                                                        */
/* This is part of the source for AGiliTy: the (Mostly)   */
/*    Universal AGT Interpreter */

/* Overview of the source for AGiliTy:
 agil.c
  --initialization
  --tokeniser

 parser.c:
  --parser
  --disambiguation
  --menuing system

 exec.c 
  --verb execution wrapper
  --standard utilities for running actions

 runverb.c:
  --standard verbs
 
 metacommand.c
  --metacommand execution 

 debugcmd.c
  --Routines to execute debugging verb

 savegame.c
  --save and restore 
  --undo and restart 

 rmem.c
  --general utilities
  --memory management
  --buffered file i/o

 interface.c
  --high level input/output
  --general utilities

 agtdbg.c
  --Metacommand debugging output

 os_<name>.c
  --low level input/output
  --other platform dependent code
*/


#include <ctype.h>
#include "agtread.h"
#include "uagt.h"

/*   The following are AGT 'purity' flags; they turn off features of */
/* my interpreter that are not fully consistent with the original AGT */
/* and so could break some games. Some of these are trivial improvements; */
/* some are more radical and should be used with caution. Several are */
/* only useful if a game was designed with them in mind. */
/*   In all cases, setting the flag to 1 more closely follows the */
/* behavior of the original interpreters */
/* WARNING: Most of these haven't been tested in the non-default state. */
/* Most of these will eventually become variables settable by the user */
/*   or from a (possibly game-specific) configuration file */

bool PURE_INPUT=1;  /* Is the input line bold? */

bool PURE_TONE=0;  /* Is low level sound enabled? */


/*-------------------------------------------------------------------*/
/*  Misc. things to support the tokenizer and the dictionry.         */
/*-------------------------------------------------------------------*/

/* The following should not be changed without also changing the
   wtype enum statement in uagt.h to match */
static const char* ext_voc[]=     
{"the","my","a","an",  /* These 4 are ignored in canonical AGT */
    "then",".",";","and", "," , "its", "all","undo",
    "pick","go","exits","talk","take","door","again","but","except",
   "scene","everything","listexit","listexits","close",
   "verb","noun","adjective","prep","object","name","step",
   "he","she","it","they","him","her","them","is","are","oops",
 /* Everything between 'in' and 'about' should be a preposition */
   "in","out", "into","at","to","across","inside","with","near","for",
   "of","behind", "beside", "on", "off", "under", "from","through",
   "toward", "towards", "between", "around", "upon", "thru", 
   "by", "over", "up", "down", 
   "about"};
 /* 'about' must be the last element of this list */

#define w_ispunct(c) ((c)==',' || (c)==';' || ( (c)=='.' && !PURE_DOT ))





/*-------------------------------------------------------------------*/
/* Routines to read in and use various auxilary files.               */
/*   (.TTL, .INS, .VOC, .CFG)                                        */
/*-------------------------------------------------------------------*/

static bool emptyline(unsigned char *s)
/* Check if s consists only of white space and control characters */
{
  unsigned char *p;
 
  for(p=s;*p!=0;p++)
    if (!isspace(*p) && *p>26) return 0;
  return 1;
}

static void print_title(char *fname)
{
  int height;
  signed char center_mode; /* Center title? */
  descr_line *buff;
  char *s;
  bool skip_line; /* Skip first line: it has COLORS */

  if (agx_file)
    buff=read_descr(title_ptr.start,title_ptr.size);
  else
    buff=read_ttl(fname);

  if (buff==NULL) {
    writeln("");
    if (aver<AGX00)
      writeln("This game was created with Malmberg and Welch's Adventure "
	      "Game Toolkit; it is being executed by");
    else writeln("This game is being executed by ");
    writeln("");
  } else {
    if (buff[0]!=NULL && strncmp(buff[0],"COLORS",6)==0) {
      /* Do screen colors */
      skip_line=1;
    } else skip_line=0;
    /* Compute height and count the number of non-empty lines
       starting with spaces. We use height as a loop variable
       and center_mode to store the count temporarily. */
    center_mode=0;
    for(height=skip_line;buff[height]!=NULL;height++)
      if (!emptyline((uchar*)buff[height])) 
	if (isspace(buff[height][0])) center_mode++;
	else center_mode--;    
    
    if (box_title || aver==AGTCOS) center_mode=TB_CENTER;
    else /* includes aver==AGT135 */
      if (center_mode<=0) center_mode=TB_CENTER;
      else center_mode=TB_NOCENT;
    
    agt_textcolor(-1);
    agt_clrscr();
    textbox(buff+skip_line,height-skip_line,center_mode|TB_BOLD|TB_TTL|
	    ( box_title ? TB_BORDER : 0));
    agt_textcolor(-2); /* Bold off */
  }  /* End printing of title proper */

  if (agx_file)
    free_descr(buff);
  else 
    free_ttl(buff);

  agt_textcolor(7);
  agt_center(1);
  if (buff!=NULL) {
    if (aver<AGX00) 
      writeln("[Created with Malmberg and Welch's Adventure Game Toolkit]");
    if (height<=screen_height-9) writeln("");
    writeln("This game is being executed by");
  }
  agt_textcolor(-1);
  s=rmalloc(80);
  sprintf(s,"AGiliTy: "
	   "The (Mostly) Universal AGT Interpreter  %s",version_str);
  writeln(s);
  rfree(s);
  agt_textcolor(-2);
  writeln("Copyright 1996,97 by Robert Masenten"); 
  writeln(portstr);
  if (height<=screen_height-10) writeln("");
  agt_center(0);
}



/* .INS reading routines -------------------------------------- */

void print_instructions(char *fname)
{
  char *buffer;
  uchar *s;

  writeln("INSTRUCTIONS:");
  if (open_ins_file(fname,1)) {   /* Instruction file exists */   
    while ( NULL!=(buffer=read_ins_line()) ) {
      for(s=(uchar*)buffer;*s!=0;s++) *s=trans_ascii[*s];
      writeln(buffer);
    }
  }
  writeln("");
}

/* Routines to build the verb menu from the .VOC information */

static void build_verbmenu(void)
{
  int i, n;
  char *p,*d;
  
  verbmenu=rmalloc(vm_size*sizeof(menuentry));
  vm_width=0;
  for(i=0;i<vm_size;i++) {
    p=verbmenu[i]; d=dict[verbinfo[i].verb];
    n=0;
    for(;n<MENU_WIDTH && *d!=0;p++,d++,n++) *p=*d;
    if (verbinfo[i].prep!=0 && n+1<MENU_WIDTH) {
      *p++=' ';    
      d=dict[verbinfo[i].prep];
      *p++=toupper(*d++);
      for(;n<MENU_WIDTH && *d!=0;p++,d++,n++) *p=*d;    
    } 
    verbmenu[i][0]=toupper(verbmenu[i][0]);
    *p=0;
    if (n>vm_width) vm_width=n;
  }
}

/* .CFG reading routines  -------------------------------------------- */

#define opt(s) (strcasecmp(optstr[0],s)==0)

/* These are the interpreter specific options; this is called
   from cfg_option in agtdata.c. */
void agil_option(int optnum,char *optstr[], bool setflag, bool lastpass)
{
  if (opt("ibm_char")) fix_ascii_flag=!setflag;
  else if (!lastpass) return; /* On the first pass through the game specific
				 file, we ignore all but the above options */
  else if (opt("tone")) PURE_TONE=setflag;
  else if (opt("input_bold")) PURE_INPUT=setflag;
  else if (!agt_option(optnum,optstr,setflag)) /* Platform specific options */
    rprintf("Invalid option %s\n",optstr[0]);
}

/*-------------------------------------------------------------------*/
/* Tokeniser: Split input into words and look them up in dictionary  */
/*-------------------------------------------------------------------*/

static bool noise_word(word w)
{
  if (w==ext_code[wthe] || w==ext_code[wa] || w==ext_code[wan]) return 1;
  if (w==ext_code[wmy]) return 1;
  if (aver>=AGT18 && aver<=AGT18MAX && w==ext_code[wis]) return 1;
  return 0;
}

static void tokenise(char *buff)
/* Convert input string into vocabulary codes */
/* 0 here denotes an unrecognized word and -1 marks the end. */
{
  int ip,j, k;

  j=0;ip=0;
  k=0;  /* k is the character pointer */
  for(k=0;;k++) {
    if (buff[k]!=0 && !isspace(buff[k]) && !w_ispunct(buff[k])) {
	if (j<WORD_LENG-1) 
	  in_text[ip][j++]=buff[k];
    } else if (j>0)  /* End of word: add it to input */
      {
	in_text[ip][j]=0;
	input[ip]=search_dict(in_text[ip]);
	if (input[ip]==-1) input[ip]=0;
        if (!noise_word(input[ip])) ip+=1; 
	     /* i.e. if not one of the four ignored words, advance */
	j=0;
      } /* If j=0 and not punct, then no new word; just skip the whitespace */
    if (w_ispunct(buff[k]) ) {
      in_text[ip][0]=buff[k];in_text[ip][1]=0;
      input[ip]=search_dict(in_text[ip]);
      if (input[ip]==-1) input[ip]=0;
      j=0;ip++;    
    }
    if (ip>=MAXINPUT-1) {
      writeln("Too many words in input; ignoring rest of line.");
      break;
    }
    if (buff[k]==0) break;
  } 
  input[ip]=-1;
  in_text[ip][0]=0;
}



/*-------------------------------------------------------------------*/
/* Main game loop: Get player input and call the parser.             */
/*-------------------------------------------------------------------*/

static void game_end(void)
{
  bool done_flag;
  char *s;

  if (winflag || deadflag) {
    writeln("");writeln("");agt_center(1);
    if (winflag) 
      if (irun_mode) writeln(" ***** I have won! *****");
      else writeln(" ***** You have won! ***** ");
    if (deadflag)
      if (irun_mode) writeln(" ***** I have died! *****");
      else writeln(" ***** You have died! ***** " );
    writeln("");writeln("");agt_center(0);
  }
  if (deadflag && !endflag) 
    if (curr_lives>1) { /* Ressurection code */
      if (curr_lives==max_lives)
	sysmsg(151,"Hmmm.... so $you$'ve gotten $your$self killed. "
		"Would you like me to try a ressurection?");
      else sysmsg(152,"<Sigh>  $You$'ve died *again*."
		  "Would you like me to try another resurrection?");
      if (yesno("? ")) {  /* Now do ressurection */
	curr_lives--;
	quitflag=deadflag=0;
	sysmsg(154,"$You$ emerge coughing from a cloud of orange smoke.");
	writeln("");
	loc=ressurect_room-first_room;
	newlife_flag=1;
	set_statline();
	newroom();
	return; 
      } else writeln("As you wish...");
    }
    else if (max_lives>1) 
      sysmsg(153,"$You$'ve used up all of $your$ lives.");
  writeln("");
  print_score();
  writeln("");
  done_flag=quitflag;  /* If player has QUIT, don't ask again */
  while (!done_flag) {
    writestr("Would you like to ");
    if (restart_state!=NULL) writestr("restart, ");
    writestr("restore");
    if (undo_state!=NULL && can_undo)
      writestr(", undo,");
    else if (restart_state!=NULL) writestr(",");
    writestr(" or quit? ");
    s=agt_readline(5);
    if (strncasecmp(s,"RESTART",7)==0)
      if (restart_state!=NULL)
	{restart_game();done_flag=1;}
      else writeln("Sorry, I'm unable to do that because of limited memory.");
    else if (strncasecmp(s,"RESTORE",7)==0)
      if (loadgame()) {done_flag=1;}
      else writeln("(RESTORE failed)");
    else if (strncasecmp(s,"UNDO",4)==0)
      if (can_undo && undo_state!=NULL)
	{putstate(undo_state);done_flag=1;}
      else writeln("Insufficiant memory to support UNDO");
    else if (toupper(s[0])=='Q')
      {quitflag=1;done_flag=1;}
  }
  set_statline();
}


static void parse_loop(void)
/* This exists to deal with THEN lists; parse() handles the indiviudual
 commands */
{
  for(ip=0;ip>=0 && ip<MAXINPUT && input[ip]!=-1; ) {
    if (!parse() || quitflag || winflag || deadflag ||endflag) break;
    if (doing_restore) break;
    if (ip>=0 && ip<MAXINPUT && input[ip]!=-1)
      writeln(""); /* Insert blank lines between commands when dealing
		      with THEN lists */
  }
}


static long rm_start_size;
static char memstr[100];

static void mainloop(void)
{
  char *s;

  doing_restore=0;
  while(!quitflag)
    {
      if (DEBUG_MEM) {
	sprintf(memstr,
         "A:%ld F:%ld  Delta:%ld   Size:%ld+%ld=%ld (%ld left)\n", 
		ralloc_cnt,rfree_cnt,ralloc_cnt-rfree_cnt,
		rm_start_size,rm_size-rm_start_size,rm_size,
		rm_freesize);
	writeln(memstr);}
      rm_size=0;  /* Reset it to zero */
      rm_freesize=get_rm_freesize();
      if (!menu_mode) {
	prompt_out(1);
	s=agt_readline(0);
	agt_newline();
	if (!doing_restore) tokenise(s);   /* Tokenizes into input */
	rfree(s);
	if (!doing_restore) parse_loop();
      } 
      else 
	menu_cmd();
      if (doing_restore) {
	if (doing_restore==1) loadgame();
	else if (doing_restore==2) restart_game();
	else if (doing_restore==3 || doing_restore==4) 
	  return; /* Quit or New game requested */
	doing_restore=0;	
      }
      if (winflag || deadflag || endflag || quitflag) 
	game_end(); 
    }
}


/*-------------------------------------------------------------------*/
/* Start up and shut down: Routines to initialise the game state and */
/*   clean up after the game ends.                                   */
/*-------------------------------------------------------------------*/

static int init(void)
{
  int i, can_save;
  uchar *tmp1, *tmp2;

  init_vals();
  if (!agx_file) dict[0][0]=0;  /*  Turn "ANY" into "" */
  l_stat[0]=r_stat[0]=0; /* Clear the status line */
/*  lactor=lobj=lnoun=NULL;*/
  tscore=old_score=objscore=0;
  turncnt=0;
  curr_time=start_time;
  loc=start_room-first_room;
  cmd_saveable=0;
  first_visit_flag=newlife_flag=room_firstdesc=1;
  curr_lives=max_lives;

  /* Note: flag[0] is the debugging flag and is set elsewhere */
  if (FLAG_NUM<0) FLAG_NUM=0;
  dbgflagptr=flag=rrealloc(flag,sizeof(bool)*(FLAG_NUM+1)); 
  for(i=1;i<=FLAG_NUM;i++) 
    flag[i]=0;
  dbgcntptr=agt_counter=rmalloc(sizeof(short)*(CNT_NUM+1));
  for(i=0;i<=CNT_NUM;i++)
    {agt_counter[i]=-1;}
  dbgvarptr=agt_var=rmalloc(sizeof(*agt_var)*(VAR_NUM+1));
  for(i=0;i<=VAR_NUM;i++)
    agt_var[i]=0;

  for(i=0;i<=maxnoun-first_noun;i++) {
    if (noun[i].position[0]=='\0') 
      noun[i].pos_prep=0;
    else noun[i].pos_prep=-1;
    noun[i].pos_name=0;
  }
  pictable=rmalloc(sizeof(int)*maxpict);
  for(i=0;i<maxpict;i++) pictable[i]=i;
  init_state_sys(); /* Initialize the system for saving and restoring
		       game states */
  tmp1=rmalloc(MEM_MARGIN);  /* Preserve some work space */

  tmp2=getstate(NULL); /* Make sure we have space to save */
  if (tmp2==NULL) can_save=0;
  else can_save=1;

  if (tmp2!=NULL)
    undo_state=getstate(NULL);
  else undo_state=NULL;

  if (undo_state!=NULL) 
    restart_state=getstate(NULL);
  else restart_state=NULL;

  rfree(tmp1);
  rfree(tmp2);
  rm_start_size=get_rm_size();
  rm_freesize=get_rm_freesize();
  return can_save;
}


static void ext_dict(void)
/* Enter the vocabulary extensions into the dictionary */
{
  wtype i;
  for(i=wthe;i<=wabout;i++)
    ext_code[i]=add_dict(ext_voc[i]);
}


static void fix_dummy(void)
{
  int i;

 /* At this point, all occurances in the game file of the dictionary
    words have been converted to dictionary indices, and so as long as
    we don't change the dictionary index values, we can change the contents
    without interfering with the metacommand scanner (since it compares 
    dictionary indices, not actual strings) */

  if (!PURE_DUMMY) {
    for(i=0;i<DUMB_VERB;i++)
      dict[ syntbl[auxsyn[i+BASE_VERB]] ][5]=' ';
	   /* Convert underscores into spaces: 
	      i.e. 'dummy_verb5' -> 'dummy verb5' */
    dict[ syntbl[auxsyn[21]] ][6]=' '; /* change_locations */
    dict[ syntbl[auxsyn[55]] ][5]=' '; /* magic_word */
  }

  if (!PURE_SUBNAME)     /* Replace the 'e' by a space */
    for(i=0;i<MAX_SUB;i++)
      sprintf(dict[sub_name[i]],"subroutin %d",i+1);
  /* This must be no longer than 25 characters with the terminating null */
  
  /* Now set PURE_DOT based on whether any dictionary word 
     contains a period. */
  if (aver>=AGT18 && aver<=AGT18MAX) PURE_DOT=0;
  else {
    PURE_DOT=FORCE_PURE_DOT;
    for(i=0;i<dp && !PURE_DOT;i++)
      if (strchr(dict[i],'.')!=NULL &&    /* i.e. dict[i] contains period */
	  i!=ext_code[wp])   /* The period itself _is_ a dictionary word:
				avoid this false match */
	PURE_DOT=1;
  }
}


/* char *v */

static void print_license(void)
{
  writeln("AGiliTy");
  writestr("The (Mostly) Universal AGT Interpreter, ");
  writeln(version_str);
  writeln("  Copyright 1996,97 by Robert Masenten");
  writestr("[");writestr(portstr);writeln("]");
  writeln("-----------------------------------------------------------");
  writeln("");
  writeln("  This is an interpreter for game files created with Malmberg and "
	  "Welch's _Adventure Game Toolkit_. AGiliTy is universal in the "
	  "sense that it understands and interprets most of the many versions "
	  "of the AGT game file format.");
  writeln("  It is *not* a port of the original interpreters but rather a "
	  "completely new interpreter built around the game file format; "
	  "while it follows the original interpreters on most things, there "
	  "are some differences which are described in the file "
	  "'readme.agility' which should have come with this program.");
  writeln("");
  writeln("  This software is copyright 1996 by Robert Masenten but may be "
	  "freely distributed and used for non-commercial purposes so long as "
	  "this copyright notice is retained.; since this is a beta version, "
	  "I also ask that this not be placed on FTP sites or BBSs other than "
	  "the Interactive Fiction archive at ftp.gmd.de and its mirrors.");
  writeln("");
  writeln("  Since this is free software, it has NO WARRANTY of any kind.");
  writeln("");
  writeln("  Send comments and bug reports to Robert Masenten at:");
  writeln("      masenten@math.upenn.edu");
  writeln("");
  writeln("ACKNOWLEDGMENTS");
  writeln("Thanks to Jay Goemmer, my most active beta tester, who has sent "
	  "me pages of useful suggestions and bug reports; " 
	  "David Kinder, responsible for the Amiga port and a source of "
	  "much valuble feedback; all of those who have sent me suggestions "
	  "and bug reports, including Audrey DeLisle, John Hartnup, " 
	  "Sami Kivela, "
	  "Alexander Lehmann, Ben Straub, Adam Thornton, Mark Tilford, and "
	  "Gil Williamson; Robert Pelak, who suggested the name \"AGiliTy\"; "
	  "and to everyone on Rec.arts.int-fiction who suggested names for "
	  "my interpreter.");
  writeln("");
  writeln("SPECIAL VERBS RECOGNIZED");
  writeln("These are all of the special verbs recognized by the interpreter:");
  writeln("  SCORE  Print out your score.");
  writeln("  NOTIFY Turn score notification on and off.");
  writeln("  INSTRUCTIONS or INS  Display the instructions for the game.");
  writeln("  INTRODUCTION or INTRO  Repeat the introduction of the game.");
  writeln("  BRIEF   Don't print room descriptions for rooms you've seen.");
  writeln("  VERBOSE  Print room descriptions even  for rooms you've already "
	  "seen.");
  writeln("  LIST EXITS  List the exits from a room.");
  writeln("  LISTEXIT ON/OFF  Turn on/off automatic listing of exits.");
  writeln("  SCRIPT  Start sending a transcript to a file.");
  writeln("  UNSCRIPT  Stop creating a transcript.");
  writeln("  SOUND ON, OFF  Turn sound on/off.");
  writeln("  LOG  Start sending all of your commands to a file.");
  writeln("  REPLAY <number>  Replay your commands from a file, "
	  "executing one every <number> seconds.");
  writeln("  REPLAY STEP  Replay your commands from a file, "
	  "one for every keypress.");
  writeln("  AGILDEBUG  Access debugging commands.");
  writeln("  MENU  Toggle menu mode on or off.");
  writeln("  OOPS  Correct a word you just mistyped; must be the first "
	  "command on a line.");
  writeln("  UNDO  Undo your last move; must be the first command on a line.");
  writeln("  SAVE  Save the game.");
  writeln("  RESTORE  Restore the game.");
  writeln("  RESTART  Restart the game.");
  writeln("  QUIT  Quit.");
  writeln("");
} 


void close_game(void); /* Called by setup_game, and so needs
				 to be defined here. */

static void setup_game(char *game_name)
/* game_name is the common filename of the AGT game files */
{
  int can_save;
  char choice;
  bool have_ins;

  hold_game_name=rstrdup(game_name);
  rm_acct=1;rm_trap=1;  
  rm_size=ralloc_cnt=rfree_cnt=0;
  mars_fix=0; no_auxsyn=0;
  debug_disambig=0;debug_any=1;
  dbg_nomsg=1; /* Supress output of MSG arguments to metacommands */
  textbold=0;debug_mode=0;aver=0;
  verboseflag=1;notify_flag=0;logflag=0;menu_mode=0;
  bold_mode=0;
 
  text_file=1; 
  read_config(agt_globalfile(0),1);   /* Global configuration file */
  read_config(openfile(game_name,pCFG,NULL,0),0); 
  text_file=0;
    /* First pass through game specific config file */
  build_trans_ascii();
#ifdef PROFILE
  resetwatch();
#endif
  writeln("Loading game...");
  if (!read_agx(game_name,0))  
    readagt(game_name,0);
#ifdef PROFILE
  writeln(stopwatch());
  agt_getkey(0);
#endif
  if (have_opt) 
    menu_mode=opt_data[5];   /* See agtread.c for discussion of OPT file
				format */
  text_file=1;
  read_config(openfile(game_name,pCFG,NULL,0),1);/*Game specific config file*/
  text_file=0;
  sort_cmd();
  ext_dict(); 
  build_verbmenu(); 
  fix_dummy(); /* Prevent player from calling dummy verbs or subroutines by 
		    typing 'Subroutine n' on the command line */
  can_save=init();
  if (!agx_file) open_descr(game_name);
  start_interface();
  if (intro_first && intro_ptr.size>0) {
    agt_clrscr();
    print_descr(intro_ptr,1);
    wait_return();
  }
  print_title(game_name);
  have_ins=open_ins_file(game_name,0);
  do {
    if (have_ins) 
      writestr("Choose <I>nstructions, <A>GiliTy Information, "
	       "or <other> to start the game");
    else 
      writestr("Choose <A>GiliTy Information or <other> to start the game");
    choice=tolower(agt_getkey(0));  /* Wait for keypress */
    agt_clrscr();
    if (have_ins && choice=='i') print_instructions(game_name);
    else if (choice=='a') print_license();
  } while ( (choice=='i' && have_ins) || choice=='a');
  close_ins_file();
  if (!intro_first && intro_ptr.size>0) {
    print_descr(intro_ptr,1);
    wait_return();
    agt_clrscr();
  }
  if (maxroom<first_room) {close_game();exit(EXIT_SUCCESS);}
  set_statline();
  if (can_save==0) {
    writeln("[Insufficiant memory to support SAVE, RESTORE, or UNDO]");
  }
  else if (undo_state==NULL) 
    writeln("[Insufficiant memory to support UNDO]");
  do_look=do_autoverb=1;
  newroom();  
  rm_acct=1; /* Turn on memory allocation accounting */
}


/* We need to import save_lnoun from exec.c so that we can free it. */
extern parse_rec *save_lnoun;

void close_game(void)
{
  if (agx_file)
    agx_close_descr();
  else
    close_descr();
  fontcmd(1,-1); /* Restore original font */
  musiccmd(7,-1);  /* Clean up */
  close_interface();

 /* Now free everything in sight; this _shouldn't_ be necessary,
    but why take chances? */
  free_all_agtread();
  rfree(restart_state);rfree(undo_state);
  rfree(pictable);rfree(save_lnoun);
  rfree(hold_game_name);
  rfree(verbptr);rfree(verbend);
  rfree(flag);rfree(agt_counter);rfree(agt_var);

  if (DEBUG_MEM)
    printf("\n\nAlloc:%ld  Freed:%ld  Difference:%ld\n",ralloc_cnt,
	   rfree_cnt,ralloc_cnt-rfree_cnt);
}


void run_game(char *gamefile)
{
  doing_restore=0;
  do {
    if (doing_restore==3) 
      setup_game(new_game());
    else setup_game(gamefile);
    doing_restore=0;
    mainloop();
    close_game();
  } while (doing_restore==3);
}


