[net.sources.games] Robots for XENIX Rel 3.0

cem@intelca.UUCP (Chuck McManis) (06/13/85)

Included below is the original source to robots modified so that it will
run on XENIX Release 3.0 (A System III clone) Hopefully all of the 
changes should be reversable by not defining XENIX, as I don't have
access to a non XENIX machine I can't check it. 

THIS IS NOT A SHAR ARCHIVE, just the source to the robots game. Remove the
text between the -Cut Here- and the signature at the end of the file
and compile it with cc -O robots.c -o robots -lcurses -ltermcap
then install it in the /usr/games directory. The man page is in the original
posting.

--Chuck

-------------------------Cut Here------------------------------------------

/* Written by Allan R. Black, Strathclyde University.
 * This program may be freely distributed provided that
 * this comment is not removed and that
 * no profit is gained from such distribution.
 */

/*
 *     @(#)robots.c    1.7     3/21/84
 */

/* 1.7a modified by Stephen J. Muir at Lancaster University. */
/* 1.7b adapted to Xenix Rel 3.0 by Chuck McManis */

# define XENIX

# include <curses.h>
# include <signal.h>
# include <pwd.h>
# include <ctype.h>
# ifdef XENIX
# include <sys/types.h>
# endif
# include <sys/file.h>

# ifdef XENIX       /* Xenix defaults to 16 bit ints */
# define LONG long  
# define SFMT %ld
# else
# define SFMT %d
# define LONG
# endif

# MIN_ROBOTS    10
# define MAX_ROBOTS    500
# define MIN_VALUE     10
# define MAX_FREE      3

# define VERT  '|'
# define HORIZ '-'
# define ROBOT '='
# define SCRAP '@'
# define ME    'I'
# define MUNCH '*'
# define DOT   '.'

# define LEVEL         (level+1)

# define MSGPOS                39
# define RVPOS         51

# define HOF_FILE      "/usr/games/lib/robots_hof"
# define TMP_FILE      "/usr/games/lib/robots_tmp"

# define NUMSCORES     20
# define NUMNAME       "Twenty"

# define TEMP_DAYS     7
# define TEMP_NAME     "Week"

# define MAXSTR                100

extern char    *getlogin ();
extern struct passwd   *getpwnam ();

struct scorefile {
       int     s_uid;
       LONG int s_score;
       char    s_name[MAXSTR];
       bool    s_eaten;
       int     s_lev    int     s_days;
};

# define FILE_SIZE     (NUMSCORES*sizeof(struct scorefile))

/*# define ALLSCORES*/

# define SECSPERDAY    86400

# define BEL   007
# define CTLH  010
# define CTLJ  012
# define CTLK  013
# define CTLL  014
# define CTLY  031
# define 25
# define CTLB  002
# define CTLN  016
# define CTLR  022

char   whoami[MAXSTR];

int    my_x, my_y;
int    new_x, new_y;

int    level = 0;
LONG int    score = 0;
int    free_teleports = 0;
int    free_per_level = 1;
bool   dead = FALSE;
bool   last_stand;

int    count;
char   com, cmd_ch;
bool   running, adjacent, first_move, bad_move;
int    dots = 0;

int    robot_value = MIN_VALUE;
int    max_robots = MIN_ROBOTS;
int    robots_alive;
struct passwd  *pass;

struct robot {
       bool    alive;
       int     x;
       int     y;
} robot_list[MAX_ROBOTS];

LONG int seed;

#ifndef XENIX
struct ltchars ltc;
#endif

char   dsusp;

int   r();
int    interrupt();

char   *forbidden [] =
       { "root",
         "daemon",
         "uucp",
         "pop",
         "ingres",
         "demo",
         "chessel",
         "saturn",
         "sjm",
         "cosmos",
         0
       };

main(argc,argv)
       int argc;
       char *argv[];
{
       register char *x, **xx;
       if(argc > 1) {
               if(argv[1][0] == '-') {
                       switch(argv[1][1]) {
                       case 's':
                               scoring(FALSE);
                               exit(0);
                       }
               }
       }
       if ((x = getlogin ()) == 0 || (pass = getpwnam (x)) == 0)
       { printf ("Who the hell are you?\n");
         exit (1);
       }
       strcpy(whoami,x);
       for (xx = forbidden; *xx; ++xx)
               if (strcmp (whoami, *xx) == 0)
               { printf ("only individuals may play robots\n");
                 exit (1);
               }
       seed = time(0)+pass->pw_uid;
       signal(SIGQUIT,interrupt);
       signal(SIGINT,interrupt);
       initscr();
       crmode();
       noecho();
# ifndef XENIX    /* Xenix doesn't support suspend yet */
       ioctl(1,TIOCGLTC,&ltc);
       dsusp = ltc.t_dsuspc;
       ltc.t_dsustc.t_suspc;
       ioctl(1,TIOCSLTC,&ltc);
# endif
       for(;;) {
               count = 0;
               running = FALSE;
               adjacent = FALSE;
               last_stand = FALSE;
               if(rnd(free_per_level) < free_teleports) {
                       free_per_level++;
                       if(free_per_level > MAX_FREE) free_per_level = MAX_FR            }
               free_teleports += free_per_level;
               leaveok(stdscr,FALSE);
               draw_screen();
               put_robots();
               do {
                       my_x = rndx();
                       my_y = rndy();
                       move(my_y,my_x);
               } while(winch(stdscr) != ' ');
               addch(ME);
               for(;;) {
                       scorer();
                       if(robots_alive == 0) break;
                       command();
                       robots();
                       if(dead) munch();
               }
               if (!level)
                 ice (2);
#ifdef XENIX
               mvprintw(LINES-2,MSGPOS,"%d robots are now scrap heaps",max_robots);
#else
               msg("%d robots are now scrap heaps",max_robots);
#endif
               leaveok(stdscr,FALSE);
               move(my_y,my_x);
               refresh();
               readchar();
               level++;
       }
}

draw_screen()
{
       register int x, y;
       clear();
       for(y = 1; y < LINES-2; y++) {
               mvaddch(y,0,VERT);
               mvaddch(y,COLS-1,VERT);
       }
       for(x = 0; x < COLS; x++) {
               mvaddch(0,x,HORIZ);
        mvaddch(LINES-2,x,HORIZ);
       }
}

put_robots()
{
       register struct robot *r, *end;
       register int x, y;
       robot_value += level*5+rnd(level*10);
       max_robots += level*3+rnd(level*5);
       if(max_robots > MAX_ROBOTS) max_robots = MAX_ROBOTS;
       robots_alive = max_robots;
       end = &robot_list[max_robots];
       for(r = robot_list; r < end; r++) {         for(;;) {
                       x = rndx();
                       y = rndy();
                       move(y,x);
                       if(winch(stdscr) == ' ') break;
               }
               r->x = x;
               r->y = y;
               r->alive = TRUE;
               addch(ROBOT);
       }
}

command()
{
       register int x, y;
retry:
       move(my_y,my_x);
       refresh();
       if(last_stand) return;
       bad_move = FALSE;
       if(!running) {
               cmd_ch = read_com();
               switch(cmd_ch) {
               case CTLH:
               case CTLJ:
               case CTLK:
               case CTLL:
               case CTLY          case CTLU:
               case CTLB:
               case CTLN:
                       cmd_ch |= 0100;
                       adjacent = TRUE;
               case 'H':
               case 'J':
               case 'K':
               case 'L':
               case 'Y':
               case 'U':
               case 'B':
               case 'N':
                       cmd_ch |= 040;
                       running = TRUE;
                       first_move = TRUE;
               case 't':
               case 'T':
               case 's':
               case 'S':
               case 'm':
               case 'M':
               case '?           case 'd':
               case 'D':
               case CTLR:
                       count = 0;
               }
       }
       switch(cmd_ch) {
       case '.':
               return;
       case 'h':
       case 'j':
       case 'k':
       case 'l':
       case 'y':
       case 'u':
       case 'b':
       case 'n':
               do_move(cmd_ch);
               break;
       case '    case 'T':
       teleport:
               new_x = rndx();
               new_y = rndy();
               move(new_y,new_x);
               switch(winch(stdscr)) {
               case ROBOT:
               case SCRAP:
               case ME:
                       goto teleport;
               }
               if(free_teleports > 0) {
                       for(x = new_x-1; x <= new_x+1; x++) {
                               for(y = new_y-1; y <= new_y+1; y++) {
                                       move(y,x);
                                       if(winch(stdscr) == ROBOT) goto teleport;
                               }
                       }
                       feports--;
               }
               break;
       case 's':
       case 'S':
               last_stand = TRUE;
               leaveok(stdscr,TRUE);
               return;
       case 'm':
       case 'M':
       case '?':
               good_moves();
               goto retry;
       case 'd':
       case 'D':
               if(dots < 2) {
                       dots++;
                       put_dots();
               } else {
                       erase_dots();
                       dots = 0;
               }
               goto retry;
       case 'q':
       case 'Q':
               quit(FALSE);
       case CTLR:
               clearok(curscr,TRUE);
               wrefresh(curscr);
               goto retry;
       default:
               bad_move = TRUE;
       }
       if(bad_move) {
               if(running) {
                       if(first_move) putchar(BEL);
                       running = FALSE;
                       adjacent = FALSE;
                       first_mALSE;
               } else {
                       putchar(BEL);
               }
               refresh();
               count = 0;
               goto retry;
       }
       first_move = FALSE;
       mvaddch(my_y,my_x,' ');
       my_x = new_x;
       my_y = new_y;
       move(my_y,my_x);
       if(winch(stdscr) == ROBOT) munch();
       mvaddch(my_y,my_x,ME);
       refresh();
}

read_com()
{
       if(count == 0) {
               if (dots)
               { put_dots ();
                 move (my_y, my_x);
                 refresh ();
               }
         f(isdigit(com = readchar())) {
                       count = com-'0';
                       while(isdigit(com = readchar())) count = count*10+com-'0';
               }
               if (dots)
                       erase_dots ();
       }
       if(count > 0) count--;
       return(com);
}

readchar()
{
       static char buf[1];
       while(read(0,buf,1) <= 0);
       return(buf[0]);
}

do_move(dir)
       char dir;
{
       register int x, y;
       new_x = my_x+xinc(dir);
       new_y = my_y+yinc(dir);
       move(new_y,new_x);
       switch(winch(stdscr)) {
       case VERT:
       case HORIZ:
       case SCRAP:
               bad_move = TRUE;
       }
       if(adjacent & !first_move) {
               for(x = new_x-1; x <= new_x+1;                        for(y = new_y-1; y <= new_y+1; y++) {
                               move(y,x);
                               switch(winch(stdscr)) {
                               case ROBOT:
                               case SCRAP:
                                       bad_move = TRUE;
                                       return;
                               }
                       }
               }
       }
}

put_dots()
{
       register int x, y;
       for(x = my_x-dots; x <= my_x+dots; x++) {
               if (x < 1 || x > COLS - 2)
                       continue;
               for(y = my_y-dots; y <= my_y+dots; y++) {
                       if (y < 1 || y > LINES - 3)
                               continue;
                       move(y,x);
                       if(winch(stdscr) == ' ') addch(DOT);
               }
       }
}

erase_dots()
{
       register int x, y;
       for(x = my_x-dots; x <= my_x+dots; x++) {
               if (x < 1 || x > COLS - 2)
                continue;
               for(y = my_y-dots; y <= my_y+dots; y++) {
                       if (y < 1 || y > LINES - 3)
                               continue;
                       move(y,x);
                       if(winch(stdscr) == DOT) addch(' ');
               }
       }
}

good_moves()
{
       register int x, y;
       register int test_x, test_y;
       register char *m, *a;
       static char moves[] = "hjklyubn.";
       static char ans[sizeof moves];
       a = ans;
       for(m = moves; *m; m++) {
               test_x = my_x+xinc(*m);
               test_y = my_*m);
               move(test_y,test_x);
               switch(winch(stdscr)) {
               case ' ':
               case ME:
                       for(x = test_x-1; x <= test_x+1; x++) {
                               for(y = test_y-1; y <= test_y+1; y++) {
                                       move(y,x);
                                       if(winch(stdscr) == ROBOT) goto bad;
                               }
                       }
                       *a++ = *m;
               }
       bad:;
       }
       *a = 0;
       if(ans[0]) {
               a = ans;
       } else {
               a = "Forget it!";
       }
       mvprintw(LINES-1,MSGPOS,",RVPOS-MSGPOS,RVPOS-MSGPOS,a);
}

xinc(dir)
       char dir;
{
       switch(dir) {
       case 'h':
       case 'y':
       case 'b':
               return(-1);
       case 'l':
       case 'u':
       case 'n':
               return(1);
      j':
       case 'k':
       default:
               return(0);
       }
}

yinc(dir)
       char dir;
{
       switch(dir) {
       case 'k':
       case 'y':
       case 'u':
               return(-1);
       case 'j':
       case 'b':
       case 'n':
               return(1);
       case 'h':
       case 'l':
       default:
               return(0);
       }
}

robots()
{
       register struct robot *r, *end, *find;
       end = &robot_list[max_robots];
       for(r = robot_list; r < end; r++) {
               if(r->alive) {
                       mvaddch(r->y,r->x,' ');
               }
       }
       for(r = robot_list; r < end; r++) {
        if(r->alive) {
                       r->x += sign(my_x-r->x);
                       r->y += sign(my_y-r->y);
                       move(r->y,r->x);
                       switch(winch(stdscr)) {
                       case ME:
                               addch(MUNCH);
                               dead = TRUE;
                            k;
                       case SCRAP:
                               r->alive = FALSE;
                               score += robot_value;
                               robots_alive--;
                               break;
                       case ROBOT:
                               for(find = robot_list; find < r; find++) {
                                       if(find->alive) {
                                               if(r->x == find->x && r->y == find->y) {
                                                       find->alive = FALSE;
                                                       break;
                                        }
                                       }
                               }
                               r->alive = FALSE;
                               addch(SCRAP);
                               score += robot_value*2;
                               robots_alive -= 2;
                               break;
                       case MUNCH:
                               break;
                       default:
                               addch(ROBOT);
                       }
               }
       }
}

munch()
{
       scorer();
#ifdef XENIX
       mvprintw(LINES-2,MSGPCH! You're robot food");
#else
       msg("MUNCH! You're robot food");
#endif
       leaveok(stdscr,FALSE);
       leaveok(stdscr,FALSE);
       mvaddch(my_y,my_x,MUNCH);
       move(my_y,my_x);
       refresh();
       readchar();
       quit(TRUE);
}

quit(eaten)
       bool eaten;
{
       move(LINES-1,0);
       refresh();
       endwin();
       putchar('\n');
#ifndef XENIX
       ltc.t_dsuspc = dsusp;
       ioctl(1,TIOCSLTC,&ltc);
#endif
       scoring(eaten);
       exit(0);
}

scoring(eaten)
       bool eaten;
{
       static char buf[MAXSTR];
       sprintf(buf,"for this %s",TEMP_NAME);
       record_score(eaten,TMP_FILAYS,buf);
       printf("[Press return to continue]");
       fflush(stdout);
       gets(buf);
       record_score(eaten,HOF_FILE,0,"of All Time");
}

record_score(eaten,fname,max_days,type_str)
       bool eaten;
       char *fname;
       int max_days;
       char *type_str;
{
       int fd;
       int (*action)();
       action = signal(SIGINT,SIG_IGN);
       if((fd = open(fname,2)) < 0) {
        perror(fname);
       } else {
#ifdef XENIX 
               do_score(eaten,fd,max_days,type_str);
#else
               if(flock(fd,LOCK_EX) < 0) {
                       perror(fname);
               } else {
                       do_score(eaten,fd,max_days,type_str);
                       flock(fd,LOCK_UN);
               }
#endif
               close(fd);
       }
       signal(SIGINT,action);
}

do_score(eaten,fd,max_days,type_str)
       bool eaten;
       int fd, max_days;
       char *type_str;
{
       register struct scorefile *position;
       register int x;
       register struct scorefile *remove, *sfile,      struct scorefile *oldest, *this;
       char *so, *ts;
       int uid, this_day, limit;
       static char buf[20];
       ts = buf;
       this_day = max_days ? time(0)/SECSPERDAY : 0;
       limit = this_day-max_days;
       sfile = (struct scorefile *)(malloc(FILE_SIZE));
       eof = &sfile[NUMSCORES];
       this = 0;
       for(position = sfile; position < eof; position++) {
               position->s_score                position->s_days = 0;
       }
       read(fd,sfile,FILE_SIZE);
       remove = 0;
       if(score > 0) {
               uid = pass->pw_uid;
               oldest = 0;
               x = limit;
               for(position = eof-1; position >= sfile; position--) {
                       if(position->s_days < x) {
                               x = position->s_days;
                               oldest = position;
                       }
               }
               position = 0;
               for(remove = sfile; remove < eof; remove++) {
                       if(position == 0 && score > remove->s_score) position = remove;
# ifndef ALLSCORES
                       if (remove->s_uid == uid)
                       { if (remove->s_days < limit)
                               oldest = remove;
                         else
                               break;
                       }
# endif ALLSCORES
               }
               if(remove < eof) {
                       if(position == 0 && remove->s_days < limit) position = remove;
               } else if(oldest) {
                       remove = oldest;
                       if(position == 0) {
                               position = eof-1;
                       } else if(remove < position) {
                               position--;
                       }
               } else if(position) {
                       remove = eof-1;
               }
               if(position) {
                       if(remove < position) {
                               while(remove < position) {
                                       *remove = *(r;
                                       remove++;
                               }
                       } else {
                               while(remove > position) {
                                       *remove = *(remove-1);
                                       remove--;
                               }
                       }
                       position->s_score = score;
                       strncpy(position->s_name,whoami,MAXSTR);
                       position->s_eaten = eaten;
                       position->s_level = LEVEL;
                       position->s_uid = uid;
                       position->s_days = this_day;
                       this = position;
#ifdef XENIX
                       lseek(fd,0L,0);
#else
                       lseek(fd,0,0);
#endif
                       write(fd,sfile,FILE_SIZE);
                       close(fd);
               }
       }
       printf(
# ifdef ALLSCORES
               "\nTop %s Scores %s:\n",
# else ALLSCORES
               "\nTop %s Robotists %s:\n",
# endif ALLSCORES
               NUMNAME,
               type_str
       );
       printf("Rank   Score    Name\n");
       count = 0;
       for(position = sfile; position < eof; position++) {
               if(position->s_score == 0) break;
               if(position == this && SO && SE) {
                 tputs(SO,0,_putchar);
               }
               if (position->s_days >= limit)
               { 
#ifdef XENIX
	       printf ("%-6d %-8ld %s: %s on level %d.",
#else
	       printf ("%-6d %-8d %s: %s on level %d.",
#endif  
                         ++count,
                         position->s_score,
                         position->s_name,
                         position->s_eaten ? "eaten" : "chickened out",
                         position->s_level
                        );
# ifdef OLDSCORES
               } else
               {
# ifdef XENIX
		 printf ("**OLD** %-8ld %s: %s on level %d.",
# else
		 printf ("**OLD** %-8d %s: %s on level %d.",
# endif
                         position->s_score,
                         position->s_name,
                         position->s_eaten ? "eaten" : "chickened out",
                         position->s_level
                        );
# endif OLDSCORES
               }
               tion == this && SO && SE) {
                       tputs(SE,0,_putchar);
               }
# ifndef OLDSCORES
               if (position->s_days >= limit)
# endif OLDSCORES
                       putchar ('\n');
       }
}

scorer()
{
       static char     infobuf [6];
       register int    x, y;
       if ((y = free_teleports-free_per_level)              y = 0;
       x = free_teleports-y;
       sprintf(infobuf,"%d+%d",x,y);
       if (y == 0)
               infobuf[1] = '\0';
       move(LINES-1,0);
       clrtoeol();
#ifdef XENIX
       printw("<%s> level: %d    score: %ld",infobuf,LEVEL,score)e 
       printw("<%s> level: %d    score: %d",infobuf,LEVEL,score);
#endif
       mvprintw(LINES-1,RVPOS,"robots: %d    value: %d",robots_alive,robot_value);
}

rndx()
{
       return(rnd(COLS-2)+1);
}

rndy()
{
       return(rnd(LINES-3)+1);
}

rnd(mod)
       int mod;
{
       if(mod <= 0) return(0);
       return((((seed = seed*11109+13849) >> 16) & 0xffff) % mod);
}

sign(x)
       register int x;
{
       if(x < 0) return(-1);
       return(x > 0);
}

#ifndef XENIX /* XENIX can't resolve the reference to _doprnt */
msg(message,args)
       char *message;
       int args;
{
       reint x;
       static FILE msgpr;
       static char msgbuf[1000];
       msgpr._flag = _IOSTRG | _IOWRT;
       msgpr._ptr = msgbuf;
       msgpr._cnt = 1000;
       _doprnt(message,&args,&msgpr);
       putc(0,&msgpr);
       mvaddstr(LINES-1,MSGPOS,msgbuf);
       clrtoeol();
       refresh();
}
#endif

interrupt()
{
       quit(FALSE);
}
-- 
                                            - - - D I S C L A I M E R - - - 
{ihnp4,fortune}!dual\                     All opinions expressed herein are my
        {qantel,idi}-> !intelca!cem       own and not those of my employer, my
 {ucbvax,hao}!hplabs/                     friends, or my avocado plant. :-}

guy@sun.uucp (Guy Harris) (06/17/85)

A lot of the changes "ifdef"fed with XENIX reflect the way the code should
have been in the first place:

1) Contrary to popular belief, "int"s are not guaranteed to be 32 bits long.
All of the variables that are declared with LONG in the XENIX version should
be declared as long in *all* versions (yes, even under 4.xBSD).  All
"printf" and "scanf" formats that print or read into these variables should
use "%ld" format, not "%d".  The second argument to "lseek" should always be
a "long", so you should say

	lseek(fd,0L,0);

not

	lseek(fd,0,0);

even on 4.xBSD systems.

2) Contrary to popular belief, not all UNIX systems have "_doprnt".  System
III doesn't have it; PDP-11 V7 and System Vs Releases 1 and 2 have it, but
don't document it; 4.2BSD has it and made the mistake of documenting it.
Zilog's ZEUS doesn't have it because the Zilog calling sequence makes
routines that take a variable number of arguments a pain to implement.

If you take Chuck McManus' corrected version and:

	Change all the "ifdef"fed sections involving "int" vs. "long int"
	stuff or the "msg" routine to *only* contain the code compiled if
	XENIX is defined,

	Change the section that reads

		# ifdef XENIX
		# include <sys/types.h>
		# endif
		# include <sys/file.h>

	to read

		# ifdef BSD4_2
		# include <sys/file.h>
		# endif

	because the inclusion of "sys/file.h" seems to be solely for
	the benefit of the "flock" system call which only exists on
	4.2BSD,

	Change all other occurrences of # ifdef XENIX to # ifndef BSD4_2,
	because all the other # ifdef XENIXes are there to conditionally
	compile in code using 4.2BSD features (if you feel *really*
	ambitious, you can throw in #ifdefs for the file-locking stuff to
	use the various flavors of "lockf" derived from the original John
	Bass version for those versions of UNIX that support it),

	And run the whole thing through "lint" to catch trash like

	       seed = time(0)+pass->pw_uid;

	and replace it with the *correct* version which is

	       seed = time((time_t *)0)+pass->pw_uid;

	(I'd classify this as a venial sin, except that I've fixed
	that kind of stuff enough times on CCI's 16-bit-"int"/32-bit-pointer
	machines that I think it deserves to be a mortal sin).

Could people take a little more care *NOT* to make assumptions about the
version of UNIX or machine they're writing for when writing code?  If so,
there'll be a lot fewer "has anybody gotten 'Hump The Hostess' running under
{V7,4.1BSD,4.2BSD,System III,System V,Xenix,Zeus,Penix}" questions, and a
lot fewer repeated postings of programs until a portable version emerges.

	Guy Harris

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (06/19/85)

> there'll be a lot fewer "has anybody gotten 'Hump The Hostess' running under
> {V7,4.1BSD,4.2BSD,System III,System V,Xenix,Zeus,Penix}" questions, and a

Hey, I want a copy.