diff options
author | mrg <mrg@NetBSD.org> | 1997-10-04 09:00:13 +0000 |
---|---|---|
committer | mrg <mrg@NetBSD.org> | 1997-10-04 09:00:13 +0000 |
commit | e9152f6d21d5a8b1e02922e0fc95b71fc21cbd92 (patch) | |
tree | 3e1b32f0c0c61414ebd853c92584cd9d95a99acc /hunt/hunt | |
parent | 6f367f8f8be268d527e585867c1c42ffbb07668c (diff) | |
download | bsdgames-darwin-e9152f6d21d5a8b1e02922e0fc95b71fc21cbd92.tar.gz bsdgames-darwin-e9152f6d21d5a8b1e02922e0fc95b71fc21cbd92.tar.zst bsdgames-darwin-e9152f6d21d5a8b1e02922e0fc95b71fc21cbd92.zip |
hunt version 1993-07-17
Diffstat (limited to 'hunt/hunt')
-rw-r--r-- | hunt/hunt/connect.c | 41 | ||||
-rw-r--r-- | hunt/hunt/hunt.6 | 378 | ||||
-rw-r--r-- | hunt/hunt/hunt.c | 1072 | ||||
-rw-r--r-- | hunt/hunt/otto.c | 585 | ||||
-rw-r--r-- | hunt/hunt/playit.c | 628 |
5 files changed, 2704 insertions, 0 deletions
diff --git a/hunt/hunt/connect.c b/hunt/hunt/connect.c new file mode 100644 index 00000000..587f8b40 --- /dev/null +++ b/hunt/hunt/connect.c @@ -0,0 +1,41 @@ +/* + * Hunt + * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold + * San Francisco, California + */ + +# include "hunt.h" +# include <signal.h> + +do_connect(name, team, enter_status) +char *name; +char team; +long enter_status; +{ + static long uid; + static long mode; + extern char *ttyname(); + + if (uid == 0) + uid = htonl(getuid()); + (void) write(Socket, (char *) &uid, LONGLEN); + (void) write(Socket, name, NAMELEN); + (void) write(Socket, &team, 1); + enter_status = htonl(enter_status); + (void) write(Socket, (char *) &enter_status, LONGLEN); + (void) strcpy(Buf, ttyname(fileno(stderr))); + (void) write(Socket, Buf, NAMELEN); +# ifdef INTERNET + if (Send_message != NULL) + mode = C_MESSAGE; + else +# endif +# ifdef MONITOR + if (Am_monitor) + mode = C_MONITOR; + else +# endif + mode = C_PLAYER; + mode = htonl(mode); + (void) write(Socket, (char *) &mode, sizeof mode); +} diff --git a/hunt/hunt/hunt.6 b/hunt/hunt/hunt.6 new file mode 100644 index 00000000..5394e1ec --- /dev/null +++ b/hunt/hunt/hunt.6 @@ -0,0 +1,378 @@ +.\" hunt +.\" Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold +.\" San Francisco, California +.\" +.\" Copyright (c) 1985 Regents of the University of California. +.\" All rights reserved. The Berkeley software License Agreement +.\" specifies the terms and conditions for redistribution. +.\" +.TH HUNT 6 "21 August 1986" +.UC 4 +.SH NAME +hunt \- a multi-player multi-terminal game +.SH SYNOPSIS +\fB/usr/games/hunt\fP [ \fB\-qmcsfbS\fP ] [ \fB\-n\fP name ] [ \fB\-t\fP team ] [ \fB\-p\fP port ] [ \fB\-w\fP message ] [ host ] +.SH DESCRIPTION +The object of the game +.I hunt +is to kill off the other players. +There are no rooms, no treasures, and no monsters. +Instead, you wander around a maze, find grenades, trip mines, and shoot down +walls and players. +The more players you kill before you die, the better your score is. +If the +.B \-m +flag is given, +you enter the game as a monitor +(you can see the action but you cannot play). +.PP +.I Hunt +normally looks for an active game on the local network; +if none is found, it starts one up on the local host. +The location of the game may be specified by giving the +.I host +argument. +This presupposes that a hunt game is already running on that host, see +.IR huntd (6) +for details on how to setup a game on a specific host. +If more than one game if found, +you may pick which game to play in. +.PP +If the +.B \-q +flag is given, +.I hunt +queries the local network (or specific host) +and reports on all active games found. +This is useful for shell startup scripts, \fIe.g.\fP csh's .login. +.PP +The player name may be specified on the command line by using the +.B \-n +option. +.PP +The +.BR \-c , +.BR \-s , +and +.B \-f +options are for entering the game cloaked, scanning, or flying respectively. +.PP +The +.B \-b +option turns off beeping when you reach the typeahead limit. +.PP +The +.B \-t +option aids team playing by making everyone else on one's team +appear as the team name. +A team name is a single digit to avoid conflicting with other characters +used in the game. +.PP +The +.B \-p +.I port +option allows the rendezvous port number to be set. +This is a useful way for people playing on dialup lines to avoid playing +with people on 9600 baud terminals. +.PP +The +.B \-w +.I message +option is the only way to send a message to everyone else's screen when +you start up. +It is most often used to say ``eat slime death - NickD's coming in''. +.PP +When you die and are asked if you wish to re-enter the game, +there are other answers than just yes or no. +You can also reply with a +.B w +for write a message before continuing or +.B o +to change how you enter the game (cloaked, scanning, or flying). +.PP +To be notified automatically when a +.I hunt +starts up, add your login to the +.I hunt-players +mailing list (see +.IR huntd (6)). +.SH "PLAYING HINTS" +.I Hunt +only works on crt (vdt) terminals with at least 24 lines, 80 columns, and +cursor addressing. +The screen is divided in to 3 areas. +On the right hand side is the status area. +It shows damage sustained, +charges remaining, +who's in the game, +who's scanning (the +.B ``*'' +in front of the name), +who's cloaked (the +.B ``+'' +in front of the name), +and other players' scores. +The rest of the screen is taken up by your map of the maze. +The 24th line +is used for longer messages that don't fit in the status area. +.PP +.I Hunt +uses the same keys to move as +.IR vi (1) +does, +.IR i.e. , +.BR h , +.BR j , +.BR k , +and +.B l +for left, down, up, right respectively. +To change which direction you're facing in the maze, +use the upper case version of the movement key (\c +.IR i.e. , +.BR HJKL ). +You can only fire or throw things in the direction you're facing. +.TP +Other commands are: +.sp +.nf +.ta +.ta \w'>\|<\|^\|v\ \ 'u +f or 1 \- Fire a bullet (Takes 1 charge) +g or 2 \- Throw grenade (Takes 9 charges) +F or 3 \- Throw satchel charge (Takes 25 charges) +G or 4 \- Throw bomb (Takes 49 charges) +5 \- Throw big bomb (Takes 81 charges) +6 \- Throw even bigger bomb (Takes 121 charges) +7 \- Throw even more big bomb (Takes 169 charges) +8 \- Throw even more bigger bomb (Takes 225 charges) +9 \- Throw very big bomb (Takes 289 charges) +0 \- Throw very, very big bomb (Takes 361 charges) +@ \- Throw biggest bomb (Takes 441 charges) +o \- Throw small slime (Takes 15 charges) +O \- Throw big slime (Takes 30 charges) +p \- Throw bigger slime (Takes 45 charges) +P \- Throw biggest slime (Takes 60 charges) +s \- Scan (show where other players are) (Takes 1 charge) +c \- Cloak (hide from scanners) (Takes 1 charge) + +^L \- Redraw screen +q \- Quit +.fi +.TP +The symbols on the screen are: +.sp +.nf +.ta +.ta \w'>\|<\|^\|v\ \ 'u +\-\||\|+ \- walls +/\|\\ \- diagonal (deflecting) walls +# \- doors (dispersion walls) +; \- small mine +g \- large mine +: \- bullet +o \- grenade +O \- satchel charge +@ \- bomb +s \- small slime +$ \- big slime +>\|<\|^\|v \- you facing right, left, up, or down +}\|{\|i\|! \- other players facing right, left, up, or down +\(** \- explosion +.ne 3 +.cs R 24 +.cs I 24 +\fR\\|/\fP +.cs R +\fI\-\(**\-\fP \- grenade and large mine explosion +.fl +.cs R 24 +\fR/|\\\fP +.cs R +.cs I +.fi +.LP +Other helpful hints: +.sp +.ie n .ds b [] +.el .ds b \(bu +.ta +.ta \w'\*b\ \|'u +.nr In \n(.i +.de MP +.br +.in \n(Inu+\w'\*b\ \|'u +.ti \n(Inu +\*b \c +.. +.MP +You can only fire in the direction you are facing. +.MP +You can only fire three shots in a row, then the gun must cool off. +.MP +Shots move 5 times faster than you do. +.MP +To stab someone, +you face that player and move at them. +.MP +Stabbing does 2 points worth of damage and shooting does 5 points. +.MP +Slime does 5 points of damage each time it hits. +.MP +You start with 15 charges and get 5 more every time a player enters +or re-enters. +.MP +Grenade explosions cover a 3 by 3 area, each larger bomb cover a +correspondingly larger area (ranging from 5 by 5 to 21 by 21). +All explosions are centered around the square the shot hits and +do the most damage in the center. +.MP +Slime affects all squares it oozes over. +The number of squares is equal to the number of charges used. +.MP +One small mine and one large mine is placed in the maze for every new player. +A mine has a 2% probability of tripping when you walk forward on to it; +50% when going sideways; +95% when backing up. +Tripping a mine costs you 5 points or 10 points respectively. +Defusing a mine is worth 1 charge or 9 charges respectively. +.MP +You cannot see behind you. +.MP +Cloaking consumes 1 ammo charge per 20 of your moves. +.MP +Scanning consumes 1 ammo charge per (20 \(mu the number of players) +of other player moves. +.MP +Turning on cloaking turns off scanning \(em turning on scanning turns off +cloaking. +.MP +When you kill someone, +you get 2 more damage capacity points and 2 damage points get taken away. +.MP +Maximum typeahead is 5 characters. +.MP +A shot destroys normal (\c +.IR i.e., +non-diagonal, non-door) walls. +.MP +Diagonal walls deflect shots and change orientation. +.MP +Doors disperse shots in random directions (up, down, left, right). +.MP +Diagonal walls and doors cannot be destroyed by direct shots but may +be destroyed by an adjacent grenade explosion. +.MP +Slime goes around walls, not through them. +.MP +Walls regenerate, reappearing in the order they were destroyed. +One percent of the regenerated walls will be diagonal walls or doors. +When a wall is generated directly beneath a player, he is thrown in +a random direction for a random period of time. When he lands, he +sustains damage (up to 20 percent of the amount of damage already +sustained); +.IR i.e. , +the less damage he had, the more nimble he is and +therefore less likely to hurt himself on landing. +.\"MP +.\"There is a volcano close to the center of the maze which goes off +.\"close to every 30 deaths. +.MP +Every 30 deaths or so, a +.B ``?'' +will appear. +It is a wandering bomb which will explode when it hits someone, or +when it is slimed. +.MP +If no one moves, everything stands still. +.MP +The environment variable +.B HUNT +is checked to get the player name. +If you don't have this variable set, +.I hunt +will ask you what name you want to play under. +If you wish to set other options than just your name, +you can enumerate the options as follows: +.br +.ti +1i +setenv HUNT "name=Sneaky,team=1,cloak,mapkey=zoFfGg1f2g3F4G" +.br +sets the player name to Sneaky, +sets the team to one, +sets the enter game attribute to cloaked, +and the maps \fBz\fP to \fBo\fP, \fBF\fP to \fBf\fP, \fBG\fP to \fBg\fP, +\fB1\fP to \fBf\fP, +\fB2\fP to \fBg\fP, \fB3\fP to \fBF\fP, and \fB4\fP to \fBG\fP. +The \fImapkey\fP option must be last. +Other options are: scan, fly, nobeep, port=string, host=string, +and message=string \(em which correspond to the command line options. +String options cannot contain commas since commas +are used to separate options. +.MP +It's a boring game if you're the only one playing. +.PP +Your score is the decayed average of the ratio of number of kills to number +of times you entered the game and is only kept for the duration +of a single session of \fIhunt\fP. +.PP +.I Hunt +normally drives up the load average to be approximately +(number_of_players + 0.5) greater than it would be without a +.I hunt +game executing. +.SH STATISTICS +The +.B \-S +option fetches the current game statistics. +The meaning of the column headings are as follows: +.I score +\(em the player's last score; +.I ducked +\(em +how many shots a player ducked; +.I absorb +\(em how many shots a player absorbed; +.I faced +\(em how many shots were fired at player's face; +.I shot +\(em how many shots were fired at player; +.I robbed +\(em how many of player's shots were absorbed; +.I missed +\(em how many of player's shots were ducked; +.I slimeK +\(em how many slime kills player had; +.I enemy +\(em how many enemies were killed; +.I friend +\(em how many friends were killed (self and same team); +.I deaths +\(em how many times player died; +.I still +\(em how many times player died without typing in any commands; +.I saved +\(em how many times a shot/bomb would have killed player if he hadn't +ducked or absorbed it. +.SH FILES +.nf +.ta +.ta \w'/usr/games/lib/huntd\ \ \ 'u +/usr/games/lib/huntd game coordinator +.DT +.fi +.SH "SEE ALSO" +huntd(6) +.SH AUTHORS +Conrad Huang, Ken Arnold, and Greg Couch; +.br +University of California, San Francisco, Computer Graphics Lab +.SH ACKNOWLEDGEMENTS +We thank Don Kneller, +John Thomason, Eric Pettersen, Mark Day, +and Scott Weiner for providing +endless hours of play-testing to improve the character of the game. +We hope their significant others will forgive them; +we certainly don't. +.SH BUGS +To keep up the pace, not everything is as realistic as possible. diff --git a/hunt/hunt/hunt.c b/hunt/hunt/hunt.c new file mode 100644 index 00000000..6599b87e --- /dev/null +++ b/hunt/hunt/hunt.c @@ -0,0 +1,1072 @@ +/* + * Hunt + * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold + * San Francisco, California + */ + +# include <errno.h> +# if !defined(USE_CURSES) && defined(BSD_RELEASE) && BSD_RELEASE >= 44 +# include <termios.h> +static struct termios saved_tty; +# endif +# include <curses.h> +# include <string.h> +# include "hunt.h" +# include <signal.h> +# include <ctype.h> +# include <sys/stat.h> +# include <sys/time.h> + +/* + * Some old versions of curses don't have these defined + */ +# if !defined(cbreak) && (!defined(BSD_RELEASE) || BSD_RELEASE < 44) +# define cbreak() crmode() +# endif + +# if !defined(USE_CURSES) || !defined(TERMINFO) +# define beep() (void) putchar(CTRL('G')) +# endif +# if !defined(USE_CURSES) +# undef refresh +# define refresh() (void) fflush(stdout); +# endif +# ifdef USE_CURSES +# define clear_eol() clrtoeol() +# define put_ch addch +# define put_str addstr +# endif + +#if !defined(BSD_RELEASE) || BSD_RELEASE < 44 +extern int _putchar(); +#endif + +FLAG Last_player = FALSE; +# ifdef MONITOR +FLAG Am_monitor = FALSE; +# endif + +char Buf[BUFSIZ]; + +int Socket; +# ifdef INTERNET +char *Sock_host; +char *use_port; +FLAG Query_driver = FALSE; +char *Send_message = NULL; +FLAG Show_scores = FALSE; +# endif + +SOCKET Daemon; +# ifdef INTERNET +# define DAEMON_SIZE (sizeof Daemon) +# else +# define DAEMON_SIZE (sizeof Daemon - 1) +# endif + +char map_key[256]; /* what to map keys to */ +FLAG no_beep; + +static char name[NAMELEN]; +static char team = ' '; + +static int in_visual; + +extern int cur_row, cur_col; +extern char *tgoto(); + +# ifdef INTERNET +extern SOCKET *list_drivers(); +# endif + +/* + * main: + * Main program for local process + */ +main(ac, av) +int ac; +char **av; +{ + char *term; + int c; + extern int errno; + extern int Otto_mode; + extern int optind; + extern char *optarg; + long enter_status; + SIGNAL_TYPE intr(), sigterm(), sigemt(), tstp(); + long env_init(), quit(); + + enter_status = env_init((long) Q_CLOAK); + while ((c = getopt(ac, av, "Sbcfh:l:mn:op:qst:w:")) != EOF) { + switch (c) { + case 'l': /* rsh compatibility */ + case 'n': + (void) strncpy(name, optarg, NAMELEN); + break; + case 't': + team = *optarg; + if (!isdigit(team)) { + fprintf(stderr, "Team names must be numeric\n"); + team = ' '; + } + break; + case 'o': +# ifndef OTTO + fputs("The -o flag is reserved for future use.\n", + stderr); + goto usage; +# else + Otto_mode = TRUE; + break; +# endif + case 'm': +# ifdef MONITOR + Am_monitor = TRUE; +# else + fputs("The monitor was not compiled in.\n", stderr); +# endif + break; +# ifdef INTERNET + case 'S': + Show_scores = TRUE; + break; + case 'q': /* query whether hunt is running */ + Query_driver = TRUE; + break; + case 'w': + Send_message = optarg; + break; + case 'h': + Sock_host = optarg; + break; + case 'p': + use_port = optarg; + Test_port = atoi(use_port); + break; +# else + case 'S': + case 'q': + case 'w': + case 'h': + case 'p': + fputs("Need TCP/IP for S, q, w, h, and p options.\n", + stderr); + break; +# endif + case 'c': + enter_status = Q_CLOAK; + break; + case 'f': +# ifdef FLY + enter_status = Q_FLY; +# else + fputs("The flying code was not compiled in.\n", stderr); +# endif + break; + case 's': + enter_status = Q_SCAN; + break; + case 'b': + no_beep = !no_beep; + break; + default: + usage: + fputs( +"usage:\thunt [-qmcsfS] [-n name] [-t team] [-p port] [-w message] [host]\n", + stderr); + exit(1); + } + } +# ifdef INTERNET + if (optind + 1 < ac) + goto usage; + else if (optind + 1 == ac) + Sock_host = av[ac - 1]; +# else + if (optind > ac) + goto usage; +# endif + +# ifdef INTERNET + if (Show_scores) { + SOCKET *hosts; + + for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1) + dump_scores(*hosts); + exit(0); + } + if (Query_driver) { + SOCKET *hosts; + + for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1) { + struct hostent *hp; + int num_players; + + hp = gethostbyaddr((char *) &hosts->sin_addr, + sizeof hosts->sin_addr, AF_INET); + num_players = ntohs(hosts->sin_port); + printf("%d player%s hunting on %s!\n", + num_players, (num_players == 1) ? "" : "s", + hp != NULL ? hp->h_name : + inet_ntoa(hosts->sin_addr)); + } + exit(0); + } +# endif +# ifdef OTTO + if (Otto_mode) + (void) strncpy(name, "otto", NAMELEN); + else +# endif + fill_in_blanks(); + + (void) fflush(stdout); + if (!isatty(0) || (term = getenv("TERM")) == NULL) { + fprintf(stderr, "no terminal type\n"); + exit(1); + } +# ifdef USE_CURSES + initscr(); + (void) noecho(); + (void) cbreak(); +# else /* !USE_CURSES */ +# if !defined(BSD_RELEASE) || BSD_RELEASE < 44 + _tty_ch = 0; +# endif + gettmode(); + (void) setterm(term); + (void) noecho(); + (void) cbreak(); +# if defined(BSD_RELEASE) && BSD_RELEASE >= 44 + tcgetattr(0, &saved_tty); +# endif + _puts(TI); + _puts(VS); +# endif /* !USE_CURSES */ + in_visual = TRUE; + if (LINES < SCREEN_HEIGHT || COLS < SCREEN_WIDTH) + leave(1, "Need a larger window"); + clear_the_screen(); + (void) signal(SIGINT, intr); + (void) signal(SIGTERM, sigterm); + (void) signal(SIGEMT, sigemt); + (void) signal(SIGPIPE, SIG_IGN); +#if !defined(USE_CURSES) && defined(SIGTSTP) + (void) signal(SIGTSTP, tstp); +#endif + + for (;;) { +# ifdef INTERNET + find_driver(TRUE); + + if (Daemon.sin_port == 0) + leave(1, "Game not found, try again"); + + jump_in: + do { + int option; + + Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0); + if (Socket < 0) { + perror("socket"); + exit(1); + } + option = 1; + if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK, + &option, sizeof option) < 0) + perror("setsockopt loopback"); + errno = 0; + if (connect(Socket, (struct sockaddr *) &Daemon, + DAEMON_SIZE) < 0) { + if (errno != ECONNREFUSED) { + perror("connect"); + leave(1, "connect"); + } + } + else + break; + sleep(1); + } while (close(Socket) == 0); +# else /* !INTERNET */ + /* + * set up a socket + */ + + if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0) { + perror("socket"); + exit(1); + } + + /* + * attempt to connect the socket to a name; if it fails that + * usually means that the driver isn't running, so we start + * up the driver. + */ + + Daemon.sun_family = SOCK_FAMILY; + (void) strcpy(Daemon.sun_path, Sock_name); + if (connect(Socket, &Daemon, DAEMON_SIZE) < 0) { + if (errno != ENOENT) { + perror("connect"); + leave(1, "connect2"); + } + start_driver(); + + do { + (void) close(Socket); + if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0) { + perror("socket"); + exit(1); + } + sleep(2); + } while (connect(Socket, &Daemon, DAEMON_SIZE) < 0); + } +# endif + + do_connect(name, team, enter_status); +# ifdef INTERNET + if (Send_message != NULL) { + do_message(); + if (enter_status == Q_MESSAGE) + break; + Send_message = NULL; + /* don't continue as that will call find_driver */ + goto jump_in; + } +# endif + playit(); + if ((enter_status = quit(enter_status)) == Q_QUIT) + break; + } + leave(0, (char *) NULL); + /* NOTREACHED */ +} + +# ifdef INTERNET +# ifdef BROADCAST +broadcast_vec(s, vector) + int s; /* socket */ + struct sockaddr **vector; +{ + char if_buf[BUFSIZ]; + struct ifconf ifc; + struct ifreq *ifr; + unsigned int n; + int vec_cnt; + + *vector = NULL; + ifc.ifc_len = sizeof if_buf; + ifc.ifc_buf = if_buf; + if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) + return 0; + vec_cnt = 0; + n = ifc.ifc_len / sizeof (struct ifreq); + *vector = (struct sockaddr *) malloc(n * sizeof (struct sockaddr)); + for (ifr = ifc.ifc_req; n != 0; n--, ifr++) + if (ioctl(s, SIOCGIFBRDADDR, ifr) >= 0) + memcpy(&(*vector)[vec_cnt++], &ifr->ifr_addr, + sizeof (struct sockaddr)); + return vec_cnt; +} +# endif + +SOCKET * +list_drivers() +{ + int option; + u_short msg; + u_short port_num; + static SOCKET test; + int test_socket; + int namelen; + char local_name[256]; + static initial = TRUE; + static struct in_addr local_address; + register struct hostent *hp; + extern int errno; +# ifdef BROADCAST + static int brdc; + static SOCKET *brdv; +# else + u_long local_net; +# endif + int i; + static SOCKET *listv; + static unsigned int listmax; + unsigned int listc; + int mask; + struct timeval wait; + + if (initial) { /* do one time initialization */ +# ifndef BROADCAST + sethostent(1); /* don't bother to close host file */ +# endif + if (gethostname(local_name, sizeof local_name) < 0) { + leave(1, "Sorry, I have no name."); + /* NOTREACHED */ + } + if ((hp = gethostbyname(local_name)) == NULL) { + leave(1, "Can't find myself."); + /* NOTREACHED */ + } + local_address = * ((struct in_addr *) hp->h_addr); + + listmax = 20; + listv = (SOCKET *) malloc(listmax * sizeof (SOCKET)); + } else if (Sock_host != NULL) + return listv; /* address already valid */ + + test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0); + if (test_socket < 0) { + perror("socket"); + leave(1, "socket system call failed"); + /* NOTREACHED */ + } + test.sin_family = SOCK_FAMILY; + test.sin_port = htons(Test_port); + listc = 0; + + if (Sock_host != NULL) { /* explicit host given */ + if ((hp = gethostbyname(Sock_host)) == NULL) { + leave(1, "Unknown host"); + /* NOTREACHED */ + } + test.sin_addr = *((struct in_addr *) hp->h_addr); + goto test_one_host; + } + + if (!initial) { + /* favor host of previous session by broadcasting to it first */ + test.sin_addr = Daemon.sin_addr; + msg = htons(C_PLAYER); /* Must be playing! */ + (void) sendto(test_socket, (char *) &msg, sizeof msg, 0, + (struct sockaddr *) &test, DAEMON_SIZE); + } + +# ifdef BROADCAST + if (initial) + brdc = broadcast_vec(test_socket, (struct sockaddr **) &brdv); + + if (brdc <= 0) { + initial = FALSE; + test.sin_addr = local_address; + goto test_one_host; + } + +# ifdef SO_BROADCAST + /* Sun's will broadcast even though this option can't be set */ + option = 1; + if (setsockopt(test_socket, SOL_SOCKET, SO_BROADCAST, + &option, sizeof option) < 0) { + perror("setsockopt broadcast"); + leave(1, "setsockopt broadcast"); + /* NOTREACHED */ + } +# endif + + /* send broadcast packets on all interfaces */ + msg = htons(C_TESTMSG()); + for (i = 0; i < brdc; i++) { + test.sin_addr = brdv[i].sin_addr; + if (sendto(test_socket, (char *) &msg, sizeof msg, 0, + (struct sockaddr *) &test, DAEMON_SIZE) < 0) { + perror("sendto"); + leave(1, "sendto"); + /* NOTREACHED */ + } + } +# else /* !BROADCAST */ + /* loop thru all hosts on local net and send msg to them. */ + msg = htons(C_TESTMSG()); + local_net = inet_netof(local_address); + sethostent(0); /* rewind host file */ + while (hp = gethostent()) { + if (local_net == inet_netof(* ((struct in_addr *) hp->h_addr))){ + test.sin_addr = * ((struct in_addr *) hp->h_addr); + (void) sendto(test_socket, (char *) &msg, sizeof msg, 0, + (struct sockaddr *) &test, DAEMON_SIZE); + } + } +# endif + +get_response: + namelen = DAEMON_SIZE; + errno = 0; + wait.tv_sec = 1; + wait.tv_usec = 0; + for (;;) { + if (listc + 1 >= listmax) { + listmax += 20; + listv = (SOCKET *) realloc((char *) listv, + listmax * sizeof(SOCKET)); + } + + mask = 1 << test_socket; + if (select(test_socket + 1, &mask, NULL, NULL, &wait) == 1 + && recvfrom(test_socket, (char *) &port_num, sizeof port_num, + 0, (struct sockaddr *) &listv[listc], &namelen) > 0) { + /* + * Note that we do *not* convert from network to host + * order since the port number *should* be in network + * order: + */ + for (i = 0; i < listc; i += 1) + if (listv[listc].sin_addr.s_addr + == listv[i].sin_addr.s_addr) + break; + if (i == listc) + listv[listc++].sin_port = port_num; + continue; + } + + if (errno != 0 && errno != EINTR) { + perror("select/recvfrom"); + leave(1, "select/recvfrom"); + /* NOTREACHED */ + } + + /* terminate list with local address */ + listv[listc].sin_family = SOCK_FAMILY; + listv[listc].sin_addr = local_address; + listv[listc].sin_port = htons(0); + + (void) close(test_socket); + initial = FALSE; + return listv; + } + +test_one_host: + msg = htons(C_TESTMSG()); + (void) sendto(test_socket, (char *) &msg, sizeof msg, 0, + (struct sockaddr *) &test, DAEMON_SIZE); + goto get_response; +} + +find_driver(do_startup) +FLAG do_startup; +{ + SOCKET *hosts; + + hosts = list_drivers(); + if (hosts[0].sin_port != htons(0)) { + int i, c; + + if (hosts[1].sin_port == htons(0)) { + Daemon = hosts[0]; + return; + } + /* go thru list and return host that matches daemon */ + clear_the_screen(); +# ifdef USE_CURSES + move(1, 0); +# else + mvcur(cur_row, cur_col, 1, 0); + cur_row = 1; + cur_col = 0; +# endif + put_str("Pick one:"); + for (i = 0; i < HEIGHT - 4 && hosts[i].sin_port != htons(0); + i += 1) { + struct hostent *hp; + char buf[80]; + +# ifdef USE_CURSES + move(3 + i, 0); +# else + mvcur(cur_row, cur_col, 3 + i, 0); + cur_row = 3 + i; + cur_col = 0; +# endif + hp = gethostbyaddr((char *) &hosts[i].sin_addr, + sizeof hosts[i].sin_addr, AF_INET); + (void) sprintf(buf, "%8c %.64s", 'a' + i, + hp != NULL ? hp->h_name + : inet_ntoa(hosts->sin_addr)); + put_str(buf); + } +# ifdef USE_CURSES + move(4 + i, 0); +# else + mvcur(cur_row, cur_col, 4 + i, 0); + cur_row = 4 + i; + cur_col = 0; +# endif + put_str("Enter letter: "); + refresh(); + while (!islower(c = getchar()) || (c -= 'a') >= i) { + beep(); + refresh(); + } + Daemon = hosts[c]; + clear_the_screen(); + return; + } + if (!do_startup) + return; + + start_driver(); + sleep(2); + find_driver(FALSE); +} + +dump_scores(host) + SOCKET host; +{ + struct hostent *hp; + int s; + char buf[BUFSIZ]; + int cnt; + + hp = gethostbyaddr((char *) &host.sin_addr, sizeof host.sin_addr, + AF_INET); + printf("\n%s:\n", hp != NULL ? hp->h_name : inet_ntoa(host.sin_addr)); + fflush(stdout); + + s = socket(SOCK_FAMILY, SOCK_STREAM, 0); + if (s < 0) { + perror("socket"); + exit(1); + } + if (connect(s, (struct sockaddr *) &host, sizeof host) < 0) { + perror("connect"); + exit(1); + } + while ((cnt = read(s, buf, BUFSIZ)) > 0) + write(fileno(stdout), buf, cnt); + (void) close(s); +} + +# endif + +start_driver() +{ + register int procid; + +# ifdef MONITOR + if (Am_monitor) { + leave(1, "No one playing."); + /* NOTREACHED */ + } +# endif + +# ifdef INTERNET + if (Sock_host != NULL) { + sleep(3); + return; + } +# endif + +# ifdef USE_CURSES + move(HEIGHT, 0); +# else + mvcur(cur_row, cur_col, HEIGHT, 0); + cur_row = HEIGHT; + cur_col = 0; +# endif + put_str("Starting..."); + refresh(); + procid = fork(); + if (procid == -1) { + perror("fork"); + leave(1, "fork failed."); + } + if (procid == 0) { + (void) signal(SIGINT, SIG_IGN); +# ifndef INTERNET + (void) close(Socket); +# else + if (use_port == NULL) +# endif + execl(Driver, "HUNT", (char *) NULL); +# ifdef INTERNET + else + execl(Driver, "HUNT", "-p", use_port, (char *) NULL); +# endif + /* only get here if exec failed */ + (void) kill(getppid(), SIGEMT); /* tell mom */ + _exit(1); + } +# ifdef USE_CURSES + move(HEIGHT, 0); +# else + mvcur(cur_row, cur_col, HEIGHT, 0); + cur_row = HEIGHT; + cur_col = 0; +# endif + put_str("Connecting..."); + refresh(); +} + +/* + * bad_con: + * We had a bad connection. For the moment we assume that this + * means the game is full. + */ +bad_con() +{ + leave(1, "The game is full. Sorry."); + /* NOTREACHED */ +} + +/* + * bad_ver: + * version number mismatch. + */ +bad_ver() +{ + leave(1, "Version number mismatch. No go."); + /* NOTREACHED */ +} + +/* + * sigterm: + * Handle a terminate signal + */ +SIGNAL_TYPE +sigterm() +{ + leave(0, (char *) NULL); + /* NOTREACHED */ +} + + +/* + * sigemt: + * Handle a emt signal - shouldn't happen on vaxes(?) + */ +SIGNAL_TYPE +sigemt() +{ + leave(1, "Unable to start driver. Try again."); + /* NOTREACHED */ +} + +# ifdef INTERNET +/* + * sigalrm: + * Handle an alarm signal + */ +SIGNAL_TYPE +sigalrm() +{ + return; +} +# endif + +/* + * rmnl: + * Remove a '\n' at the end of a string if there is one + */ +rmnl(s) +char *s; +{ + register char *cp; + + cp = strrchr(s, '\n'); + if (cp != NULL) + *cp = '\0'; +} + +/* + * intr: + * Handle a interrupt signal + */ +SIGNAL_TYPE +intr() +{ + register int ch; + register int explained; + register int y, x; + + (void) signal(SIGINT, SIG_IGN); +# ifdef USE_CURSES + getyx(stdscr, y, x); + move(HEIGHT, 0); +# else + y = cur_row; + x = cur_col; + mvcur(cur_row, cur_col, HEIGHT, 0); + cur_row = HEIGHT; + cur_col = 0; +# endif + put_str("Really quit? "); + clear_eol(); + refresh(); + explained = FALSE; + for (;;) { + ch = getchar(); + if (isupper(ch)) + ch = tolower(ch); + if (ch == 'y') { + if (Socket != 0) { + (void) write(Socket, "q", 1); + (void) close(Socket); + } + leave(0, (char *) NULL); + } + else if (ch == 'n') { + (void) signal(SIGINT, intr); +# ifdef USE_CURSES + move(y, x); +# else + mvcur(cur_row, cur_col, y, x); + cur_row = y; + cur_col = x; +# endif + refresh(); + return; + } + if (!explained) { + put_str("(Yes or No) "); + refresh(); + explained = TRUE; + } + beep(); + refresh(); + } +} + +/* + * leave: + * Leave the game somewhat gracefully, restoring all current + * tty stats. + */ +leave(eval, mesg) +int eval; +char *mesg; +{ + if (in_visual) { +# ifdef USE_CURSES + move(HEIGHT, 0); + refresh(); + endwin(); +# else /* !USE_CURSES */ + mvcur(cur_row, cur_col, HEIGHT, 0); + (void) fflush(stdout); /* flush in case VE changes pages */ +# if defined(BSD_RELEASE) && BSD_RELEASE >= 44 + tcsetattr(0, TCSADRAIN, &__orig_termios); +# else + resetty(); +# endif + _puts(VE); + _puts(TE); +# endif /* !USE_CURSES */ + } + if (mesg != NULL) + puts(mesg); + exit(eval); +} + +#if !defined(USE_CURSES) && defined(SIGTSTP) +/* + * tstp: + * Handle stop and start signals + */ +SIGNAL_TYPE +tstp() +{ +# if BSD_RELEASE < 44 + static struct sgttyb tty; +# endif + int y, x; + + y = cur_row; + x = cur_col; + mvcur(cur_row, cur_col, HEIGHT, 0); + cur_row = HEIGHT; + cur_col = 0; +# if !defined(BSD_RELEASE) || BSD_RELEASE < 44 + tty = _tty; +# endif + _puts(VE); + _puts(TE); + (void) fflush(stdout); +# if defined(BSD_RELEASE) && BSD_RELEASE >= 44 + tcsetattr(0, TCSADRAIN, &__orig_termios); +# else + resetty(); +# endif + (void) kill(getpid(), SIGSTOP); + (void) signal(SIGTSTP, tstp); +# if defined(BSD_RELEASE) && BSD_RELEASE >= 44 + tcsetattr(0, TCSADRAIN, &saved_tty); +# else + _tty = tty; + ioctl(_tty_ch, TIOCSETP, &_tty); +# endif + _puts(TI); + _puts(VS); + cur_row = y; + cur_col = x; + _puts(tgoto(CM, cur_row, cur_col)); + redraw_screen(); + (void) fflush(stdout); +} +#endif /* !defined(USE_CURSES) && defined(SIGTSTP) */ + +# if defined(BSD_RELEASE) && BSD_RELEASE < 43 +char * +strpbrk(s, brk) + register char *s, *brk; +{ + register char *p; + register c; + + while (c = *s) { + for (p = brk; *p; p++) + if (c == *p) + return (s); + s++; + } + return (0); +} +# endif + +long +env_init(enter_status) + long enter_status; +{ + register int i; + char *envp, *envname, *s; + + for (i = 0; i < 256; i++) + map_key[i] = (char) i; + + envname = NULL; + if ((envp = getenv("HUNT")) != NULL) { + while ((s = strpbrk(envp, "=,")) != NULL) { + if (strncmp(envp, "cloak,", s - envp + 1) == 0) { + enter_status = Q_CLOAK; + envp = s + 1; + } + else if (strncmp(envp, "scan,", s - envp + 1) == 0) { + enter_status = Q_SCAN; + envp = s + 1; + } + else if (strncmp(envp, "fly,", s - envp + 1) == 0) { + enter_status = Q_FLY; + envp = s + 1; + } + else if (strncmp(envp, "nobeep,", s - envp + 1) == 0) { + no_beep = TRUE; + envp = s + 1; + } + else if (strncmp(envp, "name=", s - envp + 1) == 0) { + envname = s + 1; + if ((s = strchr(envp, ',')) == NULL) { + *envp = '\0'; + strncpy(name, envname, NAMELEN); + break; + } + *s = '\0'; + strncpy(name, envname, NAMELEN); + envp = s + 1; + } +# ifdef INTERNET + else if (strncmp(envp, "port=", s - envp + 1) == 0) { + use_port = s + 1; + Test_port = atoi(use_port); + if ((s = strchr(envp, ',')) == NULL) { + *envp = '\0'; + break; + } + *s = '\0'; + envp = s + 1; + } + else if (strncmp(envp, "host=", s - envp + 1) == 0) { + Sock_host = s + 1; + if ((s = strchr(envp, ',')) == NULL) { + *envp = '\0'; + break; + } + *s = '\0'; + envp = s + 1; + } + else if (strncmp(envp, "message=", s - envp + 1) == 0) { + Send_message = s + 1; + if ((s = strchr(envp, ',')) == NULL) { + *envp = '\0'; + break; + } + *s = '\0'; + envp = s + 1; + } +# endif + else if (strncmp(envp, "team=", s - envp + 1) == 0) { + team = *(s + 1); + if (!isdigit(team)) + team = ' '; + if ((s = strchr(envp, ',')) == NULL) { + *envp = '\0'; + break; + } + *s = '\0'; + envp = s + 1; + } /* must be last option */ + else if (strncmp(envp, "mapkey=", s - envp + 1) == 0) { + for (s = s + 1; *s != '\0'; s += 2) { + map_key[(unsigned int) *s] = *(s + 1); + if (*(s + 1) == '\0') { + break; + } + } + *envp = '\0'; + break; + } else { + *s = '\0'; + printf("unknown option %s\n", envp); + if ((s = strchr(envp, ',')) == NULL) { + *envp = '\0'; + break; + } + envp = s + 1; + } + } + if (*envp != '\0') + if (envname == NULL) + strncpy(name, envp, NAMELEN); + else + printf("unknown option %s\n", envp); + } + return enter_status; +} + +fill_in_blanks() +{ + register int i; + register char *cp; + +again: + if (name[0] != '\0') { + printf("Entering as '%s'", name); + if (team != ' ') + printf(" on team %c.\n", team); + else + putchar('\n'); + } else { + printf("Enter your code name: "); + if (fgets(name, NAMELEN, stdin) == NULL) + exit(1); + } + rmnl(name); + if (name[0] == '\0') { + name[0] = '\0'; + printf("You have to have a code name!\n"); + goto again; + } + for (cp = name; *cp != '\0'; cp++) + if (!isprint(*cp)) { + name[0] = '\0'; + printf("Illegal character in your code name.\n"); + goto again; + } + if (team == ' ') { + printf("Enter your team (0-9 or nothing): "); + i = getchar(); + if (isdigit(i)) + team = i; + while (i != '\n' && i != EOF) + i = getchar(); + } +} diff --git a/hunt/hunt/otto.c b/hunt/hunt/otto.c new file mode 100644 index 00000000..ab3b9029 --- /dev/null +++ b/hunt/hunt/otto.c @@ -0,0 +1,585 @@ +# ifdef OTTO +/* + * otto - a hunt otto-matic player + * + * This guy is buggy, unfair, stupid, and not extensible. + * Future versions of hunt will have a subroutine library for + * automatic players to link to. If you write your own "otto" + * please let us know what subroutines you would expect in the + * subroutine library. + * + * $Id: otto.c,v 1.1.1.1 1997/10/04 09:00:14 mrg Exp $ + */ + +# include <curses.h> +# include <ctype.h> +# include "hunt.h" +# include <sys/time.h> +# include <signal.h> +# undef WALL +# undef NORTH +# undef SOUTH +# undef WEST +# undef EAST +# undef FRONT +# undef LEFT +# undef BACK +# undef RIGHT + +# ifdef HPUX +# define random rand +# endif + +# ifndef USE_CURSES +extern char screen[SCREEN_HEIGHT][SCREEN_WIDTH2]; +# define SCREEN(y, x) screen[y][x] +# else +# if defined(BSD_RELEASE) && BSD_RELEASE >= 44 +# define SCREEN(y, x) stdscr->lines[y]->line[x].ch +# else +# define SCREEN(y, x) stdscr->_y[y][x] +# endif +# endif + +# ifndef DEBUG +# define STATIC static +# else +# define STATIC +# endif + +# define OPPONENT "{}i!" +# define PROPONENT "^v<>" +# define WALL "+\\/#*-|" +# define PUSHOVER " bg;*#&" +# define SHOTS "$@Oo:" + +/* number of "directions" */ +# define NUMDIRECTIONS 4 + +/* absolute directions (facings) - counterclockwise */ +# define NORTH 0 +# define WEST 1 +# define SOUTH 2 +# define EAST 3 +# define ALLDIRS 0xf + +/* relative directions - counterclockwise */ +# define FRONT 0 +# define LEFT 1 +# define BACK 2 +# define RIGHT 3 + +# define ABSCHARS "NWSE" +# define RELCHARS "FLBR" +# define DIRKEYS "khjl" + +STATIC char command[BUFSIZ]; +STATIC int comlen; + +# ifdef DEBUG +STATIC FILE *debug = NULL; +# endif + +# define DEADEND 0x1 +# define ON_LEFT 0x2 +# define ON_RIGHT 0x4 +# define ON_SIDE (ON_LEFT|ON_RIGHT) +# define BEEN 0x8 +# define BEEN_SAME 0x10 + +struct item { + char what; + int distance; + int flags; +}; + +STATIC struct item flbr[NUMDIRECTIONS]; + +# define fitem flbr[FRONT] +# define litem flbr[LEFT] +# define bitem flbr[BACK] +# define ritem flbr[RIGHT] + +STATIC int facing; +STATIC int row, col; +STATIC int num_turns; /* for wandering */ +STATIC char been_there[HEIGHT][WIDTH2]; +STATIC struct itimerval pause_time = { { 0, 0 }, { 0, 55000 }}; + +STATIC int stop_look(); +STATIC int look_around(); +STATIC int face_and_move_direction(); +STATIC int attack(); +STATIC int duck(); +STATIC int go_for_ammo(); +STATIC int wander(); + +STATIC SIGNAL_TYPE +nothing() +{ +} + +otto(y, x, face) + int y, x; + char face; +{ + register int i; + extern int Otto_count; + int old_mask; + +# ifdef DEBUG + if (debug == NULL) { + debug = fopen("bug", "w"); + setbuf(debug, NULL); + } + fprintf(debug, "\n%c(%d,%d)", face, y, x); +# endif + (void) signal(SIGALRM, nothing); + old_mask = sigblock(sigmask(SIGALRM)); + setitimer(ITIMER_REAL, &pause_time, NULL); + sigpause(old_mask); + sigsetmask(old_mask); + + /* save away parameters so other functions may use/update info */ + switch (face) { + case '^': facing = NORTH; break; + case '<': facing = WEST; break; + case 'v': facing = SOUTH; break; + case '>': facing = EAST; break; + default: abort(); + } + row = y; col = x; + been_there[row][col] |= 1 << facing; + + /* initially no commands to be sent */ + comlen = 0; + + /* find something to do */ + look_around(); + for (i = 0; i < NUMDIRECTIONS; i++) { + if (strchr(OPPONENT, flbr[i].what) != NULL) { + attack(i, &flbr[i]); + memset(been_there, 0, sizeof been_there); + goto done; + } + } + + if (strchr(SHOTS, bitem.what) != NULL && !(bitem.what & ON_SIDE)) { + duck(BACK); + memset(been_there, 0, sizeof been_there); +# ifdef BOOTS + } else if (go_for_ammo(BOOT_PAIR)) { + memset(been_there, 0, sizeof been_there); + } else if (go_for_ammo(BOOT)) { + memset(been_there, 0, sizeof been_there); +# endif + } else if (go_for_ammo(GMINE)) + memset(been_there, 0, sizeof been_there); + else if (go_for_ammo(MINE)) + memset(been_there, 0, sizeof been_there); + else + wander(); + +done: + (void) write(Socket, command, comlen); + Otto_count += comlen; +# ifdef DEBUG + (void) fwrite(command, 1, comlen, debug); +# endif +} + +# define direction(abs,rel) (((abs) + (rel)) % NUMDIRECTIONS) + +STATIC +stop_look(itemp, c, dist, side) + struct item *itemp; + char c; + int dist; + int side; +{ + switch (c) { + + case SPACE: + if (side) + itemp->flags &= ~DEADEND; + return 0; + + case MINE: + case GMINE: +# ifdef BOOTS + case BOOT: + case BOOT_PAIR: +# endif + if (itemp->distance == -1) { + itemp->distance = dist; + itemp->what = c; + if (side < 0) + itemp->flags |= ON_LEFT; + else if (side > 0) + itemp->flags |= ON_RIGHT; + } + return 0; + + case SHOT: + case GRENADE: + case SATCHEL: + case BOMB: +# ifdef OOZE + case SLIME: +# endif + if (itemp->distance == -1 || (!side + && (itemp->flags & ON_SIDE + || itemp->what == GMINE || itemp->what == MINE))) { + itemp->distance = dist; + itemp->what = c; + itemp->flags &= ~ON_SIDE; + if (side < 0) + itemp->flags |= ON_LEFT; + else if (side > 0) + itemp->flags |= ON_RIGHT; + } + return 0; + + case '{': + case '}': + case 'i': + case '!': + itemp->distance = dist; + itemp->what = c; + itemp->flags &= ~(ON_SIDE|DEADEND); + if (side < 0) + itemp->flags |= ON_LEFT; + else if (side > 0) + itemp->flags |= ON_RIGHT; + return 1; + + default: + /* a wall or unknown object */ + if (side) + return 0; + if (itemp->distance == -1) { + itemp->distance = dist; + itemp->what = c; + } + return 1; + } +} + +look(rel_dir, itemp) + int rel_dir; + struct item *itemp; +{ + register int r, c; + register char ch; + + itemp->what = 0; + itemp->distance = -1; + itemp->flags = DEADEND|BEEN; /* true until proven false */ + + switch (direction(facing, rel_dir)) { + + case NORTH: + if (been_there[row - 1][col] & NORTH) + itemp->flags |= BEEN_SAME; + for (r = row - 1; r >= 0; r--) + for (c = col - 1; c < col + 2; c++) { + ch = SCREEN(r, c); + if (stop_look(itemp, ch, row - r, c - col)) + goto cont_north; + if (c == col && !been_there[r][c]) + itemp->flags &= ~BEEN; + } + cont_north: + if (itemp->flags & DEADEND) { + itemp->flags |= BEEN; + been_there[r][col] |= NORTH; + for (r = row - 1; r > row - itemp->distance; r--) + been_there[r][col] = ALLDIRS; + } + break; + + case SOUTH: + if (been_there[row + 1][col] & SOUTH) + itemp->flags |= BEEN_SAME; + for (r = row + 1; r < HEIGHT; r++) + for (c = col - 1; c < col + 2; c++) { + ch = SCREEN(r, c); + if (stop_look(itemp, ch, r - row, col - c)) + goto cont_south; + if (c == col && !been_there[r][c]) + itemp->flags &= ~BEEN; + } + cont_south: + if (itemp->flags & DEADEND) { + itemp->flags |= BEEN; + been_there[r][col] |= SOUTH; + for (r = row + 1; r < row + itemp->distance; r++) + been_there[r][col] = ALLDIRS; + } + break; + + case WEST: + if (been_there[row][col - 1] & WEST) + itemp->flags |= BEEN_SAME; + for (c = col - 1; c >= 0; c--) + for (r = row - 1; r < row + 2; r++) { + ch = SCREEN(r, c); + if (stop_look(itemp, ch, col - c, row - r)) + goto cont_east; + if (r == row && !been_there[r][c]) + itemp->flags &= ~BEEN; + } + cont_west: + if (itemp->flags & DEADEND) { + itemp->flags |= BEEN; + been_there[r][col] |= WEST; + for (c = col - 1; c > col - itemp->distance; c--) + been_there[row][c] = ALLDIRS; + } + break; + + case EAST: + if (been_there[row][col + 1] & EAST) + itemp->flags |= BEEN_SAME; + for (c = col + 1; c < WIDTH; c++) + for (r = row - 1; r < row + 2; r++) { + ch = SCREEN(r, c); + if (stop_look(itemp, ch, c - col, r - row)) + goto cont_east; + if (r == row && !been_there[r][c]) + itemp->flags &= ~BEEN; + } + cont_east: + if (itemp->flags & DEADEND) { + itemp->flags |= BEEN; + been_there[r][col] |= EAST; + for (c = col + 1; c < col + itemp->distance; c++) + been_there[row][c] = ALLDIRS; + } + break; + + default: + abort(); + } +} + +STATIC +look_around() +{ + register int i; + + for (i = 0; i < NUMDIRECTIONS; i++) { + look(i, &flbr[i]); +# ifdef DEBUG + fprintf(debug, " look(%c)=%c(%d)(0x%x)", + RELCHARS[i], flbr[i].what, flbr[i].distance, flbr[i].flags); +# endif + } +} + +/* + * as a side effect modifies facing and location (row, col) + */ + +STATIC +face_and_move_direction(rel_dir, distance) + int rel_dir, distance; +{ + register int old_facing; + register char cmd; + + old_facing = facing; + cmd = DIRKEYS[facing = direction(facing, rel_dir)]; + + if (rel_dir != FRONT) { + register int i; + struct item items[NUMDIRECTIONS]; + + command[comlen++] = toupper(cmd); + if (distance == 0) { + /* rotate look's to be in right position */ + for (i = 0; i < NUMDIRECTIONS; i++) + items[i] = + flbr[(i + old_facing) % NUMDIRECTIONS]; + memcpy(flbr, items, sizeof flbr); + } + } + while (distance--) { + command[comlen++] = cmd; + switch (facing) { + + case NORTH: row--; break; + case WEST: col--; break; + case SOUTH: row++; break; + case EAST: col++; break; + } + if (distance == 0) + look_around(); + } +} + +STATIC +attack(rel_dir, itemp) + int rel_dir; + struct item *itemp; +{ + if (!(itemp->flags & ON_SIDE)) { + face_and_move_direction(rel_dir, 0); + command[comlen++] = 'o'; + command[comlen++] = 'o'; + duck(FRONT); + command[comlen++] = ' '; + } else if (itemp->distance > 1) { + face_and_move_direction(rel_dir, 2); + duck(FRONT); + } else { + face_and_move_direction(rel_dir, 1); + if (itemp->flags & ON_LEFT) + rel_dir = LEFT; + else + rel_dir = RIGHT; + (void) face_and_move_direction(rel_dir, 0); + command[comlen++] = 'f'; + command[comlen++] = 'f'; + duck(FRONT); + command[comlen++] = ' '; + } +} + +STATIC +duck(rel_dir) + int rel_dir; +{ + int dir; + + switch (dir = direction(facing, rel_dir)) { + + case NORTH: + case SOUTH: + if (strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL) + command[comlen++] = 'h'; + else if (strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL) + command[comlen++] = 'l'; + else if (dir == NORTH + && strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL) + command[comlen++] = 'j'; + else if (dir == SOUTH + && strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL) + command[comlen++] = 'k'; + else if (dir == NORTH) + command[comlen++] = 'k'; + else + command[comlen++] = 'j'; + break; + + case WEST: + case EAST: + if (strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL) + command[comlen++] = 'k'; + else if (strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL) + command[comlen++] = 'j'; + else if (dir == WEST + && strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL) + command[comlen++] = 'l'; + else if (dir == EAST + && strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL) + command[comlen++] = 'h'; + else if (dir == WEST) + command[comlen++] = 'h'; + else + command[comlen++] = 'l'; + break; + } +} + +/* + * go for the closest mine if possible + */ + +STATIC +go_for_ammo(mine) + char mine; +{ + register int i, rel_dir, dist; + + rel_dir = -1; + dist = WIDTH; + for (i = 0; i < NUMDIRECTIONS; i++) { + if (flbr[i].what == mine && flbr[i].distance < dist) { + rel_dir = i; + dist = flbr[i].distance; + } + } + if (rel_dir == -1) + return FALSE; + + if (!(flbr[rel_dir].flags & ON_SIDE) + || flbr[rel_dir].distance > 1) { + if (dist > 4) + dist = 4; + face_and_move_direction(rel_dir, dist); + } else + return FALSE; /* until it's done right */ + return TRUE; +} + +STATIC +wander() +{ + register int i, j, rel_dir, dir_mask, dir_count; + + for (i = 0; i < NUMDIRECTIONS; i++) + if (!(flbr[i].flags & BEEN) || flbr[i].distance <= 1) + break; + if (i == NUMDIRECTIONS) + memset(been_there, 0, sizeof been_there); + dir_mask = dir_count = 0; + for (i = 0; i < NUMDIRECTIONS; i++) { + j = (RIGHT + i) % NUMDIRECTIONS; + if (flbr[j].distance <= 1 || flbr[j].flags & DEADEND) + continue; + if (!(flbr[j].flags & BEEN_SAME)) { + dir_mask = 1 << j; + dir_count = 1; + break; + } + if (j == FRONT + && num_turns > 4 + (random() % + ((flbr[FRONT].flags & BEEN) ? 7 : HEIGHT))) + continue; + dir_mask |= 1 << j; +# ifdef notdef + dir_count++; +# else + dir_count = 1; + break; +# endif + } + if (dir_count == 0) { + duck(random() % NUMDIRECTIONS); + num_turns = 0; + return; + } else if (dir_count == 1) + rel_dir = ffs(dir_mask) - 1; + else { + rel_dir = ffs(dir_mask) - 1; + dir_mask &= ~(1 << rel_dir); + while (dir_mask != 0) { + i = ffs(dir_mask) - 1; + if (random() % 5 == 0) + rel_dir = i; + dir_mask &= ~(1 << i); + } + } + if (rel_dir == FRONT) + num_turns++; + else + num_turns = 0; + +# ifdef DEBUG + fprintf(debug, " w(%c)", RELCHARS[rel_dir]); +# endif + face_and_move_direction(rel_dir, 1); +} + +# endif /* OTTO */ diff --git a/hunt/hunt/playit.c b/hunt/hunt/playit.c new file mode 100644 index 00000000..4a6046c9 --- /dev/null +++ b/hunt/hunt/playit.c @@ -0,0 +1,628 @@ +/* + * Hunt + * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold + * San Francisco, California + */ + +# if defined(HPUX) || (defined(BSD_RELEASE) && BSD_RELEASE >= 44) +# include <termios.h> +# endif +# include <curses.h> +# include <ctype.h> +# include <signal.h> +# include <errno.h> +# include "hunt.h" +# include <sys/file.h> + +# ifndef FREAD +# define FREAD 1 +# endif + +# if !defined(USE_CURSES) || !defined(TERMINFO) +# define beep() (void) putchar(CTRL('G')) +# endif +# if !defined(USE_CURSES) +# undef refresh +# define refresh() (void) fflush(stdout); +# endif +# ifdef USE_CURSES +# define clear_eol() clrtoeol() +# define put_ch addch +# define put_str addstr +# endif + +int input(); +static int nchar_send; +static int in = FREAD; +# ifndef USE_CURSES +char screen[SCREEN_HEIGHT][SCREEN_WIDTH2], blanks[SCREEN_WIDTH]; +int cur_row, cur_col; +# endif +# ifdef OTTO +int Otto_count; +int Otto_mode; +static int otto_y, otto_x; +static char otto_face; +# endif + +# define MAX_SEND 5 +# define STDIN 0 + +/* + * ibuf is the input buffer used for the stream from the driver. + * It is small because we do not check for user input when there + * are characters in the input buffer. + */ +static int icnt = 0; +static unsigned char ibuf[256], *iptr = ibuf; +static unsigned char getchr(); + +#define GETCHR() (--icnt < 0 ? getchr() : *iptr++) + +#if !defined(BSD_RELEASE) || BSD_RELEASE < 44 +extern int _putchar(); +#endif + +/* + * playit: + * Play a given game, handling all the curses commands from + * the driver. + */ +playit() +{ + register int ch; + register int y, x; + extern int errno; + long version; + + if (read(Socket, (char *) &version, LONGLEN) != LONGLEN) { + bad_con(); + /* NOTREACHED */ + } + if (ntohl(version) != HUNT_VERSION) { + bad_ver(); + /* NOTREACHED */ + } + errno = 0; +# ifdef OTTO + Otto_count = 0; +# endif + nchar_send = MAX_SEND; + while ((ch = GETCHR()) != EOF) { +# ifdef DEBUG + fputc(ch, stderr); +# endif + switch (ch & 0377) { + case MOVE: + y = GETCHR(); + x = GETCHR(); +# ifdef USE_CURSES + move(y, x); +# else + mvcur(cur_row, cur_col, y, x); + cur_row = y; + cur_col = x; +# endif + break; + case ADDCH: + ch = GETCHR(); +# ifdef OTTO + switch (ch) { + + case '<': + case '>': + case '^': + case 'v': + otto_face = ch; +# ifdef USE_CURSES + getyx(stdscr, otto_y, otto_x); +# else + otto_y = cur_row; + otto_x = cur_col; +# endif + break; + } +# endif + put_ch(ch); + break; + case CLRTOEOL: + clear_eol(); + break; + case CLEAR: + clear_the_screen(); + break; + case REFRESH: + refresh(); + break; + case REDRAW: + redraw_screen(); + refresh(); + break; + case ENDWIN: + refresh(); + if ((ch = GETCHR()) == LAST_PLAYER) + Last_player = TRUE; + ch = EOF; + goto out; + case BELL: + beep(); + break; + case READY: + refresh(); + if (nchar_send < 0) +# if defined(HPUX) || (defined(BSD_RELEASE) && BSD_RELEASE >= 44) + tcflush(STDIN, TCIFLUSH); +# else +# ifndef TCFLSH + (void) ioctl(STDIN, TIOCFLUSH, &in); +# else + (void) ioctl(STDIN, TCFLSH, 0); +# endif +# endif + nchar_send = MAX_SEND; +# ifndef OTTO + (void) GETCHR(); +# else + Otto_count -= (GETCHR() & 0xff); + if (!Am_monitor) { +# ifdef DEBUG + fputc('0' + Otto_count, stderr); +# endif + if (Otto_count == 0 && Otto_mode) + otto(otto_y, otto_x, otto_face); + } +# endif + break; + default: +# ifdef OTTO + switch (ch) { + + case '<': + case '>': + case '^': + case 'v': + otto_face = ch; +# ifdef USE_CURSES + getyx(stdscr, otto_y, otto_x); +# else + otto_y = cur_row; + otto_x = cur_col; +# endif + break; + } +# endif + put_ch(ch); + break; + } + } +out: + (void) close(Socket); +} + +/* + * getchr: + * Grab input and pass it along to the driver + * Return any characters from the driver + * When this routine is called by GETCHR, we already know there are + * no characters in the input buffer. + */ +static +unsigned char +getchr() +{ + long readfds, s_readfds; + int driver_mask, stdin_mask; + int nfds, s_nfds; + + driver_mask = 1L << Socket; + stdin_mask = 1L << STDIN; + s_readfds = driver_mask | stdin_mask; + s_nfds = (Socket > STDIN) ? Socket : STDIN; + s_nfds++; + +one_more_time: + do { + errno = 0; + readfds = s_readfds; + nfds = s_nfds; + nfds = select(nfds, &readfds, NULL, NULL, NULL); + } while (nfds <= 0 && errno == EINTR); + + if (readfds & stdin_mask) + send_stuff(); + if ((readfds & driver_mask) == 0) + goto one_more_time; + icnt = read(Socket, ibuf, sizeof ibuf); + if (icnt < 0) { + bad_con(); + /* NOTREACHED */ + } + if (icnt == 0) + goto one_more_time; + iptr = ibuf; + icnt--; + return *iptr++; +} + +/* + * send_stuff: + * Send standard input characters to the driver + */ +send_stuff() +{ + register int count; + register char *sp, *nsp; + static char inp[sizeof Buf]; + + count = read(STDIN, Buf, sizeof Buf); + if (count <= 0) + return; + if (nchar_send <= 0 && !no_beep) { + (void) write(1, "\7", 1); /* CTRL('G') */ + return; + } + + /* + * look for 'q'uit commands; if we find one, + * confirm it. If it is not confirmed, strip + * it out of the input + */ + Buf[count] = '\0'; + nsp = inp; + for (sp = Buf; *sp != '\0'; sp++) + if ((*nsp = map_key[*sp]) == 'q') + intr(); + else + nsp++; + count = nsp - inp; + if (count) { +# ifdef OTTO + Otto_count += count; +# endif + nchar_send -= count; + if (nchar_send < 0) + count += nchar_send; + (void) write(Socket, inp, count); + } +} + +/* + * quit: + * Handle the end of the game when the player dies + */ +long +quit(old_status) +long old_status; +{ + register int explain, ch; + + if (Last_player) + return Q_QUIT; +# ifdef OTTO + if (Otto_mode) + return Q_CLOAK; +# endif +# ifdef USE_CURSES + move(HEIGHT, 0); +# else + mvcur(cur_row, cur_col, HEIGHT, 0); + cur_row = HEIGHT; + cur_col = 0; +# endif + put_str("Re-enter game [ynwo]? "); + clear_eol(); + explain = FALSE; + for (;;) { + refresh(); + if (isupper(ch = getchar())) + ch = tolower(ch); + if (ch == 'y') + return old_status; + else if (ch == 'o') + break; + else if (ch == 'n') { +# ifndef INTERNET + return Q_QUIT; +# else +# ifdef USE_CURSES + move(HEIGHT, 0); +# else + mvcur(cur_row, cur_col, HEIGHT, 0); + cur_row = HEIGHT; + cur_col = 0; +# endif + put_str("Write a parting message [yn]? "); + clear_eol(); + refresh(); + for (;;) { + if (isupper(ch = getchar())) + ch = tolower(ch); + if (ch == 'y') + goto get_message; + if (ch == 'n') + return Q_QUIT; + } +# endif + } +# ifdef INTERNET + else if (ch == 'w') { + static char buf[WIDTH + WIDTH % 2]; + char *cp, c; + +get_message: + c = ch; /* save how we got here */ +# ifdef USE_CURSES + move(HEIGHT, 0); +# else + mvcur(cur_row, cur_col, HEIGHT, 0); + cur_row = HEIGHT; + cur_col = 0; +# endif + put_str("Message: "); + clear_eol(); + refresh(); + cp = buf; + for (;;) { + refresh(); + if ((ch = getchar()) == '\n' || ch == '\r') + break; +# if defined(TERMINFO) || BSD_RELEASE >= 44 + if (ch == erasechar()) +# else + if (ch == _tty.sg_erase) +# endif + { + if (cp > buf) { +# ifdef USE_CURSES + int y, x; + getyx(stdscr, y, x); + move(y, x - 1); +# else + mvcur(cur_row, cur_col, cur_row, + cur_col - 1); + cur_col -= 1; +# endif + cp -= 1; + clear_eol(); + } + continue; + } +# if defined(TERMINFO) || BSD_RELEASE >= 44 + else if (ch == killchar()) +# else + else if (ch == _tty.sg_kill) +# endif + { +# ifdef USE_CURSES + int y, x; + getyx(stdscr, y, x); + move(y, x - (cp - buf)); +# else + mvcur(cur_row, cur_col, cur_row, + cur_col - (cp - buf)); + cur_col -= cp - buf; +# endif + cp = buf; + clear_eol(); + continue; + } else if (!isprint(ch)) { + beep(); + continue; + } + put_ch(ch); + *cp++ = ch; + if (cp + 1 >= buf + sizeof buf) + break; + } + *cp = '\0'; + Send_message = buf; + return (c == 'w') ? old_status : Q_MESSAGE; + } +# endif + beep(); + if (!explain) { + put_str("(Yes, No, Write message, or Options) "); + explain = TRUE; + } + } + +# ifdef USE_CURSES + move(HEIGHT, 0); +# else + mvcur(cur_row, cur_col, HEIGHT, 0); + cur_row = HEIGHT; + cur_col = 0; +# endif +# ifdef FLY + put_str("Scan, Cloak, Flying, or Quit? "); +# else + put_str("Scan, Cloak, or Quit? "); +# endif + clear_eol(); + refresh(); + explain = FALSE; + for (;;) { + if (isupper(ch = getchar())) + ch = tolower(ch); + if (ch == 's') + return Q_SCAN; + else if (ch == 'c') + return Q_CLOAK; +# ifdef FLY + else if (ch == 'f') + return Q_FLY; +# endif + else if (ch == 'q') + return Q_QUIT; + beep(); + if (!explain) { +# ifdef FLY + put_str("[SCFQ] "); +# else + put_str("[SCQ] "); +# endif + explain = TRUE; + } + refresh(); + } +} + +# ifndef USE_CURSES +put_ch(ch) + char ch; +{ + if (!isprint(ch)) { + fprintf(stderr, "r,c,ch: %d,%d,%d", cur_row, cur_col, ch); + return; + } + screen[cur_row][cur_col] = ch; + putchar(ch); + if (++cur_col >= COLS) { + if (!AM || XN) + putchar('\n'); + cur_col = 0; + if (++cur_row >= LINES) + cur_row = LINES; + } +} + +put_str(s) + char *s; +{ + while (*s) + put_ch(*s++); +} +# endif + +clear_the_screen() +{ +# ifdef USE_CURSES + clear(); + move(0, 0); + refresh(); +# else + register int i; + + if (blanks[0] == '\0') + for (i = 0; i < SCREEN_WIDTH; i++) + blanks[i] = ' '; + + if (CL != NULL) { +#if !defined(BSD_RELEASE) || BSD_RELEASE < 44 + tputs(CL, LINES, _putchar); +#else + tputs(CL, LINES, __cputchar); +#endif + for (i = 0; i < SCREEN_HEIGHT; i++) + memcpy(screen[i], blanks, SCREEN_WIDTH); + } else { + for (i = 0; i < SCREEN_HEIGHT; i++) { + mvcur(cur_row, cur_col, i, 0); + cur_row = i; + cur_col = 0; + clear_eol(); + } + mvcur(cur_row, cur_col, 0, 0); + } + cur_row = cur_col = 0; +#endif +} + +#ifndef USE_CURSES +clear_eol() +{ + if (CE != NULL) +#if !defined(BSD_RELEASE) || BSD_RELEASE < 44 + tputs(CE, 1, _putchar); +#else + tputs(CE, 1, __cputchar); +#endif + else { + fwrite(blanks, sizeof (char), SCREEN_WIDTH - cur_col, stdout); + if (COLS != SCREEN_WIDTH) + mvcur(cur_row, SCREEN_WIDTH, cur_row, cur_col); + else if (AM) + mvcur(cur_row + 1, 0, cur_row, cur_col); + else + mvcur(cur_row, SCREEN_WIDTH - 1, cur_row, cur_col); + } + memcpy(&screen[cur_row][cur_col], blanks, SCREEN_WIDTH - cur_col); +} +# endif + +redraw_screen() +{ +# ifdef USE_CURSES + clearok(stdscr, TRUE); + touchwin(stdscr); +# else + register int i; +# ifndef NOCURSES + static int first = 1; + + if (first) { + curscr = newwin(SCREEN_HEIGHT, SCREEN_WIDTH, 0, 0); + if (curscr == NULL) { + fprintf(stderr, "Can't create curscr\n"); + exit(1); + } +# if !defined(BSD_RELEASE) || BSD_RELEASE < 44 + for (i = 0; i < SCREEN_HEIGHT; i++) + curscr->_y[i] = screen[i]; +# endif + first = 0; + } +# if defined(BSD_RELEASE) && BSD_RELEASE >= 44 + for (i = 0; i < SCREEN_HEIGHT; i++) { + register int j; + for (j = 0; j < SCREEN_WIDTH; j++) + curscr->lines[i]->line[j].ch = screen[i][j]; + } + curscr->cury = cur_row; + curscr->curx = cur_col; +# else + curscr->_cury = cur_row; + curscr->_curx = cur_col; +# endif + clearok(curscr, TRUE); + touchwin(curscr); + wrefresh(curscr); +#else + mvcur(cur_row, cur_col, 0, 0); + for (i = 0; i < SCREEN_HEIGHT - 1; i++) { + fwrite(screen[i], sizeof (char), SCREEN_WIDTH, stdout); + if (COLS > SCREEN_WIDTH || (COLS == SCREEN_WIDTH && !AM)) + putchar('\n'); + } + fwrite(screen[SCREEN_HEIGHT - 1], sizeof (char), SCREEN_WIDTH - 1, + stdout); + mvcur(SCREEN_HEIGHT - 1, SCREEN_WIDTH - 1, cur_row, cur_col); +#endif +#endif +} + +/* + * do_message: + * Send a message to the driver and return + */ +do_message() +{ + extern int errno; + long version; + + if (read(Socket, (char *) &version, LONGLEN) != LONGLEN) { + bad_con(); + /* NOTREACHED */ + } + if (ntohl(version) != HUNT_VERSION) { + bad_ver(); + /* NOTREACHED */ + } +# ifdef INTERNET + if (write(Socket, Send_message, strlen(Send_message)) < 0) { + bad_con(); + /* NOTREACHED */ + } +# endif + (void) close(Socket); +} |