[net.games.hack] Wishing

mlm@cmu-cs-cad.ARPA (Michael Mauldin) (03/08/85)

In the interest of everyone's getting a genuine Amulet of Yendor out of
the Hack game, so we can all get back to REAL work, here is a copy of
the "wish" program.  To use it, compile it like so:

	cc -o wish wish.c

Then type something like

	wish -n20 -w'+3 crysknife'

Which loosely translated means: "Play 20 games of hack as a wizard,
checking your wands until you get a wand of wishing.  Then wish for a
+3 crysknife and save the game for me."

Simple, no?  The output looks like this:

	% wish -n20 -w'+3 crysknife'
	Started game 1
	b => 'The light here seems better now.'
	c => 'That is a silly thing to zap.'
	Child process terminated
	
	Started game 2
	b => 'The light here seems better now.'
	c => 'The light here seems better now.'
	Child process terminated
	
	Started game 3
	b => 'What direction?'
	c => 'What direction?'
	Child process terminated
	
	Started game 4
	b => 'What direction?'
	c => 'You may wish for an object. What do you want? '
        Saving game...successful.  Happy hacking!

Warning:  This is not a complicated, well thought out, bullet proof
program.  Before running it, make certain that you don't have a game
saved. Wish may hang if a monster comes up and starts beating on your
character while wish is fooling around with wands.  An interrupt sent
to the wish program will send a hangup signal to the hack process, so 
after you interrupt it, you need to check again that you don't have a
game saved.

Disclaimer:  Don't complain about the situations which cause it to hang
unless you have code to fix the problem.  In that case, post both.

Remember the best scores are obtained by having multiple amulets and
leaving with a strength of 18/** and a pack full of nothing but
diamonds.  Be sure to wish for "3 diamonds" every time.  One amulet and
a pack with 260 diamonds (!) was worth slightly over 1 million points 
for my speleologist.  To get multiple amulets, look for a trap door on
level 25, and then go through it a lot.

Michael L. Mauldin (Fuzzy)		Department of Computer Science
Mauldin@CMU-CS-CAD.ARPA			Carnegie-Mellon University
(412) 578-3065				Pittsburgh, PA  15213

#!/bin/sh
echo 'Start of Wish Distribution:'
echo 'x - wish.c'
sed 's/^X//' > wish.c << '/'
X/*****************************************************************
X *
X * Wish: repeatedly play hack as a wizard until you get a wand of wishing.
X *
X * Usage: wish [-n<games>] [-w'<item>']
X * Bugs: may hang if monsters intervene while checking wands.
X *
X * HISTORY
X * 28-Feb-85  Michael Mauldin (mlm) at Carnegie-Mellon University
X *	Created.
X *****************************************************************/
X
X# include <stdio.h>
X# include <signal.h>
X
X# define HACK	"/usr/games/hack"	/* Hack shell script */
X
X# define READ	0
X# define WRITE	1
X# define ESC	'\033'
X# define SKIPARG	while (*++(*argv)); --(*argv)
X
Xint child = 0;
X
Xint   fhack, thack;
XFILE  *tohack, *fromhack;
X
X# ifdef HACKLOG
XFILE *hacklog;
X# endif
X
Xmain (argc, argv)
Xint   argc;
Xchar *argv[];
X
X{ int   ptc[2], ctp[2];
X  int   game=0, games=50, hupchild();
X  char  *wish = "+6 plate mail", hackmess[1024];
X
X  while (--argc > 0 && (*++argv)[0] == '-')
X  { while (*++(*argv))
X    { switch (**argv)
X      { case 'n': games = atoi (*argv+1); SKIPARG; break;
X        case 'w': wish = *argv+1; SKIPARG; break;
X        default:  
X	  quit (1, "Usage: wish [-n<games>] [-w'<item to wish for>']\n");
X      }
X    }
X  }
X
X  putenv ("TERM", "h19");
X
X  if ((pipe (ptc) < 0) || (pipe (ctp) < 0))
X  { fprintf (stderr, "Cannot get pipes!\n");
X    exit (1);
X  }
X
X  thack = ptc[WRITE];
X  fhack = ctp[READ];
X
X  tohack = fdopen (thack, "w");
X  setbuf (tohack, NULL);
X
X  fromhack = fdopen (fhack, "r");
X  setbuf (fromhack, NULL);
X
X# ifdef HACKLOG
X  hacklog = fopen ("hack.log", "a");
X  setbuf (hacklog, NULL);
X# endif
X
X  signal (SIGINT, hupchild);
X  signal (SIGQUIT, hupchild);
X  signal (SIGHUP, hupchild);
X  signal (SIGPIPE, hupchild);
X
X  for (game=1; game <= games; game++)
X  {
X    /* Child process */
X    if ((child = fork ()) == 0)
X    { close (0);
X      dup (ptc[READ]);
X      close (1);
X      dup (ctp[WRITE]);
X  
X      execl ("/bin/sh", "sh", "-c", HACK, 0);
X      fprintf (stderr, "Could not execute hack!\n");
X      _exit (1);
X    }
X  
X    /* Parent Process */
X    else
X    { printf ("Started game %d\n", game);
X      fflush (stdout);
X
X      waitfor ("[TSFKCW] ");
X      fprintf (tohack, "w\n");
X
X      fprintf (tohack, "zb");
X      waitfor (" or ?*]? ");
X      skip (4);
X      readstr (hackmess, ESC);
X      printf ("b => '%s'\n", hackmess);
X      fflush (stdout);
X      if (stlmatch (hackmess, "You may wish for an object"))
X      { fprintf (tohack, "%s\nS", wish);
X	printf ("Saving game...");
X	fflush (stdout);
X	wait (0);
X	printf ("successful.  Happy hacking!\n");
X	exit (0);
X      }
X      else if (stlmatch (hackmess, "What direction"))
X	fprintf (tohack, "\n");
X      else
X        ;
X      
X      fprintf (tohack, "zc");
X      waitfor (" or ?*]? ");
X      skip (4);
X      readstr (hackmess, ESC);
X      printf ("c => '%s'\n", hackmess);
X      fflush (stdout);
X      if (stlmatch (hackmess, "You may wish for an object"))
X      { fprintf (tohack, "%s\nS", wish);
X	printf ("Saving game...");
X	fflush (stdout);
X	wait (0);
X	printf ("successful.  Happy hacking!\n");
X	exit (0);
X      }
X      else if (stlmatch (hackmess, "What direction"))
X	fprintf (tohack, "\n");
X      else
X        ;
X      
X      fprintf (tohack, "Qy");
X      waitfor ("-W quit on dungeon level 1");
X      wait (0);
X      printf ("Child process terminated\n\n");
X      fflush (stdout);
X    }
X  }
X  
X  printf ("No wands of wishing found after %d games.\n", game-1);
X}
X
X/****************************************************************
X * waitfor: read characters until we've read a message.  
X * Send spaces when a --More-- line is read.
X *
X * This routine should have a way of telling that the message was 
X * missed or is not coming.  Don't yell at me about it, fix it and
X * re-post it!
X ****************************************************************/
X
Xwaitfor (mess)
Xchar *mess;
X{ register char *m = mess, *n = "--More--";
X  register int ch;
X
X# ifdef HACKLOG
X  fprintf (hacklog, "\nWaitfor '%s':\n", mess);
X# endif
X
X  while (*m)
X  { if ((ch = fgetc (fromhack)) == *m) m++;
X    else m = mess;
X
X    if (ch == *n) n++;
X    else n = "--More--";
X    
X    if (*n=='\0') fprintf (tohack, " ");
X
X# ifdef HACKLOG
X    fputc (ch, hacklog);
X# endif
X  }
X}
X/****************************************************************
X * skip: Read (and ignore) 'n' characters of input.
X ****************************************************************/
X
Xskip (n)
Xint n;
X{
X  while (n-- > 0)
X    fgetc (fromhack);
X}
X
X/****************************************************************
X * readstr: Read the next string from the input, terminate by
X * the 'end' character (usually an ESC for an H19). 
X ****************************************************************/
X
Xreadstr (str, end)
Xchar *str;
Xint   end;
X{
X  while ((*str = fgetc (fromhack)) != end) str++;
X  *str = '\0';  
X}
X
X/****************************************************************
X * hupchild: if the user interrrupts the parent, lets try to
X * get the child to terminate gracefully.  Use the HUP signal
X * to the Hack process, which should result in the game's being saved.
X ****************************************************************/
X
Xhupchild ()
X{ if (child != 0)
X  { kill (child, 1);
X    wait (0);
X    printf ("Child process terminated by hangup.\n");
X    exit (0);
X  }  
X}
X
X# ifndef CMU
X/*  stlmatch  --  match leftmost part of string
X *
X *  Usage:  i = stlmatch (big,small)
X *	int i;
X *	char *small, *big;
X *
X *  Returns 1 iff initial characters of big match small exactly;
X *  else 0.
X *
X *  HISTORY
X * 20-Nov-79  Steven Shafer (sas) at Carnegie-Mellon University
X *	Rewritten for VAX from Ken Greer's routine.
X *
X *  Originally from klg (Ken Greer) on IUS/SUS UNIX
X */
X
X#include <c.h>
X
Xint stlmatch (big,small)
Xchar *small, *big;
X{
X	register char *s, *b;
X	s = small;
X	b = big;
X	do {
X		if (*s == '\0')  return (TRUE);
X	} 
X	while (*s++ == *b++);
X	return (FALSE);
X}
X
X/*  putenv  --  put value into environment
X *
X *  Usage:  i = putenv (name,value)
X *	int i;
X *	char *name, *value;
X *
X *  Putenv associates "value" with the environment parameter "name".
X *  If "value" is 0, then "name" will be deleted from the environment.
X *  Putenv returns 0 normally, -1 on error (not enough core for malloc).
X *
X *  Putenv may need to add a new name into the environment, or to
X *  associate a value longer than the current value with a particular
X *  name.  So, to make life simpler, putenv() copies your entire
X *  environment into the heap (i.e. malloc()) from the stack
X *  (i.e. where it resides when your process is initiated) the first
X *  time you call it.
X *
X *  HISTORY
X * 20-Nov-79  Steven Shafer (sas) at Carnegie-Mellon University
X *	Created for VAX.  Too bad Bell Labs didn't provide this.  It's
X *	unfortunate that you have to copy the whole environment onto the
X *	heap, but the bookkeeping-and-not-so-much-copying approach turns
X *	out to be much hairier.  So, I decided to do the simple thing,
X *	copying the entire environment onto the heap the first time you
X *	call putenv(), then doing realloc() uniformly later on.
X *	Note that "putenv(name,getenv(name))" is a no-op; that's the reason
X *	for the use of a 0 pointer to tell putenv() to delete an entry.
X *
X */
X
X#define EXTRASIZE 5		/* increment to add to env. size */
X
Xchar *index(), *malloc(), *realloc();
Xint strlen();
X
Xstatic int envsize = -1;	/* current size of environment */
Xextern char **environ;		/* the global which is your env. */
X
Xstatic int findenv();		/* look for a name in the env. */
Xstatic int newenv();		/* copy env. from stack to heap */
Xstatic int moreenv();		/* incr. size of env. */
X
Xint putenv (name,value)
Xchar *name, *value;
X{
X	register int i,j;
X	register char *p;
X
X	if (envsize < 0) {	/* first time putenv called */
X		if (newenv() < 0)	/* copy env. to heap */
X			return (-1);
X	}
X
X	i = findenv (name);	/* look for name in environment */
X
X	if (value) {		/* put value into environment */
X		if (i < 0) {	/* name must be added */
X			for (i=0; environ[i]; i++) ;
X			if (i >= (envsize - 1)) {	/* need new slot */
X				if (moreenv() < 0)  return (-1);
X			}
X			p = malloc (strlen(name) + strlen(value) + 2);
X			if (p == 0)	/* not enough core */
X				return (-1);
X			environ[i+1] = 0;	/* new end of env. */
X		}
X		else {		/* name already in env. */
X			p = realloc (environ[i],
X				strlen(name) + strlen(value) + 2);
X			if (p == 0)  return (-1);
X		}
X		sprintf (p,"%s=%s",name,value);	/* copy into env. */
X		environ[i] = p;
X	}
X	else {			/* delete name from environment */
X		if (i >= 0) {	/* name is currently in env. */
X			free (environ[i]);
X			for (j=i; environ[j]; j++) ;
X			environ[i] = environ[j-1];
X			environ[j-1] = 0;
X		}
X	}
X
X	return (0);
X}
X
Xstatic int findenv(name)
Xchar *name;
X{
X	register char *namechar, *envchar;
X	register int i,found;
X
X	found = 0;
X	for (i=0; environ[i] && !found; i++) {
X		envchar = environ[i];
X		namechar = name;
X		while (*namechar && (*namechar == *envchar)) {
X			namechar++;
X			envchar++;
X		}
X		found = (*namechar == '\0' && *envchar == '=');
X	}
X	return (found ? i-1 : -1);
X}
X
Xstatic int newenv ()
X{
X	register char **env, *elem;
X	register int i,esize;
X
X	for (i=0; environ[i]; i++) ;
X	esize = i + EXTRASIZE + 1;
X	env = (char **) malloc (esize * sizeof (elem));
X	if (env == 0)  return (-1);
X
X	for (i = 0; environ[i]; i++) {
X		elem = malloc (strlen(environ[i]) + 1);
X		if (elem == 0)  return (-1);
X		env[i] = elem;
X		strcpy (elem,environ[i]);
X	}
X
X	env[i] = 0;
X	environ = env;
X	envsize = esize;
X	return (0);
X}
X
Xstatic int moreenv()
X{
X	register int esize;
X	register char **env;
X
X	esize = envsize + EXTRASIZE;
X	env = (char **) realloc (environ,esize * sizeof(*env));
X	if (env == 0)  return (-1);
X	environ = env;
X	envsize = esize;
X	return (0);
X}
X# endif
/
echo 'Wish Distribution complete.'
exit