Rog-O-Matic XIV (part 07 of 10)

Michael Mauldin mlm at cmu-cs-cad.ARPA
Sat Feb 2 02:31:01 AEST 1985


#!/bin/sh
#
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# @ Here is part of your new automatic Rogue player, Rog-O-Matic XIV! @
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# 
#     [Note: this is a Beta-Test release of version XIV, and almost
#      certainly contains bugs.  A new version will be made available
#      soon.  If you experience any problems with this version, please
#      contact Michael Mauldin as soon as possible, so your input can be
#      included in the new release]
# 
# Rog-O-Matic XIV is shipped via mail in pieces, files rgm14.01, rgm14.02,
# ..., rgm14.nn.  Each piece contains some number of smaller files. To
# retrieve them, run each file through the shell 'sh', as follows:
# 
# 	sh <rgm14.01
# 	sh <rgm14.02
# 	     ...
# 	sh <rgm14.nn
# 
# or do it all at once:
# 
# 	cat rgm14.* | sh
# 
# The README file contains all necessary information to edit the "install.h"
# file, after which "make" will build the rogomatic and player binary files.
# Please note that file 'Bugreport' contains modifications you may wish to
# make to the code BEFORE you compile it.  You can safely install ALL of
# them; depending on your version of Rogue, you may HAVE to install SOME of
# them.
# 
# Rog-O-Matic is copyrighted, but permission is given to copy and modify the
# source provided that (1) it is not used for profit (2) all authorship and
# copyright notices remain intact and (3) any person or site with a copy has
# notified Michael Mauldin either by electronic mail or US Post that they
# have Rog-O-Matic XIV.
# 
# We would appreciate hearing about any interesting additions or modifi-
# cations, and would especially like to know how well the program plays
# against your Rogue.  And we really, really want to know if Rog-O-Matic
# becomes a "Total Winner" against Rogue 5.2 or Rogue 5.3 again.
# 
# 				Michael Mauldin (Fuzzy)
# 				Department of Computer Science
# 				Carnegie-Mellon University
# 				Pittsburgh, PA  15213
# 				(412) 578-3065,  mauldin at cmu-cs-a.arpa
#
echo 'Start of Rog-O-Matic XIV, part 07 of 10:'
echo 'x - Bugreport'
sed 's/^X//' > Bugreport << '/'
X/*
X * Bugreport: Rog-O-Matic XIV (CMU) Tue Jan 29 14:59:59 1985 - mlm
X * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin
X */
X
XNo known bugs (yet).
/
echo 'x - datesub.l'
sed 's/^X//' > datesub.l << '/'
X%{
X/*
X * datesub.l: Rog-O-Matic XIV (CMU) Fri Feb  1 10:23:56 1985 - mlm
X * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin
X */
X
X# define yywrap() 1
X%}
X%%
X"Jan"				printf ("1"); 
X"Feb"				printf ("2"); 
X"Mar"				printf ("3"); 
X"Apr"				printf ("4"); 
X"May"				printf ("5"); 
X"Jun"				printf ("6"); 
X"Jul"				printf ("7"); 
X"Aug"				printf ("8"); 
X"Sep"				printf ("9"); 
X"Oct"				printf ("10"); 
X"Nov"				printf ("11"); 
X"Dec"				printf ("12"); 
X%%
X
Xmain ()
X{ while (yylex ())
X    ; 
X}
/
echo 'x - learn.c'
sed 's/^X//' > learn.c << '/'
X/*
X * learn.c: Rog-O-Matic XIV (CMU) Thu Jan 31 20:30:10 1985 - mlm
X * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin
X *
X * Genetic learning component.
X */
X
X# include <stdio.h>
X# include "types.h"
X
X# define TRIALS(g)		((g)->score.count)
X# define NONE		(-1)
X# define MAXM		100
X# define ALLELE		100
X# define ZEROSTAT	{0, 0, 0, 0, 0}
X
Xtypedef struct
X{ int   id, creation, father, mother, dna[MAXKNOB];
X        statistic score, level;
X}               genotype;
X
Xextern int knob[];
Xextern double mean(), stdev(), sqrt();
Xextern FILE *wopen();
X
Xstatic int inittime=0, trialno=0, lastid=0;
Xstatic int crosses=0, shifts=0, mutations=0;
Xstatic statistic g_score = ZEROSTAT;
Xstatic statistic g_level = ZEROSTAT;
Xstatic genotype *genes[MAXM];
Xstatic int length = 0;
Xstatic int mindiff = 10, pmutate = 4, pshift = 2, mintrials = 1;
Xstatic double step = 0.33; /* standard deviations from the mean */
Xstatic FILE *glog=NULL;
X
Xstatic int compgene();
X
X/*
X * Start a new gene pool
X */
X
Xinitpool (k, m)
X{ inittime = time (0);
X
X  if (glog) fprintf (glog, "Gene pool initalized, k %d, m %d, %s",
X                     k, m, ctime (&inittime));
X
X  randompool (m);
X}
X
X/*
X * Summarize the current gene pool
X */
X
Xanalyzepool (full)
Xint full;
X{ register int g;
X
X  qsort (genes, length, sizeof (*genes), compgene);
X
X  printf ("Gene pool size %d, started %s", length, ctime (&inittime));
X  printf ("Trials %d, births %d (crosses %d, mutations %d, shifts %d)\n",
X          trialno, lastid, crosses, mutations, shifts);
X  printf ("Mean score %1.0lf+%1.0lf, Mean level %3.1lf+%3.1lf\n\n",
X	  mean (&g_score), stdev (&g_score),
X	  mean (&g_level), stdev (&g_level));
X
X  for (g=0; g<length; g++)
X  { printf ("Living: "); summgene (stdout, genes[g]);
X    if (full)
X    { if (genes[g]->mother)
X        printf ("  Parents: %3d,%-3d", genes[g]->father, genes[g]->mother);
X      else
X        printf ("  Parent:  %3d,   ", genes[g]->father);
X      printf ("  best %4.0lf/%-2.0lf",
X		genes[g]->score.high, genes[g]->level.high);
X      printf ("    DNA  "); printdna (stdout, genes[g]); printf ("\n\n");
X    }
X  }
X}
X
X/*
X * setknobs: Read gene pool, pick genotype, and set knobs accordingly.
X */
X
Xsetknobs (newid, knob, best, avg)
Xint *newid, *knob, *best, *avg;
X{ register int i, g;
X  
X  ++trialno;
X
X  g = pickgenotype ();	/* Pick one genotype */
X  *newid = genes[g]->id;
X
X  for (i=0; i<MAXKNOB; i++)	/* Set the knobs for that genotype */
X    knob[i] = genes[g]->dna[i];
X
X  *best = genes[g]->score.high;
X  *avg = (int) mean (&(genes[g]->score));
X}
X
X/*
X * evalknobs: Add a data point to the gene pool
X */
X
Xevalknobs (gid, score, level)
Xint gid, score, level;
X{ register int g;
X
X  /* Find out which gene has the correct id */
X  for (g=0; g<length; g++)
X    if (gid == genes[g]->id) break;
X
X  /* If he got deleted by someone else, blow it off */  
X  if (g >= length) return;
X
X  /* Add information about performance */
X  addstat (&(genes[g]->score), score);
X  addstat (&g_score, score);
X  addstat (&(genes[g]->level), level);
X  addstat (&g_level, level);
X
X  if (glog)
X  { fprintf (glog, "Trial %4d, Id %3d -> %4d/%-2d  ",
X      trialno, genes[g]->id, score, level);
X
X    fprintf (glog, "age %2d, %4.0lf+%-4.0lf  %4.1lf+%3.1lf\n",
X	     TRIALS(genes[g]),
X	     mean (&(genes[g]->score)), stdev (&(genes[g]->score)),
X	     mean (&(genes[g]->level)), stdev (&(genes[g]->level)));
X  }
X}
X
X/*
X * openlog: Open the gene log file
X */
X
XFILE *openlog (genelog)
Xregister char *genelog;
X{ glog = wopen (genelog, "a");    
X  return (glog);
X}
X
X/*
X * closelog: Close the log file
X */
X
Xcloselog ()
X{ if (glog) fclose (glog);
X}
X
X/*
X * pickgenotype: Run one trial, record performance, and do some learning
X */
X
Xpickgenotype ()
X{ register int youth, father, mother, new;
X  
X  /* Find genotype with fewer trials than needed to measure its performance */
X  youth = untested ();
X  if (youth >= 0) return (youth);
X
X  /* 
X   * Have a good measure of all genotypes, pick a father, a mother, and
X   * a loser and create a new genotype using genetic operators.
X   */
X
X  father = selectgene (NONE, NONE);
X  mother = selectgene (father, NONE);
X  new = badgene (father, mother);
X
X  /* If no losers yet, return the youngest */
X  if (new < 0) return (youngest ());
X 
X  /* Shift a single genotype with probability pshift */
X  if (randint (100) < pshift)
X  { if (glog)
X    { fprintf (glog, "Select: "); summgene (glog, genes[father]);
X      fprintf (glog, "Death:  "); summgene (glog, genes[new]);
X    }
X 
X    shift (father, new);
X  }
X
X  /* Mutate a single genotype with probability pmutate */
X  else if (randint (100-pshift) < pmutate)
X  { if (glog)
X    { fprintf (glog, "Select: "); summgene (glog, genes[father]);
X      fprintf (glog, "Death:  "); summgene (glog, genes[new]);
X    }
X 
X    mutate (father, new);
X  }
X
X  /* Cross two genotypes with probability 1-pshift-pmutate */
X  else
X  { if (glog)
X    { fprintf (glog, "Select: "); summgene (glog, genes[father]);
X      fprintf (glog, "Select: "); summgene (glog, genes[mother]);
X      fprintf (glog, "Death:  "); summgene (glog, genes[new]);
X    }
X 
X    cross (father, mother, new);
X  }
X
X  /* Log the birth */
X  if (glog) birth (glog, genes[new]);
X
X  return (new);  		/* Evaluate the new genotype */
X}
X
X/*
X * readgenes: Open the genepool for reading, and fill the current gene pool.
X * Returns true if the file was was, and 0 if there was no fail.  Exits
X * if the file exists and cannot be read.
X */
X
Xreadgenes (genepool)
Xregister char *genepool;
X{ char buf[BUFSIZ];
X  register char *b;
X  register int g=0;
X  FILE *gfil;
X
X  if ((gfil = fopen (genepool, "r")) == NULL)
X  { if (fexists (genepool))
X      quit (1, "Cannot open file '%s'\n", genepool);
X    else
X      return (0);
X  }
X
X  /* Read the header line */
X  b = buf;
X  fgets (b, BUFSIZ, gfil);
X  sscanf (b, "%d %d %d %d %d %d",
X	&inittime, &trialno, &lastid, &crosses, &shifts, &mutations);
X  SKIPTO ('|', b);
X  parsestat (b, &g_score);
X  SKIPTO ('|', b);
X  parsestat (b, &g_level);
X
X  /* Now read in each genotype */
X  while (fgets (buf, BUFSIZ, gfil) && length < MAXM-1)
X  { if (g >= length)
X    { genes[g] = (genotype *) malloc (sizeof (**genes));
X      length++;
X    }
X    initgene (genes[g]);
X    parsegene (buf, genes[g++]);
X  }
X
X  fclose (gfil);
X  return (1);
X}
X
X/*
X * parsegene: Given a string representing a genotype and a genotype
X * structure, fill the structure according to the string.
X */
X
Xstatic parsegene (buf, gene)
Xregister char *buf;
Xregister genotype *gene;
X{ register int i;
X
X  /* Get genotype specific info */
X  sscanf (buf, "%d %d %d %d", &gene->id, &gene->creation,
X	  &gene->father, &gene->mother);
X
X  /* Read each DNA gene */
X  SKIPTO ('|', buf);
X  SKIPCHAR (' ', buf);
X  for (i=0; ISDIGIT (*buf); i++)
X  { if (i < MAXKNOB) gene->dna[i] = atoi (buf);
X    SKIPDIG (buf);
X    SKIPCHAR (' ', buf);
X  }
X
X  /* Read the score and level performance stats */
X  SKIPTO ('|', buf);
X  parsestat (buf, &(gene->score));
X  SKIPTO ('|', buf);
X  parsestat (buf, &(gene->level));
X}
X
X/*
X * writegenes: Write the gene pool 'genes' out to file 'genepool'
X */
X
Xwritegenes (genepool)
Xregister char *genepool;
X{ register FILE *gfil;
X  register int g;
X
X  /* Open the gene file */
X  if ((gfil = wopen (genepool, "w")) == NULL)
X    quit (1, "Cannot open file '%s'\n", genepool);
X
X  /* Write the header line */
X  fprintf (gfil, "%d %d %d %d %d %d",
X	   inittime, trialno, lastid, crosses, shifts, mutations);
X  fprintf (gfil, "|");
X  writestat (gfil, &g_score);
X  fprintf (gfil, "|");
X  writestat (gfil, &g_level);
X  fprintf (gfil, "|\n");
X
X  /* Loop through each genotype */
X  for (g=0; g<length; g++)
X    writegene (gfil, genes[g]);
X  
X  fclose (gfil);
X}
X
X/*
X * Write out one line representing the gene.
X */
X
Xstatic writegene (gfil, g)
Xregister FILE *gfil;
Xregister genotype *g;
X{ register int i;
X
X  /* Print genotype specific info */
X  fprintf (gfil, "%3d %4d %3d %3d|", g->id, g->creation,
X	  g->father, g->mother);
X
X  /* Write out dna */
X  for (i=0; i<MAXKNOB; i++) 
X  { fprintf (gfil, "%2d", g->dna[i]);
X    if (i < MAXKNOB-1) fprintf (gfil, " ");
X  }
X  fprintf (gfil, "|");
X
X  /* Write out statistics */
X  writestat (gfil, &(g->score));
X  fprintf (gfil, "|");
X  writestat (gfil, &(g->level));
X  fprintf (gfil, "|\n");
X}
X
X/*
X * initgene: Allocate a new genotype structure, set everything to 0.
X */
X
Xstatic initgene (gene)
Xregister genotype *gene;
X{ register int i;
X
X  /* Clear genoptye specific info */
X  gene->id = gene->creation = gene->father = gene->mother = 0;
X
X  /* Clear the dna */
X  for (i = 0; i < MAXKNOB; i++) gene->dna[i] = 0;
X
X  /* Clear the statictics */
X  clearstat (&(gene->score));
X  clearstat (&(gene->level));
X}
X
X/*
X * compgene: Compare two genotypes in terms of score.
X */
X
Xstatic int compgene (a, b)
Xgenotype **a, **b;
X{ register int result;
X
X  result = (int) mean (&((*b)->score)) - 
X           (int) mean (&((*a)->score));
X
X  if (result) return (result);
X  else return ((*a)->id - (*b)->id);
X}
X
X/*
X * summgene: Summarize a single genotype
X */
X
Xstatic summgene (f, gene)
Xregister FILE *f;
Xregister genotype *gene;
X{ fprintf (f, "%3d age %2d, created %4d, ",
X	   gene->id, TRIALS(gene), gene->creation);
X  fprintf (f, "score %5.0lf+%-4.0lf level %4.1lf+%-3.1lf\n",
X           mean (&(gene->score)), stdev (&(gene->score)),
X           mean (&(gene->level)), stdev (&(gene->level)));
X}
X
X/*
X * Birth: Summarize Record the birth of a genotype.
X */
X
Xstatic birth (f, gene)
Xregister FILE *f;
Xregister genotype *gene;
X{
X  if (!glog) return;
X
X  fprintf (f, "Birth:  %d ", gene->id);
X
X  if (gene->mother)
X    fprintf (f, "(%d,%d)", gene->father, gene->mother);
X  else
X      fprintf (f, "(%d)", gene->father);
X
X  fprintf (f, " created %d, DNA ", gene->creation);
X  printdna (f, gene);
X  fprintf (f, "\n");
X}
X
X/*
X * printdna: Print the genotype of a gene
X */
X
Xstatic printdna (f, gene)
XFILE *f;
Xregister genotype *gene;
X{ register int i;
X
X  fprintf (f, "(");
X  for (i=0; i < MAXKNOB; i++)
X  { fprintf (f, "%02d", gene->dna[i]);
X    if (i < MAXKNOB-1) fprintf (f, " ");
X  }
X  fprintf (f, ")");
X}
X
X/*
X * cross: Cross two genotypes producing a new genotype
X */
X
Xstatic cross (father, mother, new)
Xregister int father, mother, new;
X{ register int cpoint, i;
X
X  /* Set the new genotypes info */
X  genes[new]->id = ++lastid;
X  genes[new]->creation = trialno;
X  genes[new]->father = genes[father]->id;
X  genes[new]->mother = genes[mother]->id;
X  clearstat (&(genes[new]->score));
X  clearstat (&(genes[new]->level));
X  
X  /* Pick a crossover point and dominant parent */
X  cpoint = randint (MAXKNOB-1) + 1;
X
X  /* Fifty/fifty chance we swap father and mother */
X  if (randint (100) < 50)
X  { father ^= mother; mother ^= father; father ^= mother; }
X
X  /* Copy the dna over */
X  for (i=0; i<MAXKNOB; i++)
X    genes[new]->dna[i] = (i<cpoint) ?
X      genes[father]->dna[i] : genes[mother]->dna[i];
X
X  makeunique (new);
X
X  /* Log the crossover */  
X  if (glog)
X  { fprintf (glog, "Crossing %d and %d produces %d\n",
X		genes[father]->id,  genes[mother]->id, genes[new]->id);
X  }
X  
X  crosses++;
X}
X
X/*
X * mutate: mutate a genes producing a new gene
X */
X
Xstatic mutate (father, new)
Xregister int father, new;
X{ register int i;
X
X  /* Set the new genotypes info */
X  genes[new]->id = ++lastid;
X  genes[new]->creation = trialno;
X  genes[new]->father = genes[father]->id;
X  genes[new]->mother = 0;
X  clearstat (&(genes[new]->score));
X  clearstat (&(genes[new]->level));
X
X  /* Copy the dna over */  
X  for (i=0; i<MAXKNOB; i++)
X    genes[new]->dna[i] = genes[father]->dna[i];
X
X  /* Randomly change genes until the new genotype is unique */
X  do
X  { i=randint (MAXKNOB);
X    genes[new]->dna[i] = (genes[new]->dna[i] +
X			  triangle (20) + ALLELE) % ALLELE;
X  } while (!unique (new));
X
X  /* Log the mutation */
X  if (glog)
X  { fprintf (glog, "Mutating %d produces %d\n",
X		genes[father]->id, genes[new]->id);
X  }
X
X  mutations++;
X}
X
X/*
X * shift: shift a gene producing a new gene
X */
X
Xstatic shift (father, new)
Xregister int father, new;
X{ register int i, offset;
X  
X  /* Set the new genotypes info */
X  genes[new]->id = ++lastid;
X  genes[new]->creation = trialno;
X  genes[new]->father = genes[father]->id;
X  genes[new]->mother = 0;
X  clearstat (&(genes[new]->score));
X  clearstat (&(genes[new]->level));
X
X  /* Pick an offset, triangularly distributed around 0, until unique */
X  offset = triangle (20);
X  for (i=0; i<MAXKNOB; i++)
X    genes[new]->dna[i] = (genes[father]->dna[i] + 
X			  offset + ALLELE) % ALLELE;
X
X  makeunique (new);
X
X  /* Now log the shift */
X  if (glog)
X  { fprintf (glog, "Shifting %d by %d produces %d\n",
X		genes[father]->id, offset, genes[new]->id);
X  }
X
X  shifts++;
X}
X
X/*
X * randompool: Initialize the pool to a random starting point
X */
X
Xstatic randompool (m)
Xregister int m;
X{ register int i, g;
X
X  for (g=0; g<m; g++)
X  { if (g >= length)
X    { genes[g] = (genotype *) malloc (sizeof (**genes));
X      length++;
X    }
X    initgene (genes[g]);
X    genes[g]->id = ++lastid;
X    for (i=0; i<MAXKNOB; i++) genes[g]->dna[i] = randint (ALLELE);
X    birth (glog, genes[g]);
X  }
X  
X  length = m;
X}
X
X/*
X * selectgene: Select a random gene, weighted by mean score.
X */
X
Xstatic selectgene (e1, e2)
Xregister int e1, e2;
X{ register int total=0;
X  register int g;
X
X  /* Find the total worth */
X  for (g=0; g<length; g++)
X  { if (g==e1 || g==e2) continue;
X    /* total += (int) mean (&(genes[g]->score)); */
X    total += genes[g]->score.high;
X  }
X  
X  /* Pick a random number and find the corresponding gene */
X  if (total > 0)
X  { for (g=0, total=randint (total); g<length; g++)
X    { if (g==e1 || g==e2) continue;
X      /* total -= (int) mean (&(genes[g]->score)); */
X      total -= genes[g]->score.high;
X      if (total < 0) return (g);
X    }
X  }
X
X  /* Total worth zero, pick any gene at random */
X  while ((g = randint (length))==e1 || g==e2) ;
X  return (g);
X}
X
X/*
X * unique: Return false if gene is an exact copy of another gene.
X */
X
Xstatic unique (new)
Xregister int new;
X{ register int g, i, delta, sumsquares;
X
X  for (g=0; g<length; g++)
X  { if (g != new)
X    { sumsquares = 0;
X      for (i=0; i<MAXKNOB; i++)
X      { delta = genes[g]->dna[i] - genes[new]->dna[i];
X	sumsquares += delta * delta;
X      }
X      if (sumsquares < mindiff) return (0);
X    }
X  }
X  
X  return (1);
X}
X
X/*
X * untested: Return the index of the youngest genotype with too few
X * trials to have an accurate measure.  The number of trials is
X * greater for older genotypes.
X */
X
Xstatic untested ()
X{ register int g, y= -1, trials=1e9, newtrials, count=length;
X  
X  for (g = randint (length); count-- > 0; g = (g+1) % length)
X  { if (TRIALS (genes[g]) >= trials) continue;
X
X    newtrials = trialno - genes[g]->creation;	/* Turns since creation */
X
X    if (TRIALS (genes[g]) < newtrials / (4 * length) + mintrials)
X    { y = g; trials = TRIALS (genes[g]); }
X  }
X
X  return (y);
X}
X
X/*
X * youngest: Return the index of the youngest genotype
X */
X
Xstatic youngest ()
X{ register int g, y=0, trials=1e9, newtrials, count=length;
X  
X  for (g = randint (length); count-- > 0; g = (g+1) % length)
X  { newtrials = TRIALS (genes[g]);
X    if (newtrials < trials) { y=g; trials=newtrials; }
X  }
X
X  return (y);
X}
X
X/*
X * makeunique: Mutate a genotype until it is unique
X */
X
Xstatic makeunique (new)
Xregister int new;
X{ register int i;
X
X  while (!unique (new))
X  { i=randint (MAXKNOB);
X    genes[new]->dna[i] = (genes[new]->dna[i] +
X		          triangle (20) + ALLELE) % ALLELE;
X  }
X}
X
X/*
X * triangle: Return a non-zero triangularly distributed number from -n to n.
X */
X
Xstatic triangle (n)
Xregister int n;
X{ register int val;
X
X  do
X  { val = randint (n) - randint (n);
X  } while (val==0);
X  
X  return (val);
X}
X
X/*
X * badgene: Find the worst performer so far (with the lowest id).
X * only consider genotypes dominated by other genotypes.
X */
X
Xstatic badgene (e1, e2)
Xregister int e1, e2;
X{ register int g, worst, trials;
X  double worstval, bestval, avg, dev, value;
X
X  worst = -1; worstval = 1.0e9;
X  bestval = -1.0e9;
X    
X  for (g=0; g<length; g++)
X  { if ((trials = TRIALS (genes[g])) < mintrials) continue;
X    avg = mean (&(genes[g]->score));    
X    dev = stdev (&(genes[g]->score)) / sqrt ((double) trials);
X    value = avg - step * dev;
X    if (value > bestval) { bestval=value; }
X    if (g==e1 || g==e2) continue;
X    value = avg + step * dev;
X    if (value < worstval) { worst=g; worstval=value; }
X  }
X
X  if (worstval < bestval)	return (worst);
X  else				return (-1);
X}
/
echo 'x - tactics.c'
sed 's/^X//' > tactics.c << '/'
X/*
X * tactics.c: Rog-O-Matic XIV (CMU) Thu Jan 31 20:31:22 1985 - mlm
X * Copyright (C) 1985 by A. Appel, G. Jacobson, L. Hamey, and M. Mauldin
X *
X * This file contains all of the 'medium level intelligence' of Rog-O-Matic. 
X */
X
X# include <stdio.h>
X# include <ctype.h>
X# include <curses.h>
X# include "types.h"
X# include "globals.h"
X# include "install.h"
X
X/*
X * handlearmor: This routine is called to determine whether we should 
X * take off or put on armor.
X *
X * Current strategy:   Wear best armor on levels 1..7 or 19 on or
X *                     if protected or have maintain armor.
X *                     Wear 2nd best armor between levels 13..18.
X *                     Wear best leather armor between levels 8..12   
X *                     or 2nd best if no leather armor.   DR UTexas 12/15/83
X *
X * Note that leather armor does not rust.
X */
X
Xhandlearmor ()
X{ int obj;
X
X  /* Only check when armor status is different */
X  if (!newarmor || cursedarmor) return (0);
X
X  /* 
X   * Pick the armor we want to wear. If we are worried about rust monster
X   * we wear the second best armor, but if we wont see any rust monsters,
X   * if our armor is too good for a rust monster to hit it, or we have a
X   * ring of maintain armor, then we should wear our best armor.  On
X   * levels 13-18 we wear our second best no matter what.
X   */
X   
X  obj = havearmor (1, NOPRINT, ANY);		/* Get best armor */
X
X  if (Level > 7 && Level < 19 && 
X      wearing ("maintain armor") == NONE &&
X      willrust (obj) &&
X      itemis (obj, KNOWN))
X  { if (Level < 13)
X      obj = havearmor (1, NOPRINT, RUSTPROOF);
X    if (Level >= 13 || obj == NONE)
X      obj = havearmor (2, NOPRINT, ANY);
X  }
X    
X  /* If  the new armor is really bad, then don't bother wearing any */
X  if (obj != NONE &&
X      (armorclass (obj) > 9) ||
X      (itemis (obj, CURSED) &&
X       (havenamed(scroll, "remove curse") == NONE) &&
X       (armorclass (obj) > 6))) 
X  { obj = NONE; }
X
X  /* If we are wearing the right armor, then dont bother */
X  if (obj == currentarmor)
X  { newarmor = 0; return (0); }
X
X  /* Debugging */
X  dwait (D_PACK, "handlearmor: obj %d, currentarmor %d", obj, currentarmor);
X
X  /* Take off the wrong armor */
X  if (currentarmor != NONE && takeoff ())
X  { return (1); }
X
X  /* Put on the right armor, avoid wearing cursed armor */
X  if (obj != NONE)
X  { return (wear (obj)); }
X
X  /* If we have no armor, then forget it */
X  newarmor = 0;
X  return (0);
X}
X
X/* 
X * handleweapon: wield our best weapon. Calls haveweapon.
X *
X *  The current strategy is to wield the best weapon from haveweapon.
X */
X
Xhandleweapon ()
X{ int obj; 
X
X  if ((!newweapon || cursedweapon) && !wielding (thrower)) return (0);
X
X  /* haveweapon (1) returns the index of the best weapon in the pack */
X  if ((obj = haveweapon (1, NOPRINT)) < 0) return (0);
X  
X  /* If we are not wielding our best weapon, do so */
X  if (obj == currentweapon) { newweapon = 0; return (0); }
X  else if (obj != NONE)        { return (wield (obj)); }
X  else                      { newweapon = 0; return (0); }
X}
X
X/*
X * quaffpotion: check whether we should quaff a potion, and call
X * quaff if so.  We quaff add strength, restore strength, healing, 
X * extra healing, and raise level here.  Potions of seeinvisible
X * are handled in 'fightinvisible'.
X *
X * If we are at or below the exp. level, then experiment with unknown potions.
X */
X
X# define MAXSTR (version < RV52A ? 1900 : 3100)
X
Xquaffpotion ()
X{ int obj = NONE, obj2 = NONE;
X
X  /* Take advantage of double haste bug -- assures permanent haste */
X  if (!doublehasted && version < RV52A &&
X      ((hasted && (obj = havenamed (potion, "haste self")) != NONE) ||
X       ((obj = havemult (potion, "haste self", 2)) != NONE)) &&
X      quaff (obj))
X    return (1);
X
X  /* 
X   * Can we use a gain strength to our advantage? Or a restore?
X   * If we have a Gain Strength, or our strength is very bad,
X   * then we quaff a Regain Strength.
X   */
X   
X  if (Str == Strmax && (obj = havenamed (potion, "gain strength")) != NONE &&
X      quaff (obj))
X    return (1);
X
X  if ((Str < 700 ||
X       (Str != Strmax && (havenamed (potion, "gain strength") != NONE))) &&
X      (obj = havenamed (potion, "restore strength")) != NONE &&
X      quaff (obj))
X    return (1);
X
X  if ((Str < 1600 || Level > 12) &&
X      (obj = havemult (potion, "restore strength", 2)) != NONE &&
X      quaff (obj))
X    return (1);
X
X  /* Try to get unblinded by quaffing a potion */
X  if (blinded && 
X      ((obj = havenamed (potion, "healing")) != NONE ||
X       (obj = havenamed (potion, "extra healing")) != NONE ||
X       (obj = havenamed (potion, "see invisible")) != NONE) &&
X      quaff (obj))
X    return (1);
X
X  /* Try to get uncosmic by quaffing a potion */
X  if (cosmic &&
X      (obj = havenamed(potion, "extra healing")) != NONE &&
X      quaff (obj))
X    return (1);
X
X  if (cosmic && Str != Strmax &&
X      (obj = havenamed (potion, "poison")) != NONE)
X  { if (wearing ("sustain strength") != NONE && quaff (obj) ||
X        findring ("sustain strength"))
X      return (1);
X  }
X
X  /* 
X   * Quaff healing to raise our MaxHp
X   * Wait for cosmic known to quaff extra healing. DR,TG  UTexas
X   */
X
X  if (Hp == Hpmax &&
X      ((obj = havemult (potion, "healing", 2)) != NONE ||
X       (obj = havemult (potion, "extra healing", 2)) != NONE ||
X       know ("blindness") && (obj = havenamed (potion, "healing")) != NONE ||
X       know ("blindness") && (know ("hallucination") || version < RV53A)  &&
X        Level < 15 && (obj = havenamed (potion, "extra healing")) != NONE) &&
X      quaff (obj))
X    return (1);
X
X  /*
X   * Quaff a raise level potion?
X   */
X  
X  if ((Explev > 8 || Level > 13) &&
X      (obj = havenamed (potion, "raise level")) != NONE && 
X      quaff (obj))
X    return (1);
X
X  /* Quaff an unknown potion? */
X  if ((Level >= (k_exper/10) || objcount >= maxobj || Str<1000 || blinded) &&
X      (obj = unknown (potion)) != NONE)
X  { if ((obj2 = wearing ("add strength")) != NONE && removering (obj2))
X      return (1);
X    else if (wearing ("sustain strength") < 0 &&
X             (obj2 = havenamed (ring, "sustain strength")) != NONE &&
X             puton (obj2))
X      return (1);
X    else if (quaff (obj))
X      return (1);
X  }
X
X  return (0);
X}
X
X/*
X * readscroll: check whether we should read a scroll, and call reads
X * to actually read it.
X *
X * Scrolls of identify, remove curse, genocide, enchant weapon, 
X * enchant armor, magic mapping are defined.  Scrolls of scare
X * monster and confuse monster are handled in 'battlestations'.
X * 
X * If we are at or below (k_exper/10), experiment with unknown scrolls.
X * Make certain that we are wearing our best armor when reading
X * enchant armor or an unknown scroll (which could be enchant
X * armor).
X */
X
Xreadscroll ()
X{ register int obj, obj2;
X
X  /* Check the item specific identify scrolls first */
X  if (((obj = havenamed (scroll, "identify armor")) != NONE &&
X       (obj2 = unknown (armor)) != NONE) ||
X      ((obj = havenamed (scroll, "identify weapon")) != NONE &&
X       (obj2 = unknown (hitter)) != NONE) ||
X      ((obj = havenamed (scroll, "identify potion")) != NONE &&
X       (obj2 = unknown (potion)) != NONE) ||
X      ((obj = havenamed (scroll, "identify scroll")) != NONE &&
X       (obj2 = unknown (scroll)) != NONE) ||
X      ((obj = havenamed (scroll, "identify ring, wand or staff")) != NONE &&
X       ((obj2 = unknown (ring)) != NONE || (obj2 = unknown (wand)) != NONE)))
X  { prepareident (obj2, obj);
X    return (reads (obj)); }
X
X  /* In older version, have multiple uses for generic identify scrolls */
X  if ((obj = havenamed (scroll, "identify")) != NONE &&
X      (currentweapon != NONE) &&
X      (!itemis (currentweapon, KNOWN) && 
X        (!usingarrow || goodarrow > 20)))
X  { prepareident (currentweapon, obj);
X    return (reads (obj)); }
X
X  if ((obj = havenamed (scroll, "identify")) != NONE &&
X      ((obj2 = unknown (ring)) != NONE ||
X       (obj2 = unidentified (wand)) != NONE ||
X       (obj2 = unidentified (scroll)) != NONE ||
X       Level > 10 && (obj2 = unknown (wand)) != NONE ||
X       ((cheat || version == RV36A) &&
X        ((obj2 = unknown (potion)) != NONE ||
X         (obj2 = haveother (scroll)) != NONE))))
X  { prepareident (obj2, obj);
X    return (reads (obj)); }
X
X  if ((cursedarmor || cursedweapon) &&
X      (obj = havenamed (scroll, "remove curse")) != NONE)
X    return (reads (obj));
X
X  if ((obj = havenamed (scroll, "genocide")) != NONE)
X    return (reads (obj));
X
X  if (currentweapon != NONE && 
X      (goodweapon || usingarrow || MaxLevel > 12) &&
X      (obj = havenamed (scroll, "enchant weapon")) != NONE)
X    return (reads (obj));
X
X  if (Level != didreadmap && Level > 12 &&
X       (obj = havenamed (scroll, "magic mapping")) != NONE)
X    return (reads (obj));
X
X  /* About to read an unknown scroll. We will assure that we have */
X  /* a weapon in hand, and put on our best armor for the occasion */
X  /* We must also prepare to identify something, just in case.    */
X
X  if ((obj = havenamed (scroll, "enchant armor")) != NONE ||
X      (obj = havenamed (scroll, "protect armor")) != NONE ||
X      ((currentweapon != NONE) &&
X       (Level >= (k_exper/10) || objcount >= maxobj ||
X        cursedarmor || cursedweapon) &&
X       (exploredlevel || know ("aggravate monster")) &&
X       (obj = unknown (scroll)) != NONE))
X  { prepareident (pickident (), obj);
X
X    /* Go to a corner to read the scroll */
X    if (version <= RV36B && know ("create monster") == '\0' && gotocorner ())
X      return (1);
X
X    /* Must put on our good armor first */
X    if (!cursedarmor && 
X        (!know("enchant armor") || stlmatch(inven[obj].str, "enchant armor") ||
X         !know("protect armor") || stlmatch(inven[obj].str, "protect armor")))
X    { int obj2 = havearmor (1, NOPRINT, ANY); /* Pick our best armor */
X
X      if (obj2 == currentarmor);
X
X      /* Take off the bad stuff */
X      else if (currentarmor != NONE && takeoff ()) return (1);
X
X      /* Put on the good stuff */
X      else if (obj2 != NONE && wear (obj2)) return (1);
X    }
X
X    /* No armor handling, so read the scroll */    
X    return (reads (obj));
X  }
X
X  return (0);
X}
X
X/*
X * handlering: check whether we should put on a ring, and call
X * puton to wear it.  Calls 'havering' to find the two best rings
X * and wears them if their evaluations are greater than 1000.
X *
X * 'havering' understands about when different rings are good, and how
X * much food we need to use each ring.
X */
X
Xhandlering ()
X{ int ring1, ring2;
X
X  if (!newring && !beingstalked) return (0);
X
X  ring1 = havering (1, NOPRINT);
X  ring2 = havering (2, NOPRINT);
X
X  dwait (D_PACK, "Handlering: ring1 %d, ring2 %d, left %d, right %d", 
X	 ring1, ring2, leftring, rightring);
X
X  if ((leftring == ring1 && rightring == ring2) ||
X      (rightring == ring1 && leftring == ring2))
X  { newring = 0; return (0);
X  }
X
X  if (leftring != NONE && leftring != ring1 && leftring != ring2 &&
X      removering (leftring))
X  { return (1);
X  }
X
X  if (rightring != NONE && rightring != ring1 && rightring != ring2 &&
X      removering (rightring))
X  { return (1);
X  }
X
X  if (ring1 != leftring && ring1 != rightring && puton (ring1))
X  { return (1);
X  }
X
X  if (ring2 != leftring && ring2 != rightring && puton (ring2))
X  { return (1);
X  }
X  
X  return (0);
X}
X
X/*
X * findring: called with the named of a ring, attempts to locate such
X * a ring in the pack and wear it. It will remove rings (other than
X * maintain armor) to accomplish this task if it we are wearing two
X * rings.
X *
X * Could be extended to have an ordering of rings to wear.
X */
X
Xfindring (name)
Xchar *name;
X{ int obj;
X
X  if ((obj = havenamed (ring, name)) < 0 ||
X      wearing (name) != NONE)
X    return (0);
X
X  if (leftring != NONE && rightring != NONE)
X  { if (stlmatch (inven[leftring].str, "maintain armor"))
X      return (removering (rightring));
X    else
X      return (removering (leftring));
X  }
X
X  return (puton (obj));
X}
X
X/*
X * grope: get to a safe square and sit and vibrate (move back and forth)
X * and then sleep for 'turns' turns.
X *
X * Problem: We need to know which side of us the monster is on. Then
X * we could zap him with wands or staves.  This requires some kind of
X * memory and the ability to detect when the motion command (ie 'hit'
X * fails to move us).		MLM
X */
X
Xgrope (turns)
Xregister int turns;
X{ register int k, moves;
X
X  if (atrow < 2 || atcol < 1)
X  { command (T_GROPING, "%ds", (turns > 0) ? turns : 1);  
X    return (1);
X  }
X
X  /* Count adjacent CANGO squares */  
X  for (k=0, moves=0; k<8; k++)
X    if (onrc(CANGO, atdrow(k), atdcol(k))) moves++;
X
X  if (moves > 2 && findsafe ()) /* find a spot with 2 or fewer moves */
X    return (1);
X
X  /* blindir is direction of adjacent CANGO square which is not a trap */
X  for (k=0; k<4; k++, blindir = (blindir+2) % 8)
X    if ((onrc(CANGO|TRAP, atdrow(blindir), atdcol(blindir)) == CANGO)) 
X      break;
X
X  if (turns) command (T_GROPING, "%c%c%ds", keydir[blindir],
X                      keydir[(blindir+4)&7], turns);
X  else       command (T_GROPING, "%c%c", keydir[blindir],
X                      keydir[(blindir+4)&7]);
X
X  blindir = (blindir+2) % 8;
X  return (1);
X}
X
X/*
X * findarrow: This function tries to run over an arrow trap to get a 
X *            magic arrow. Make certain we have some food.
X */
X
Xfindarrow ()
X{
X  /* If wrong version, not cheating or must go find food, then forget it */
X  if (version > RV36B || !cheat || hungry())  
X    return (0);
X
X  else if (!usingarrow && foundarrowtrap && !on (ARROW) &&
X           gotowards (trapr, trapc, 0))
X  { display ("Trying for arrow..."); return (1); }
X  
X  return (0);
X}
X
X/* 
X * checkcango: verify that a missile fired in direction 'dir' will
X *             travel 'turns' turns.
X *
X * Modified by mlm, 5/31/83: Return false if a monster is in the way.
X * only return true if the missile will travel EXACTLY the distance
X * specified.  Also changed it to not check the current square (since
X * we can fire from a door, even if we cant shoot through one).
X */
X
Xcheckcango (dir, turns)
Xregister int dir, turns;
X{ register int r, c, dr, dc;
X
X  for (dr = deltr[dir], dc = deltc[dir], r=atrow+dr, c=atcol+dc;
X       turns > 0 && onrc (CANGO | DOOR, r, c) == CANGO;
X       r+=dr, c+=dc, turns--)
X    ;
X
X  return (turns==0);
X}
X
X/*
X * godownstairs: issues a down command and check for the halftimeshow.
X */
X
Xgodownstairs (running)
Xregister int running; /* True ==> dont do anything fancy */
X{ register int p;
X  int genericinit(), downvalue();
X
X  /* We dont want to go down if we have just gotten an arrow, since */
X  /* It is probably bad, and we will want to go back to the trap;   */
X  /* Dont go down until we have killed five monsters in one blow.   */
X  /* While waiting, run back and forth to look for monsters.        */
X
X  if (cheat && version <= RV36B && !running &&
X      foundarrowtrap && usingarrow && 
X      have (food) != NONE && goodarrow < 5 && waitaround ())
X  { saynow ("Checking out arrow...");
X    return (1);
X  }
X
X  /* Check for applicability of this rule */
X  if (! new_stairs) return (0);
X
X  /* If we are on the stairs, perhaps we should rest up some */
X  p = between ((Explev+larder)*10, 60, 100);
X             
X  if (atrow == stairrow && atcol == staircol && 
X      !running && larder > 0 && Hp < max (10, percent (Hpmax, p)))
X  { command (T_RESTING, "s");
X    display ("Resting on stairs before next level");
X    return (1);
X  }
X
X  /* Allow other rules a chance to notice that we are done with the level */
X  if (on (STAIRS) && !exploredlevel)
X  { exploredlevel = 1; return (1); }
X
X  /* If we are floating, we cant go down, either rest or fail */
X  if (floating && running)
X  { saynow ("Cannot escape, floating in mid-air!"); return (0); }
X  else if (floating)
X  { saynow ("Floating above stairs...");
X    command (T_RESTING, "s"); return (1); }
X
X  /* If we are on the stairs, go down */
X  if (on (STAIRS))
X  { halftimeshow (Level);
X
X    /* Start logging at Level GOODGAME, if we arent already */
X    if (Level > (GOODGAME-2) && !replaying && !logging) toggleecho ();
X
X    /* Send the DOWN command and return */
X    command (T_MOVING, ">");
X    return (1);
X  }
X
X  /* If we are running and can run to the next level, do that */
X  if (running && makemove (RUNDOWN, genericinit, downvalue, REEVAL))
X  { return (1);
X  }
X
X  /* If we see the stairs or a trap door, go there */
X  if (!running && makemove (DOWNMOVE, genericinit, downvalue, REUSE))
X  { goalr = targetrow; goalc = targetcol;   /* Set a goal (CPU time hack) */
X    return (1);
X  }
X   
X new_stairs = 0;
X return (0);
X}
X
X/* 
X * plunge: Should we head down immediately?
X *
X * If we are being teleported too much or
X *    we are on a bad level (19 to 25) or
X *    we want to get past Rust Monsters (level 18) or
X *    we have aggravated all of the monsters then
X *
X * we head down immediately.
X */
X 
Xplunge ()
X{
X  /* Check for applicability of this rule */
X  if (stairrow < 0 && !foundtrapdoor) return (0);
X
X  if (teleported > (larder+1)*5 && godownstairs (NOTRUNNING))
X  { if (!on (STAIRS)) saynow ("Giving up on level, too much teleporting");
X    return (1);
X  }
X
X  if (Level > 17 && Level < 26 && godownstairs (NOTRUNNING))
X  { if (!on (STAIRS)) saynow ("Plunge mode!!!");
X    return (1);
X  }
X
X  if (aggravated && godownstairs (NOTRUNNING))
X  { if (!on (STAIRS)) saynow ("Running from aggravated monsters");
X    return (1);
X  }
X
X  if (haveexplored (9) && godownstairs (NOTRUNNING))
X  { if (!on (STAIRS)) saynow ("Level explored");
X    return (1);
X  }
X
X  return (0);
X}
X
X/*
X * waitaround: Hang around here waiting for monsters.
X */
X
Xstatic struct { int vertstart, 
X		    vertend, 
X		    vertdelt, 
X		    horstart, 
X		    horend, 
X		    hordelt; } cb [4] = 
X	{ {  3, 21,  1,  1, 78,  1},	/* Top left corner */
X	  {  3, 21,  1, 78,  1, -1},	/* Top right corner */
X	  { 21,  3, -1, 78,  1, -1},	/* Bottom right corner */
X	  { 21,  3, -1,  1, 78,  1} };  /* Bottom left corner */
X
Xstatic gc = 0; /* Goal corner from 0..3 */
X
X/* 
X * waitaround: For some reason we want to stay on this level for a while.
X * Try running to each corner of the level.
X */
X
Xwaitaround ()
X{ register int i, j;
X
X  if (gotowardsgoal ()) return (1);
X
X  gc = ++gc % 4;
X
X  for (i = cb[gc].vertstart; i != cb[gc].vertend; i += cb[gc].vertdelt)
X    for (j = cb[gc].horstart; j != cb[gc].horend; j += cb[gc].hordelt)
X      if (onrc (BEEN | CANGO | ROOM, i, j) &&
X          !onrc (TRAP, i, j) && gotowards (i, j, 0))
X      { goalr = i; goalc = j; return (1); }
X
X  return (0);
X}
X
X/*
X * goupstairs:
X *
X *      If we have the amulet, and our score is good enough, then
X *      go up stairs. This function also checks for the end of the
X *      game, and issues the proper calls to get the score written.
X */
X
Xgoupstairs (running)
Xint running;
X{ int obj;
X
X  /* Check for applicability of this rule */
X  if (stairrow < 0 || have(amulet) < 0 ||
X      (!running && quitat < BOGUS && Gold <= quitat))
X    return (0);
X      
X  /* If we are on the stairs, then check for win, else go up */
X  if (atrow == stairrow && atcol == staircol)
X  { 
X    /* If we are about to win, dump any magic arrows or minus things */
X    if (Level == 1 && 
X        ((obj = havearrow ()) != NONE || (obj = haveminus ()) != NONE))
X    { throw (obj, 0); return (1); }
X
X    /* No magic arrows, time to leave */
X    else if (Level == 1)
X    { 
X      /* Send an up command and a space to clear the 'You Made It' */
X      sendnow ("< ");
X
X      /* Now read chars until we have the end of the inventory. */
X      /* Note misspelling in Rogue 'Peices', so dont assume anything */
X      waitfor ("Gold P");
X
X      /* Note that quitrogue sends a '\n' to get the score */
X      quitrogue ("total winner", Gold, 0);
X      return (1);
X    }
X
X    /* Not at the top yet, keep on trucking */
X    else
X    { command (T_MOVING, "<"); return (1); }
X  }
X
X  /* If we know where the stairs are, go there */
X  else if ((goalr = stairrow) > 0 && (goalc = staircol) > 0 &&
X           gotowards (goalr, goalc, running)) 
X    return (1);
X
X  return (0);
X}
X
X/*
X * restup: If we are low on hit points, sit for a while. Since handlering 
X * was called first, we will be wearing a ring of regeneration if need be.
X * 
X * First we find a good place to rest (we will move into a room, but not
X * out of one).  In lit rooms, stand far from doors so we can shoot
X * arrows at things coming in.  In dark rooms, stand diagonally away
X * from doors (so we get a one turn warning of monsters coming in that
X * door).  In either case, stand on stairs or next to trap doors and
X * teleport traps).
X *  
X * Then rest by searching 's'.  If one blow would not kill us, and we
X * dont plan to shoot arrows, then rest up so as to heal one hit point.
X * If we are critically low, rest up one turn at a time.
X * 
X * Other considerations:	Dont move if confused or cosmic.
X *				Drink healing potions if really low.
X *				Dont rest when hungry (and no food)
X */
X
Xrestup ()
X{ register int obj, turns;
X
X  /* If we are confused, sit still so we dont bump into anything bad */
X  if (confused) { command (T_RESTING, "s"); return (1); }
X
X  /* If cosmic and plenty of hit points and food, rest for long periods */
X  if (cosmic && (Hp >= percent (Hpmax, 80)) && larder > 2)
X  { display ("Oh wow man, I'm contemplating my navel!");
X    command (T_RESTING, "100s"); return (1); }  
X
X  /* If we are well, return */
X  if (Hp >= max (8, percent (Hpmax, between (Explev*10+k_rest-50, 40, 80))))
X  { unrest ();  return (0); }
X
X  /*
X   * If we are really ill then try a healing potion (save a healing
X   * potion for blindness, extra healing for hallucination).
X   */
X
X  if (Hp < Level+10 && Hp < Hpmax/3 &&
X      ((obj = havemult (potion, "extra healing", 2)) != NONE ||
X       (obj = havemult (potion, "healing", 2)) != NONE ||
X       (know ("hallucination") &&
X        (obj = havenamed (potion, "extra healing")) != NONE) ||
X       (know ("blindness") &&
X        (obj = havenamed (potion, "healing")) != NONE)) &&
X      quaff (obj))
X  { return (1); }
X
X  /* Dont rest when we havent enough to eat */
X  if (hungry ()) return (0);
X
X  display ("Resting up...");
X
X  /*
X   * Look for a good place to rest
X   */
X
X  if (movetorest ()) return (1);
X
X  /* 
X   * If we are very ill, or we are very deep, or we are in a lit room
X   * and can shoot at things as they come ate us, rest only one turn so
X   * monsters dont get the first shot. Otherwise rest enough turns 
X   * to heal one step.
X   */
X  
X  turns = (Level < 8) ? (20-Explev*2) : 3;
X  if ((!darkroom () && ammo) || Hp < Level*2+8 || Level > 15) turns = 1;
X
X  command (T_RESTING, "%ds", turns);
X  return (1);
X}
X
X/*
X * If goalr and goalc are set (not -1,-1) then attempt to move towards
X * that square. Calls gotowards which calls bfsearch.
X */
X
Xgotowardsgoal ()
X{ if (goalr > 0 && goalc > 0)   /* Keep on trucking */
X  { if (goalr == atrow && goalc == atcol) { goalr = NONE; goalc = NONE; }
X    else if (gotowards (goalr, goalc, 0)) { return (1); }
X    else                                  { goalr = NONE; goalc = NONE; }
X  }
X  
X  return (0);
X}
X
X/*
X * gotocorner:	Find a corner using downright and try to go there.
X *		This is done so we can destroy old wands by throwing
X *		them into the corner (which destroys them).
X */
X
Xgotocorner ()
X{ int r, c;
X  if (!downright (&r, &c)) return (0);
X  if (debug (D_SCREEN))
X  { saynow ("Gotocorner called:"); mvaddch (r, c, 'T'); at (row, col); }
X  if (gotowards (r, c, 0)) { goalr=r; goalc=c; return (1); }
X  return (0);
X}
X
X/*
X * lightroom: Try to light up the room if we are below level 17.
X */
X
Xlight ()
X{ if (Level < 17) return (0);
X  return (lightroom ());
X}
X
X/*
X * shootindark: If we are arching at an old monster, fire another arrow.
X */
X
Xshootindark ()
X{ register int obj, bow;
X
X  /* If no longer arching in the dark, fail */
X  if (darkturns < 1 || darkdir == NONE || !darkroom ()) return (0);
X  
X  darkturns--;			/* Count off turns till he reaches us */
X
X  /* If he is one turn away, switch back to our sword */
X  if (!cursedweapon && wielding (thrower) && darkturns==0 && handleweapon ())
X  { dwait (D_BATTLE, "Switching to sword [4]"); return (1); }
X
X  /* If we have room, switch to our bow */
X  if (!cursedweapon && !wielding (thrower) && darkturns > 3 && 
X      (bow = havebow (1, NOPRINT)) != NONE && wield (bow))
X    return (1);
X
X  /* Fail if we have run out of arrows */
X  if ((obj = havemissile ()) < 0) return (0);
X
X  /* Throw the arrow in the arching direction */
X  return (throw (obj, darkdir));
X}
X
X/* 
X * dinnertime: Eat if we are hungry or if we have a surplus of food.
X */
X 
Xdinnertime ()
X{
X  if ((havefood (5) && objcount == maxobj && ! droppedscare) ||
X      (larder > 0 && hungry ()))
X  { return (eat ()); }
X  
X  return (0);
X}
X
X/* 
X * trywand: Zap a blank wall with an unknown and unused wand in an attempt
X * to generate a message which identifies the wand.
X */
X
Xtrywand ()
X{ register int obj, dir, r, c, count;
X
X  /* If we arent in a room, if there are monsters around,  */
X  /* or we are in the dark, then we cant try this strategy */
X  if (!on (ROOM) || mlistlen || darkroom ()) return (0);
X
X  /* Have we a wand to identify? */
X  if ((obj = unknown (wand)) < 0)
X    return (0);
X
X  /* Look for a wall either 3 or 4 away */
X  for (dir = 0; dir < 8; dir += 2)
X  { for (count = 0, r=atrow, c=atcol;
X         onrc (CANGO | DOOR, r, c) == CANGO;
X         r += deltr[dir], c += deltc[dir])
X      count++;
X
X    if (count == 4 || count == 5) break;	/* Found a likely wall */
X  }
X
X  /* If we couldnt find room, then fail */
X  if (dir > 7) return (0);
X
X  /* Set to do a reset inventory (usesynch) and point the wand */
X  usesynch = 0;  
X  return (point (obj, dir));
X}
X
X/*
X * eat: If we have food, eat it.
X */
X
Xeat ()
X{ int obj;
X
X  if ((obj = have (food)) != NONE)
X  { command (T_HANDLING, "e%c", LETTER (obj));
X    return (1);
X  }
X
X  return (0);
X}
/
echo 'Part 07 of Rog-O-Matic XIV complete.'
exit



More information about the Comp.sources.unix mailing list