/*
   File:         pg.c
   Author:       Andrew W. Moore
   Created:      4th September 1992
   Description:  Header for PartiGame software. See pg.txt for
                 further information.

   Copyright (C) Andrew W. Moore, 1992
*/

#include <stdio.h>
#include <math.h>
#include "ambs.h"
#include "amma.h"
#include "pg.h"

/* COST operations. Pretty simple, but don't access costs directly.
   Always use these routines.
*/

bool less_cost(c1,c2)
cost *c1,*c2;
{
  return(c1 -> winner && (!c2->winner || c1->steps < c2->steps));
}

bool same_cost(c1,c2)
cost *c1,*c2;
{
  if ( c1 -> winner )
    return(c2 -> winner && c1 -> steps == c2 -> steps);
  else
    return(!c2 -> winner);
}

void copy_cost(c1,c2)
cost *c1,*c2;
{
  c2 -> winner = c1 -> winner;
  c2 -> steps = c1 -> steps;
}

bool is_winner(co)
cost *co;
{
  return(co -> winner);
}

void fprint_cost(s,m,co)
FILE *s;
char *m;
cost *co;
{
  if ( is_winner(co) )
    fprintf(s,"%s = %d\n",m,co->steps);
  else
    fprintf(s,"%s = infinity\n",m);
}

void set_loser_cost(co)
cost *co;
{
  co -> winner = FALSE;
  co -> steps = -777;
}

void set_zero_cost(co)
cost *co;
{
  co -> winner = TRUE;
  co -> steps = 0;
}

void add_to_cost(pg,co,v,new_cost)
pgame *pg;
cost *co;
float v;
cost *new_cost;
{
  if ( is_winner(co) )
  {
    new_cost -> winner = TRUE;
    new_cost -> steps = co -> steps + my_irint(v);
    if ( new_cost -> steps >= pg -> number_states )
      set_loser_cost(new_cost);
  }
  else
    set_loser_cost(new_cost);
}

float cost_to_float(co)
cost *co;
{
  if ( co -> winner )
    return((float) co->steps);
  else
    return(1e20);
}

/* STATE operations */

state *find_state(stl,state_id)
state_list *stl;
int state_id;
{
  state *result = NULL;
  for (  ; result == NULL && stl != NULL ; stl = stl->next )
  {
    if ( stl -> state -> state_id == state_id )
      result = stl -> state;
  }

  return(result);
}

/* ACTIONS operations */

actions *find_action(acs,action_id)
actions *acs;
int action_id;
{
  actions *result = NULL;
  for ( ; result == NULL && acs != NULL ; acs = acs -> next )
  {
    if ( acs -> action_id == action_id )
      result = acs;
  }

  return(result);
}

state_list *find_outcomes_node(outs,next_state,res_prev)
state_list *outs;
state *next_state;
state_list **res_prev;
{
  state_list *result = NULL;

  *res_prev = NULL;

  if ( outs != NULL )
  {
    if ( outs -> state -> state_id == next_state -> state_id )
      result = outs;
    else
    {
      for ( ; result == NULL && outs -> next != NULL ; outs = outs -> next )
      {
        if ( outs -> next -> state -> state_id == next_state -> state_id )
        {
          result = outs -> next;
          *res_prev = outs;
        }
      }
    }
  }

  return(result);
}

/* PRINTING operations */

void fprint_state_list(s,stl)
FILE *s;
state_list *stl;
{
  for ( ; stl != NULL ; stl = stl -> next )
    fprintf(s,"%d%s",stl->state->state_id,(stl->next == NULL) ? "" : " ");
}

void fprint_action(s,m,ac)
FILE *s;
char *m;
actions *ac;
{
  fprintf(s,"%s ==>{",m);
  fprint_state_list(s,ac->outcomes);
  fprintf(s,"}\n");
}

void fprint_state(s,m,st,verbosity)
FILE *s;
char *m;
state *st;
int verbosity;
/*
   verbosity = 1   =>   Only print actions and outcomes
   verbosity = 2   =>   ..and cost and best action
   verbosity = 3   =>   ..and preds
*/
{
  char buff[100];
  actions *ac;
  fprintf(s,"%s id = %d\n",m,st->state_id);
  for ( ac = st -> actions ; ac != NULL ; ac = ac -> next )
  {
    sprintf(buff,"%s action %d",m,ac->action_id);
    fprint_action(s,buff,ac);
  }

  if ( verbosity > 1 )
  {
    sprintf(buff,"%s -> cost",m);
    fprint_cost(s,buff,&st->cost);
    if ( is_winner(&st->cost) )
      fprintf(s,"%s -> best -> id = %d\n",m,st->best->action_id);
    else
      fprintf(s,"%s -> best = NULL\n",m);
  }
  if ( verbosity > 2 )
  {
    fprintf(s,"%s -> preds = (",m);
    fprint_state_list(s,st->preds);
    fprintf(s,")\n");
  }
}

void fprint_pgame(s,m,pg,verbosity)
FILE *s;
char *m;
pgame *pg;
int verbosity;
/*
   verbosity = 1   =>   Only print actions and outcomes
   verbosity = 2   =>   ..and cost and best action
   verbosity = 3   =>   ..and preds
*/
{
  state_list *stl;
  char buff[100];
  for ( stl = pg -> states ; stl != NULL ; stl = stl -> next )
  {
    sprintf(buff,"%s state %d",m,stl->state->state_id);
    fprint_state(s,buff,stl->state,verbosity);
    fprintf(s,"\n");
  }
  fprintf(s,"%s -> number_states = %d\n",m,pg -> number_states);
  if ( pg -> goal == NULL )
    fprintf(s,"%s has no goal state\n",m);
  else
    fprintf(s,"%s -> goal -> id = %d\n",m,pg->goal->state_id);
}

/* STATE_LIST operstaions */

state_list *add_to_state_list(st,stl)
state *st;
state_list *stl;
{
  state_list *new = AM_MALLOC(state_list);
  new -> state = st;
  new -> next = stl;
  return(new);
}

state_list *add_to_state_list_end(stl,st)
state_list *stl;
state *st;
{
  state_list *new = AM_MALLOC(state_list);
  state_list *result;
  new -> state = st;
  new -> next = NULL;

  if ( stl == NULL )
    result = new;
  else
  {
    state_list *last = stl;
    while ( last -> next != NULL )
      last = last -> next;
    last -> next = new;
    result = stl;
  }

  return(result);
}

bool is_in_state_list(stl,st)
state_list *stl;
state *st;
{
  bool result = FALSE;
  for ( ; !result && stl != NULL ; stl = stl -> next )
    result = stl -> state -> state_id == st -> state_id;
  return(result);
}

state_list *make_copy_state_list(stl)
state_list *stl;
{
  state_list *result = NULL;

  if ( stl != NULL )
  {
    state_list *last = add_to_state_list(stl->state,(state_list *) NULL);
    result = last;

    for ( stl = stl -> next ; stl != NULL ; stl = stl -> next )
    {
      last -> next = add_to_state_list(stl->state,(state_list *)NULL);
      last = last -> next;
    }
  }

  return(result);
}

void free_state_list_node(stl)
state_list *stl;
{
  am_free((char *) stl,sizeof(state_list));
}

state_list *state_list_remove(stl,st)
state_list *stl;
state *st;
/*
   am_free()'s the state list node associated with any copies
   of st in the list. Result list is the same as the input with
   the appropriate links changed to remove the st copies. Sorry,
   crap commenting.
*/
{
  state_list *result = NULL;
  state_list *last = NULL;

  while ( stl != NULL )
  {
    if ( stl -> state -> state_id == st -> state_id )
    {
      state_list *next = stl -> next;
      free_state_list_node(stl);
      stl = next;
    }
    else
    {
      if ( last == NULL )
        result = stl;
      else
        last -> next = stl;

      last = stl;
      stl = stl -> next;
    }
  }

  if ( last != NULL ) last -> next = NULL;
  return(result);
}
  
/* PGAME operations */

state *add_state_to_pgame(pg,data)
pgame *pg;
char *data;
{
  state *st = AM_MALLOC(state);
  st -> state_id = pg->number_states;
  st -> actions  = NULL;
  st -> data     = data;
  set_loser_cost(&st->cost);
  st -> best     = NULL;
  st -> preds    = NULL;

  pg -> states = add_to_state_list_end(pg->states,st);
  pg -> number_states += 1;
  return(st);
}

actions *add_action_to_state(st,data)
state *st;
char *data;
{
  actions *ac = AM_MALLOC(actions);
  actions *last = st->actions;

  while ( last != NULL && last -> next != NULL )
    last = last -> next;

  ac -> action_id        = (last == NULL) ? 0 : 1 + last->action_id;
  ac -> outcomes         = NULL;
  ac -> opponents_choice = NULL;
  ac -> data             = data;
  ac -> next             = NULL;

  if ( last == NULL )
    st -> actions = ac;
  else
    last -> next = ac;

  return(ac);
}

void set_goal(pg,st)
pgame *pg;
state *st;
{
  if ( pg -> goal != NULL || !is_in_state_list(pg->states,st) )
  {
    printf("Warning, something's wrong with set_goal(pg,%d)\n",st->state_id);
    if ( pg -> goal != NULL )
      printf("Goal had earlier been set as state %d\n",pg->goal->state_id);
    else
    {
      printf("Its not in the set of all states: ");
      fprint_state_list(stdout,pg->states);
      printf("\n");
    }
    wait_for_key();
  }

  pg -> goal = st;
}

void maybe_include_action_outcome(ac,next_state)
actions *ac;
state *next_state;
{
  if ( !is_in_state_list(ac->outcomes,next_state) )
    ac -> outcomes = add_to_state_list_end(ac->outcomes,next_state);
}

void free_state_list(stl)
state_list *stl;
{
  while ( stl != NULL )
  {
    state_list *next = stl -> next;
    free_state_list_node(stl);
    stl = next;
  }
}

void free_actions(ac)
actions *ac;
{
  if ( ac != NULL )
  {
    free_actions(ac->next);
    free_state_list(ac->outcomes);
    am_free((char *) ac,sizeof(actions));
  }
}

void free_state_actions_preds(st)
state *st;
{
  free_actions(st->actions);
  free_state_list(st->preds);
  am_free((char *) st,sizeof(state));
}

void free_pgame_contents(pg)
pgame *pg;
{
  state_list *stl;
  for ( stl = pg -> states ; stl != NULL ; stl = stl -> next )
    free_state_actions_preds(stl->state);

  free_state_list(pg->states);

  pg->states = NULL;
  pg->goal = NULL;
}

state_list *union_ordered_state_lists(base_stl,add_stl)
state_list *base_stl,*add_stl;
/* Adds new, allocated, nodes to base_stl as necessary.
   PRE: base_stl and add_stl are ordered in increasing order of
        state_id, and each contains no duplicates within itself.

   POST: { result } = { base } UNION { add }
         result is ordered and no-duplicates.
         add_stl is unchanged.
         base_stl is.
*/
{
  state_list *result = NULL;
  state_list *last = NULL;
  while ( base_stl != NULL || add_stl != NULL )
  {
    if ( base_stl == NULL )
    {
      state_list *new_node = 
        add_to_state_list(add_stl -> state,(state_list *)NULL);
      add_stl = add_stl -> next;
      if ( last == NULL )
      {
        result = new_node;
        last = new_node;
      }
      else
      {
        last -> next = new_node;
        last = new_node;
      }
    }
    else if ( add_stl == NULL )
    {
      if ( last == NULL )
        result = base_stl;
      else
        last -> next = base_stl;

      base_stl = NULL;
    }
    else
    {
      int base_id = base_stl->state->state_id;
      int add_id = add_stl->state->state_id;
      if ( base_id == add_id )
        add_stl = add_stl -> next;
      else
      {
        state_list *new;
        if ( base_id < add_id )
        {
          new = base_stl;
          base_stl = base_stl -> next;
        }
        else
        {
          new = add_to_state_list(add_stl->state,(state_list *)NULL);
          add_stl = add_stl -> next;
        }
        new -> next = NULL;
        if ( result == NULL )
        {
          result = new;
          last = new;
        }
        else
        {
          last -> next = new;
          last = new;
        }
      }
    }
  }
  return(result);
}

void add_to_preds(st,pred_state)
state *st,*pred_state;
{
  state_list node;
  node.state = pred_state;
  node.next = NULL;
  st -> preds = union_ordered_state_lists(st->preds,&node);
}

void pred_finder(pg)
pgame *pg;
{
  state_list *stl;
  actions *ac;
  state_list *ocs;

  for ( stl = pg -> states ; stl != NULL ; stl = stl -> next )
    for ( ac = stl->state->actions ; ac != NULL ; ac = ac -> next )
      for ( ocs = ac -> outcomes ; ocs != NULL ; ocs = ocs -> next )    
        add_to_preds(ocs->state,stl->state);
}  

void set_loser_state(st)
state *st;
{
  set_loser_cost(&st->cost);
  st -> best = NULL;
}

void init_pgame(pg)
pgame *pg;
{
  pg -> states = NULL;
  pg -> goal = NULL;
  pg -> number_states = 0;
}

void all_losers(pg)
pgame *pg;
{
  state_list *stl;
  for ( stl = pg -> states ; stl != NULL ; stl = stl -> next )
    set_loser_state(stl->state);
}

void goal_state_winner(pg)
pgame *pg;
{
  state *gst = pg -> goal;

  if ( gst == NULL )
    my_error("null goal state");
  else
  {
    actions *returner = NULL;
    actions *ac;
    set_zero_cost(&gst->cost);
    
    for ( ac = gst->actions ; ac != NULL && returner == NULL ; ac = ac->next )
    {
      if ( ac->outcomes != NULL &&
           ac->outcomes->state->state_id == gst->state_id &&
           ac->outcomes->next == NULL
         )
        returner = ac;
    }

    if ( returner == NULL )
      my_error("Goal must have a guarranteed-return-to-self action");

    gst->best = returner;
  }
}

void cost_of_outcome(pg,st,ac,next_state,co)
pgame *pg;
state *st;
actions *ac;
state *next_state;
cost *co;
{
  if ( st -> state_id == next_state -> state_id )
    set_loser_cost(co);
  else
    add_to_cost(pg,&next_state->cost,1.0,co);
}

void cost_of_worst_outcome(pg,st,ac,result_cost)
pgame *pg;
state *st;
actions *ac;
cost *result_cost;
{
  state_list *ou = ac -> outcomes;
  if ( ou == NULL )
    my_error("pg.c, cost_of_worst_outcome: no outcomes");
  else
  {
    cost_of_outcome(pg,st,ac,ou->state,result_cost);
    for ( ou=ou->next ; is_winner(result_cost) && ou != NULL ; ou=ou->next )
    {
      cost co;
      cost_of_outcome(pg,st,ac,ou->state,&co);
      if ( less_cost(result_cost,&co) )
        copy_cost(&co,result_cost);
    }
  }
}

int Number_backups = 0;

actions *backup(pg,st,new_cost)
pgame *pg;
state *st;
cost *new_cost;
{
  actions *ac;
  actions *best = NULL;
  cost best_cost;

  Number_backups += 1;

  set_loser_cost(&best_cost);

  for ( ac = st -> actions ; ac != NULL ; ac = ac -> next )
  {
    cost co;
    cost_of_worst_outcome(pg,st,ac,&co);
    if ( less_cost(&co,&best_cost) )
    {
      best = ac;
      copy_cost(&co,&best_cost);
    }
  }

  copy_cost(&best_cost,new_cost);
  return(best);
}

state_list *sweep_set_and_find_preds(pg,stl)
pgame *pg;
state_list *stl;
/*
   am_free's() the memory of stl nodes, but am_malloc's() the
   memory for the result set.

   result set = predecessors of all those states whose values changed
   during their backups.

   Each state only occurs once in the state list, both input and output
*/
{
  state_list *preds = NULL;

  while ( stl != NULL )
  {
    cost new;
    state *st;
    state_list *next;
    st = stl -> state;
    next = stl -> next;
    st -> best = backup(pg,st,&new);

    if ( !same_cost(&new,&st->cost) )
    {
      copy_cost(&new,&st->cost);
      preds = union_ordered_state_lists(preds,st->preds);
    }
    free_state_list_node(stl);
    stl = next;
  }
  return(preds);
}
    
void solve_pgame(pg)
pgame *pg;
/*
   Assumes set up legally and all preds are defined.
   Assumes nothing about the costs and best actions.
*/
{
  state_list *active;

  all_losers(pg);
  goal_state_winner(pg);

  active = make_copy_state_list(pg->goal->preds);
  active = state_list_remove(active,pg->goal);

  while ( active != NULL )
  {
#ifdef DEBUG
    state_list *stl;
    int i = 1;
    for ( stl = active ; stl != NULL ; stl = stl -> next )
    {
      char buff[100];
      sprintf(buff,"Active[%d]",i);
      fprint_state(stdout,buff,stl->state,3);
      wait_for_key();
      printf("Waited for key\n");
      i++;
    }
#endif
    active = sweep_set_and_find_preds(pg,active);
    active = state_list_remove(active,pg->goal);
  }
}

void save_pgame(fname,pg)
char *fname;
pgame *pg;
{
  FILE *s = safe_fopen(fname,"w");
  state_list *stl;
  actions *ac;
  state_list *ou;
  fprintf(s,"n %4d\n",pg->number_states);
  fprintf(s,"g %4d\n",pg->goal->state_id);
  
  for ( stl = pg -> states ; stl != NULL ; stl = stl -> next )
    for ( ac = stl -> state -> actions ; ac != NULL ; ac = ac -> next )
    {
      fprintf(s,"s %4d a %4d =",stl->state->state_id,ac->action_id);
      for ( ou = ac -> outcomes ; ou != NULL ; ou = ou -> next )
        fprintf(s," %4d",ou->state->state_id);
      fprintf(s,"\n");
    }
  fclose(s);
}

typedef struct pg_file_struct
{
  char *fname;
  FILE *stream;
  int line;
  int recent_char;
} pg_file;

void open_pg_file(fname,pgf)
char *fname;
pg_file *pgf;
{
  pgf->fname = fname;
  pgf->stream = safe_fopen(fname,"r");
  pgf->line = 1;
  pgf->recent_char = ' ';
}

void close_pg_file(pgf)
pg_file *pgf;
{
  fclose(pgf->stream);
  printf("Closed PartiGame file %s\n",pgf->fname);
}

void read_pg_error(pgf,mess)
pg_file *pgf;
char *mess;
{
  printf("Error reading PartiGame file. Filename = %s, Line = %d\n",
         pgf->fname,pgf->line
        );
  my_error(mess);
}

void read_pg_string(pgf,buff)
pg_file *pgf;
char *buff;
{
  int c = pgf -> recent_char;
  while ( c == ' ' )
    c = getc(pgf->stream);
  if ( c == EOF )
    sprintf(buff,"EOF");
  else if ( c == '\n' )
  {
    sprintf(buff,"EOL");
    pgf -> line += 1;
    pgf -> recent_char = ' ';
  }
  else
  {
    int i = 0;
    while ( c != ' ' && c != EOF && c != '\n' )
    {
      buff[i] = c;
      i += 1;
      c = getc(pgf->stream);
    }
    buff[i] = '\0';
    pgf -> recent_char = c;
  }

  if ( Verbosity > 100.0 )
    printf("Line %3d of file %10s: read token %s\n",
           pgf->line,pgf->fname,buff
          );
}

void check_pg_string(pgf,goal)
pg_file *pgf;
char *goal;
{
  char buff[100];
  read_pg_string(pgf,buff);
  if ( !eq_string(goal,buff) )
  {
    printf("Expected: %s\n",goal);
    printf("     Got: %s\n",buff);
    read_pg_error(pgf,"load_pgame(): syntax error in input file");
  }
}

int read_pg_int(pgf)
pg_file *pgf;
{
  char buff[100];
  int result;
  read_pg_string(pgf,buff);
  if ( sscanf(buff,"%d",&result) != 1 )
  {
    printf("Error reading integer. String = '%s'\n",buff);
    read_pg_error(pgf,"load_pgame(): syntax error in input file");
  }
  
  if ( result < 0 || result > 10000 )
    read_pg_error(pgf,"load_pgame(): read stupid integer");

  return(result);
}  
  
void load_pgame(fname,pg)
char *fname;
pgame *pg;
{
  pg_file pgf_mem;
  pg_file *pgf = &pgf_mem;
  char buff[100];
  int number_states,goal_id;

  open_pg_file(fname,pgf);

  init_pgame(pg);
  
  check_pg_string(pgf,"n");
  number_states = read_pg_int(pgf);
  check_pg_string(pgf,"EOL");
  check_pg_string(pgf,"g");
  goal_id = read_pg_int(pgf);
  check_pg_string(pgf,"EOL");

  while ( pg -> number_states < number_states )
  {
    state *st = add_state_to_pgame(pg,(char *)NULL);
    if ( st -> state_id == goal_id )
      set_goal(pg,st);
  }

  while ( !eq_string(buff,"EOF") )
  {
    int aid;
    state *st;
    bool found_outcomes = FALSE;
    read_pg_string(pgf,buff);
    if ( !eq_string(buff,"EOF") )
    {
      if ( !eq_string(buff,"s") )
        read_pg_error(pgf,"load_pgame(): expecting an 's' or EOF");
      else
      {
        st = find_state(pg->states,read_pg_int(pgf));
        check_pg_string(pgf,"a");
        aid = read_pg_int(pgf);
        while ( find_action(st->actions,aid) == NULL )
          (void) add_action_to_state(st,(char *)NULL);
        check_pg_string(pgf,"=");
        while ( !found_outcomes )
        {
          read_pg_string(pgf,buff);
          found_outcomes = eq_string(buff,"EOL") || eq_string(buff,"EOF");
          if ( !found_outcomes )
          {
            state *next = find_state(pg->states,atoi(buff));
            if ( next == NULL )
            {
              printf("Loading pg from %s find illegal out %s\n",fname,buff);
              read_pg_error(pgf,"pg.c");
            }
            maybe_include_action_outcome(find_action(st->actions,aid),next);
          }
        }
      }
    }
  }

  close_pg_file(pgf);
}

void maybe_set_verbosity(argc,argv)
int argc;
char *argv[];
{
  int i;
  for ( i = 1 ; i < argc-1 ; i++ )
  {
    if ( eq_string(argv[i],"-v") )
      Verbosity = atof(argv[i+1]);
  }
}

state_list *sl(st,stl)
state *st;
state_list *stl;
{
  return(add_to_state_list(st,stl));
}

void test_union(pg)
pgame *pg;
{
  if ( pg -> number_states < 4 )
    my_error("test_union need 4 states");
  else
  {
    state *s[4];
    int i;
    for ( i = 0 ; i < 4 ; i++ )
      s[i] = find_state(pg->states,i);

    {
      state_list *sl_null = NULL;
      state_list *a = sl(s[0],sl(s[2],sl(s[3],sl_null)));
      state_list *b = sl(s[2],sl_null);
      state_list *c = sl(s[1],sl_null);

      am_malloc_report();
      printf("a = {"); fprint_state_list(stdout,a); printf("}\n");
      printf("b = {"); fprint_state_list(stdout,b); printf("}\n");
      printf("c = {"); fprint_state_list(stdout,c); printf("}\n");
      printf("Will do: b = union_ordered_state_lists(b,c);\n");
      b = union_ordered_state_lists(b,c);
      wait_for_key();

      am_malloc_report();
      printf("a = {"); fprint_state_list(stdout,a); printf("}\n");
      printf("b = {"); fprint_state_list(stdout,b); printf("}\n");
      printf("c = {"); fprint_state_list(stdout,c); printf("}\n");
      printf("Will do: b = union_ordered_state_lists(b,c);\n");
      b = union_ordered_state_lists(b,c);
      wait_for_key();

      am_malloc_report();
      printf("a = {"); fprint_state_list(stdout,a); printf("}\n");
      printf("b = {"); fprint_state_list(stdout,b); printf("}\n");
      printf("c = {"); fprint_state_list(stdout,c); printf("}\n");
      printf("Will do: c = union_ordered_state_lists(c,a);\n");
      c = union_ordered_state_lists(c,a);
      wait_for_key();

      am_malloc_report();
      printf("a = {"); fprint_state_list(stdout,a); printf("}\n");
      printf("b = {"); fprint_state_list(stdout,b); printf("}\n");
      printf("c = {"); fprint_state_list(stdout,c); printf("}\n");
      printf("Will do: b = union_ordered_state_lists(b,a);\n");
      b = union_ordered_state_lists(b,a);
      wait_for_key();

      am_malloc_report();
      printf("a = {"); fprint_state_list(stdout,a); printf("}\n");
      printf("b = {"); fprint_state_list(stdout,b); printf("}\n");
      printf("c = {"); fprint_state_list(stdout,c); printf("}\n");
      printf("Will do: b = union_ordered_state_lists(b,a);\n");
      b = union_ordered_state_lists(b,a);
      wait_for_key();

      am_malloc_report();
      printf("a = {"); fprint_state_list(stdout,a); printf("}\n");
      printf("b = {"); fprint_state_list(stdout,b); printf("}\n");
      printf("c = {"); fprint_state_list(stdout,c); printf("}\n");
      printf("Will free the lists\n");
      wait_for_key();

      free_state_list(a);
      free_state_list(b);
      free_state_list(c);
      am_malloc_report();
      wait_for_key();
    }
  }
}
      


void remove_outcome_and_pred(src_st,ac,dest_st)
state *src_st;
actions *ac;
state *dest_st;
/*
   PRE: st may or may not be in ac->outcomes
   POST: it isn't, and the state_list node is removed as is reference
         to "src_st" in "dest_st"'s preds
*/
{
  if ( ac == NULL || dest_st == NULL || src_st == NULL )
    my_error("remove_outcome_and_pred. NULL problem");

  ac -> outcomes = state_list_remove(ac->outcomes,dest_st);
  dest_st -> preds = state_list_remove(dest_st->preds,src_st);
}

void new_outcome_and_pred(src_st,ac,dest_st)
state *src_st;
actions *ac;
state *dest_st;
{
  if ( ac == NULL || dest_st == NULL || src_st == NULL )
    my_error("new_outcome_and_pred. NULL problem");

  maybe_include_action_outcome(ac,dest_st);
  add_to_preds(dest_st,src_st);
}


    
void test_pg(argc,argv)
int argc;
char *argv[];
{
  maybe_set_verbosity(argc,argv);
  if ( argc < 2 )
    printf("Usage: pg <filename>\n");
  else if ( eq_string(argv[1],"malloc_test") )
    am_malloc_test();
  else
  {
    pgame pg;
    bool end = FALSE;
    load_pgame(argv[1],&pg);
    fprint_pgame(stdout,"just-load-pg",&pg,1);
    printf("Will test union..\n");
    if ( Verbosity > 200.0 )
      test_union(&pg);
    printf("Will find preds..\n");
    pred_finder(&pg);
    fprint_pgame(stdout,"pred-found-pg",&pg,3);
    solve_pgame(&pg);
    fprint_pgame(stdout,"solved-pg",&pg,3);

    while ( !end )
    {
      int snum;
      printf("What state shall I print? (qu to end)> ");
      if ( scanf("%d",&snum) != 1 )
        end = TRUE;
      else
        fprint_state(stdout,"state",find_state(pg.states,snum),3);
    }

    am_malloc_report();
    printf("That was a memory report before freeing contents\n");
    wait_for_key();
    free_pgame_contents(&pg);
    am_malloc_report();
    printf("That was a final memory report after freeing pg contents\n");
  }
  printf("Bye\n");
}
