billr@saab.CNA.TEK.COM (Bill Randle) (09/15/89)
Submitted-by: Eric Raymond <eric@snark.uu.net> Posting-number: Volume 8, Issue 25 Archive-name: bs/Part01 [This should work on either SysV or BSD based Unixes. Eric is running SysV; I tested it on my Sun 3/60 (OS 3.5), which is BSD based. -br] #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 1)." # Contents: README Makefile bs.6 bs.c # Wrapped by billr@saab on Fri Sep 15 06:55:27 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(1342 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XBattleships is an intrinsically silly game, but I couldn't resist fixing this Xsucker. It now has a purely visual interface (you place ships and call for Xshots by moving the cursor around the board using the standard yuhjklbn keys). X XThe default game now disallows placement of ships so that they touch. A new X-c option is available to force the older behavior. X XI also removed the `seemiss' option (now always on) and `ask' (which is only Xuseful for cheating). And I ifdefed out the ditsy opening screen; if you want Xit back, compile with -DPENGUIN. One strike against featureitis... X XThe code now lints as clean as the broken SysV curses lint library will let it. X XSome #ifdefs in the code should result in the right things being done for XBSD or USG systems. They key off A_UNDERLINE. X XThis was probably a waste of a day or so. But what the hack -- rewriting the Xstrategy robot as an FSM was fun, and maybe the interface will set a good Xexample for the next guy. X X<*** FLAME ON ***> X XPeople who write termcap games that require you to enter #@!!#$! coordinates Xrather than doing the natural pick-and-place with cursor motions should be Xstuffed in suits and condemned to write COBOL for the rest of their days... X X<*** FLAME OFF ***> X XO.K., I feel better now that I've got that off my chest... X X Eric S. Raymond X eric@snark.uu.net X END_OF_FILE if test 1342 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(218 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# Makefile for Battleships X XCFLAGS=-DNDEBUG X XLIBS = -lcurses -ltermcap X Xbs: bs.c X cc $(CFLAGS) -o bs bs.c $(LIBS) X Xlint: X lint bs.c $(LIBS) X Xclean: X rm -f bs bs.shar *~ X Xshar: X shar READ.ME bs.c Makefile bs.6 >bs.shar END_OF_FILE if test 218 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'bs.6' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'bs.6'\" else echo shar: Extracting \"'bs.6'\" \(1812 characters\) sed "s/^X//" >'bs.6' <<'END_OF_FILE' X.TH BATTLESHIPS 6 "Aug 23, 1989" X.SH NAME Xbs \- battleships game X.SH SYNOPSIS Xbattle [ -b | -s ] [ -c ] X.SH DESCRIPTION XThis program allows you to play the familiar Battleships game against the Xcomputer on a 10x10 board. The interface is visual and largely Xself-explanatory; you place your ships and pick your shots by moving the Xcursor around the `sea' with the rogue/hack motion keys hjklyubn. X.PP XNote that when selecting a ship to place, you must type the capital letter X(these are, after all, capital ships). During ship placement, the `r' command Xmay be used to ignore the current position and randomly place your currently Xselected ship. The `R' command will place all remaining ships randomly. The ^L Xcommand (form feed, ASCII 12) will force a screen redraw). X.PP XThe command-line arguments control game modes. X X.nf X -b selects a `blitz' variant X -s selects a `salvo' variant X -c permits ships to be placed adjacently X.fi X XThe `blitz' variant allows a side to shoot for as long as it continues to Xscore hits. X.PP XThe `salvo' game allows a player one shot per turn for each of his/her ships Xstill afloat. This puts a premium scoring hits early and knocking out some Xships and also makes much harder the situation where you face a superior force Xwith only your PT-boat. X.PP XNormally, ships must be separated by at least one square of open water. The X-c option disables this check and allows them to close-pack. X.PP XThe algorithm the computer uses once it has found a ship to sink is provably Xoptimal. The dispersion criterion for the random-fire algorithm may not be. X.SH AUTHORS XOriginally written by one Bruce Holloway in 1986. Salvo mode added by Chuck A. XDeGaul (cbosgd!cad). Visual user interface, `closepack' option, code rewrite Xand manual page by Eric S. Raymond (eric@snark.uu.net) August 1989. END_OF_FILE if test 1812 -ne `wc -c <'bs.6'`; then echo shar: \"'bs.6'\" unpacked with wrong size! fi # end of 'bs.6' fi if test -f 'bs.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'bs.c'\" else echo shar: Extracting \"'bs.c'\" \(25206 characters\) sed "s/^X//" >'bs.c' <<'END_OF_FILE' X/* X * bs.c - original author: Bruce Holloway X * salvo option by: Chuck A DeGaul X * with improved user interface, autoconfiguration and code cleanup X * by Eric S. Raymond X */ X X#include <stdio.h> X#include <curses.h> X#include <signal.h> X#include <ctype.h> X#include <assert.h> X X#ifndef A_UNDERLINE /* BSD curses */ X#define beep() write(1,"\007",1); X#define saveterm savetty X#define resetterm resetty X#ifndef cbreak /* Sun curses.h has cbreak() */ X#define cbreak crmode X#define nocbreak nocrmode X#endif X#define strchr index X#define chtype char X#define ungetch ungetc X#else X#define srand(n) srand48(n) X#define rand() lrand48() Xextern long lrand48(); Xextern void srand48(); X#define bzero(s, n) (void)memset((char *)(s), '\0', n) Xextern char *memset(); X#endif X Xextern unsigned sleep(); Xextern char *strchr(), *strcpy(); Xextern long time(); Xextern void exit(); X X/* X * Constants for tuning the random-fire algorithm. It prefers moves that X * diagonal-stripe the board with a stripe separation of srchstep. If X * no such preferred moves are found, srchstep is decremented. X */ X#define BEGINSTEP 3 /* initial value of srchstep */ X X/* miscellaneous constants */ X#define SHIPTYPES 5 X#define OTHER (1-turn) X#define PLAYER 0 X#define COMPUTER 1 X#define FF '\014' /* used as redraw command */ X#define MARK_HIT 'H' X#define MARK_MISS 'o' X X/* coordinate handling */ X#define BWIDTH 10 X#define BDEPTH 10 X X/* display symbols */ X#define SHOWHIT '*' X#define SHOWSPLASH ' ' X#define IS_SHIP(c) isupper(c) X X/* how to position us on player board */ X#define PYBASE 3 X#define PXBASE 3 X#define PY(y) (PYBASE + (y)) X#define PX(x) (PXBASE + (x)*3) X#define pgoto(y, x) (void)move(PY(y), PX(x)) X X/* how to position us on cpu board */ X#define CYBASE 3 X#define CXBASE 48 X#define CY(y) (CYBASE + (y)) X#define CX(x) (CXBASE + (x)*3) X#define cgoto(y, x) (void)move(CY(y), CX(x)) X X#define ONBOARD(x, y) (x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH) X X/* other board locations */ X#define COLWIDTH 80 X#define PROMPTLINE 21 /* prompt line */ X#define SYBASE CYBASE + BDEPTH + 3 /* move key diagram */ X#define SXBASE 63 X#define MYBASE SYBASE - 1 /* diagram caption */ X#define MXBASE 64 X#define HYBASE SYBASE - 1 /* help area */ X#define HXBASE 0 X X/* this will need to be changed if BWIDTH changes */ Xstatic char numbers[] = " 0 1 2 3 4 5 6 7 8 9"; X Xstatic char carrier[] = "Aircraft Carrier"; Xstatic char battle[] = "Battleship"; Xstatic char sub[] = "Submarine"; Xstatic char destroy[] = "Destroyer"; Xstatic char ptboat[] = "PT Boat"; X Xstatic char name[40]; Xstatic char dftname[] = "Stranger"; X X/* direction constants */ X#define E 0 X#define SE 1 X#define S 2 X#define SW 3 X#define W 4 X#define NW 5 X#define N 6 X#define NE 7 Xstatic int xincr[8] = {1, 1, 0, -1, -1, -1, 0, 1}; Xstatic int yincr[8] = {0, 1, 1, 1, 0, -1, -1, -1}; X X/* current ship position and direction */ Xstatic int curx = (BWIDTH / 2); Xstatic int cury = (BDEPTH / 2); X Xtypedef struct X{ X char *name; /* name of the ship type */ X unsigned hits; /* how many times has this ship been hit? */ X char symbol; /* symbol for game purposes */ X char length; /* length of ship */ X char x, y; /* coordinates of ship start point */ X char dir; /* direction of `bow' */ X bool placed; /* has it been placed on the board? */ X} Xship_t; X Xship_t plyship[SHIPTYPES] = X{ X { carrier, 0, 'A', 5}, X { battle, 0, 'B', 4}, X { destroy, 0, 'D', 3}, X { sub, 0, 'S', 3}, X { ptboat, 0, 'P', 2}, X}; X Xship_t cpuship[SHIPTYPES] = X{ X { carrier, 0, 'A', 5}, X { battle, 0, 'B', 4}, X { destroy, 0, 'D', 3}, X { sub, 0, 'S', 3}, X { ptboat, 0, 'P', 2}, X}; X X/* "Hits" board, and main board. */ Xstatic char hits[2][BWIDTH][BDEPTH], board[2][BWIDTH][BDEPTH]; X Xstatic int turn; /* 0=player, 1=computer */ Xstatic int plywon=0, cpuwon=0; /* How many games has each won? */ X Xstatic int salvo, blitz, closepack; X X#define PR (void)addstr X Xstatic void uninitgame() X/* end the game, either normally or due to signal */ X{ X clear(); X (void)refresh(); X (void)resetterm(); X (void)echo(); X (void)endwin(); X exit(0); X} X Xstatic void announceopts() X/* announce which game options are enabled */ X{ X if (salvo || blitz || closepack) X { X (void) printw("Playing optional game ("); X if (salvo) X (void) printw("salvo, "); X else X (void) printw("nosalvo, "); X if (blitz) X (void) printw("blitz "); X else X (void) printw("noblitz, "); X if (closepack) X (void) printw("closepack)"); X else X (void) printw("noclosepack)"); X } X else X (void) printw( X "Playing standard game (noblitz, nosalvo, noclosepack)"); X} X Xstatic void intro() X{ X extern char *getlogin(); X char *tmpname; X X srand(time(0L)+getpid()); /* Kick the random number generator */ X X (void) signal(SIGINT,uninitgame); X (void) signal(SIGINT,uninitgame); X (void) signal(SIGIOT,uninitgame); /* for assert(3) */ X if(signal(SIGQUIT,SIG_IGN) != SIG_IGN) X (void)signal(SIGQUIT,uninitgame); X X if(tmpname = getlogin()) X (void)strcpy(name,tmpname); X else X (void)strcpy(name,dftname); X name[0] = toupper(name[0]); X X (void)initscr(); X (void)saveterm(); X (void)nonl(); X (void)cbreak(); X (void)noecho(); X X#ifdef PENGUIN X (void)clear(); X (void)mvaddstr(4,29,"Welcome to Battleship!"); X (void)move(8,0); X PR(" \\\n"); X PR(" \\ \\ \\\n"); X PR(" \\ \\ \\ \\ \\_____________\n"); X PR(" \\ \\ \\_____________ \\ \\/ |\n"); X PR(" \\ \\/ \\ \\/ |\n"); X PR(" \\/ \\_____/ |__\n"); X PR(" ________________/ |\n"); X PR(" \\ S.S. Penguin |\n"); X PR(" \\ /\n"); X PR(" \\___________________________________________________/\n"); X X (void) mvaddstr(22,27,"Hit any key to continue..."); (void)refresh(); X (void) getch(); X#endif /* PENGUIN */ X X (void) clear(); X (void) mvaddstr(0,35,"BATTLESHIPS"); X (void) move(PROMPTLINE + 2, 0); X announceopts(); X X (void) mvaddstr(MYBASE, MXBASE, "Aiming keys:"); X (void) mvaddstr(SYBASE, SXBASE, "y k u 7 8 9"); X (void) mvaddstr(SYBASE+1, SXBASE, " \\|/ \\|/ "); X (void) mvaddstr(SYBASE+2, SXBASE, "h-+-l 4-+-6"); X (void) mvaddstr(SYBASE+3, SXBASE, " /|\\ /|\\ "); X (void) mvaddstr(SYBASE+4, SXBASE, "b j n 1 2 3"); X} X X/* VARARGS1 */ Xstatic void prompt(n, f, s) X/* print a message at the prompt line */ Xint n; Xchar *f, *s; X{ X (void) move(PROMPTLINE + n, 0); X (void) clrtoeol(); X (void) printw(f, s); X (void) refresh(); X} X Xstatic void error(s) Xchar *s; X{ X (void) move(PROMPTLINE + 2, 0); X (void) clrtoeol(); X if (s) X { X (void) addstr(s); X (void) beep(); X } X} X Xstatic void placeship(b, ss, vis) Xint b; Xship_t *ss; Xint vis; X{ X int l; X X for(l = 0; l < ss->length; ++l) X { X int newx = ss->x + l * xincr[ss->dir]; X int newy = ss->y + l * yincr[ss->dir]; X X board[b][newx][newy] = ss->symbol; X if (vis) X { X pgoto(newy, newx); X (void) addch((chtype)ss->symbol); X } X } X ss->hits = 0; X} X Xstatic void randomplace(b, ss) X/* generate a valid random ship placement into px,py */ Xint b; Xship_t *ss; X{ X register int bwidth = BWIDTH - ss->length; X register int bdepth = BDEPTH - ss->length; X X do { X ss->y = rnd(bdepth); X ss->x = rnd(bwidth); X ss->dir = rnd(2) ? E : S; X } while X (!checkplace(b, ss, FALSE)); X} X Xstatic void initgame() X{ X int i, unplaced; X ship_t *ss; X X bzero(board, sizeof(char) * BWIDTH * BDEPTH * 2); X bzero(hits, sizeof(char) * BWIDTH * BDEPTH * 2); X for (i = 0; i < SHIPTYPES; i++) X { X ss = cpuship + i; X ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0; X ss = plyship + i; X ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0; X } X X /* draw empty boards */ X (void) mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board"); X (void) mvaddstr(PYBASE - 1, PXBASE - 3,numbers); X for(i=0; i < BDEPTH; ++i) X (void) mvprintw(PYBASE + i, PXBASE - 3, X "%c . . . . . . . . . . %c",i+'A',i+'A'); X (void) mvaddstr(PYBASE + BDEPTH, PXBASE - 3,numbers); X (void) mvaddstr(CYBASE - 2, CXBASE + 7,"Hit/Miss Board"); X (void) mvaddstr(CYBASE - 1, CXBASE - 3, numbers); X for(i=0; i < BDEPTH; ++i) X (void) mvprintw(CYBASE + i, CXBASE - 3, X "%c . . . . . . . . . . %c",i+'A',i+'A'); X (void) mvaddstr(CYBASE + BDEPTH,CXBASE - 3,numbers); X X (void) mvprintw(HYBASE, HXBASE, X "To position your ships: move the cursor to a spot, then"); X (void) mvprintw(HYBASE+1,HXBASE, X "type the first letter of a ship type to select it, then"); X (void) mvprintw(HYBASE+2,HXBASE, X "type a direction ([hjkl] or [4862]), indicating how the"); X (void) mvprintw(HYBASE+3,HXBASE, X "ship should be pointed. You may also type a ship letter"); X (void) mvprintw(HYBASE+4,HXBASE, X "followed by `r' to position it randomly, or type `R' to"); X (void) mvprintw(HYBASE+5,HXBASE, X "place all remaining ships randomly."); X X /* have the computer place ships */ X for(ss = cpuship; ss < cpuship + SHIPTYPES; ss++) X { X randomplace(COMPUTER, ss); X placeship(COMPUTER, ss, FALSE); X } X X ss = (ship_t *)NULL; X do { X extern char *strchr(); X char c, docked[SHIPTYPES + 2], *cp = docked; X X /* figure which ships still wait to be placed */ X *cp++ = 'R'; X for (i = 0; i < SHIPTYPES; i++) X if (!plyship[i].placed) X *cp++ = plyship[i].symbol; X *cp = '\0'; X X /* get a command letter */ X prompt(1, "Type one of [%s] to pick a ship.", docked+1); X do { X c = getcoord(PLAYER); X } while X (!strchr(docked, c)); X X if (c == 'R') X (void) ungetch('R'); X else X { X /* map that into the corresponding symbol */ X for (ss = plyship; ss < plyship + SHIPTYPES; ss++) X if (ss->symbol == c) X break; X X prompt(1, "Type one of [hjklrR] to place your %s.", ss->name); X pgoto(cury, curx); X } X X do { X c = getch(); X } while X (!strchr("hjklrR", c) || c == FF); X X if (c == FF) X { X (void)clearok(stdscr, TRUE); X (void)refresh(); X } X else if (c == 'r') X { X prompt(1, "Random-placing your %s", ss->name); X randomplace(PLAYER, ss); X placeship(PLAYER, ss, TRUE); X error((char *)NULL); X ss->placed = TRUE; X } X else if (c == 'R') X { X prompt(1, "Placing the rest of your fleet at random..."); X for (ss = plyship; ss < plyship + SHIPTYPES; ss++) X if (!ss->placed) X { X randomplace(PLAYER, ss); X placeship(PLAYER, ss, TRUE); X ss->placed = TRUE; X } X error((char *)NULL); X } X else if (strchr("hjkl8462", c)) X { X ss->x = curx; X ss->y = cury; X X switch(c) X { X case 'k': case '8': ss->dir = N; break; X case 'j': case '2': ss->dir = S; break; X case 'h': case '4': ss->dir = W; break; X case 'l': case '6': ss->dir = E; break; X } X X if (checkplace(PLAYER, ss, TRUE)) X { X placeship(PLAYER, ss, TRUE); X error((char *)NULL); X ss->placed = TRUE; X } X } X X for (unplaced = i = 0; i < SHIPTYPES; i++) X unplaced += !plyship[i].placed; X } while X (unplaced); X X turn = rnd(2); X X (void) mvprintw(HYBASE, HXBASE, X "To fire, move the cursor to your chosen aiming point "); X (void) mvprintw(HYBASE+1, HXBASE, X "and strike any key other than a motion key. "); X (void) mvprintw(HYBASE+2, HXBASE, X " "); X (void) mvprintw(HYBASE+3, HXBASE, X " "); X (void) mvprintw(HYBASE+4, HXBASE, X " "); X (void) mvprintw(HYBASE+5, HXBASE, X " "); X X (void) prompt(0, "Press any key to start..."); X (void) getch(); X} X Xstatic int rnd(n) Xint n; X{ X return(((rand() & 0x7FFF) % n)); X} X Xstatic int getcoord(atcpu) Xint atcpu; X{ X int ny, nx, c; X X if (atcpu) X cgoto(cury,curx); X else X pgoto(cury, curx); X (void)refresh(); X for (;;) X { X if (atcpu) X { X (void) mvprintw(CYBASE + BDEPTH+1, CXBASE+11, "(%d, %c)", curx, 'A'+cury); X cgoto(cury, curx); X } X else X { X (void) mvprintw(PYBASE + BDEPTH+1, PXBASE+11, "(%d, %c)", curx, 'A'+cury); X pgoto(cury, curx); X } X (void)refresh(); X X switch(c = getch()) X { X case 'k': case '8': ny = cury+BDEPTH-1; nx = curx; break; X case 'j': case '2': ny = cury+1; nx = curx; break; X case 'h': case '4': ny = cury; nx = curx+BWIDTH-1; break; X case 'l': case '6': ny = cury; nx = curx+1; break; X case 'y': case '7': ny = cury+BDEPTH-1; nx = curx+BWIDTH-1; break; X case 'b': case '1': ny = cury+1; nx = curx+BWIDTH-1; break; X case 'u': case '9': ny = cury+BDEPTH-1; nx = curx+1; break; X case 'n': case '3': ny = cury+1; nx = curx+1; break; X case '\014': (void)clearok(stdscr, TRUE); (void)refresh(); break; X default: X if (atcpu) X (void) mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, " "); X else X (void) mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, " "); X return(c); X } X X curx = nx % BWIDTH; X cury = ny % BDEPTH; X } X} X Xstatic int collidecheck(b, y, x) X/* is this location on the selected zboard adjacent to a ship? */ Xint b; Xint y, x; X{ X int collide; X X /* anything on the square */ X if (collide = IS_SHIP(board[b][x][y])) X return(collide); X X /* anything on the neighbors */ X if (!closepack) X { X int i; X X for (i = 0; i < 8; i++) X { X int xend, yend; X X yend = y + yincr[i]; X xend = x + xincr[i]; X if (ONBOARD(xend, yend)) X collide += IS_SHIP(board[b][xend][yend]); X } X } X return(collide); X} X Xstatic int checkplace(b, ss, vis) Xint b; Xship_t *ss; Xint vis; X{ X int l, xend, yend; X X /* first, check for board edges */ X xend = ss->x + ss->length * xincr[ss->dir]; X yend = ss->y + ss->length * yincr[ss->dir]; X if (!ONBOARD(xend, yend)) X { X if(vis) X switch(rnd(3)) X { X case 0: X error("Ship is hanging from the edge of the world"); X break; X case 1: X error("Try fitting it on the board"); X break; X case 2: X error("Figure I won't find it if you put it there?"); X break; X } X return(0); X } X X for(l = 0; l < ss->length; ++l) X { X if(collidecheck(b, ss->y+l*yincr[ss->dir], ss->x+l*xincr[ss->dir])) X { X if (vis) X switch(rnd(3)) X { X case 0: X error("There's already a ship there"); X break; X case 1: X error("Collision alert! Aaaaaagh!"); X break; X case 2: X error("Er, Admiral, what about the other ship?"); X break; X } X return(0); X } X } X return(1); X} X Xstatic int awinna() X{ X int i, j; X ship_t *ss; X X for(i=0; i<2; ++i) X { X ss = (i) ? cpuship : plyship; X for(j=0; j < SHIPTYPES; ++j, ++ss) X if(ss->length > ss->hits) X break; X if (j == SHIPTYPES) X return(OTHER); X } X return(-1); X} X Xstatic ship_t *hitship(x, y) X/* register a hit on the targeted ship */ Xint x, y; X{ X ship_t *sb, *ss; X char sym; X int oldx, oldy; X X getyx(stdscr, oldy, oldx); X sb = (turn) ? plyship : cpuship; X if(!(sym = board[OTHER][x][y])) X return((ship_t *)NULL); X for(ss = sb; ss < sb + SHIPTYPES; ++ss) X if(ss->symbol == sym) X { X if (++ss->hits < ss->length) /* still afloat? */ X return((ship_t *)NULL); X else /* sunk! */ X { X int i, j; X X if (!closepack) X for (j = -1; j <= 1; j++) X { X int bx = ss->x + j * xincr[(ss->dir + 2) % 8]; X int by = ss->y + j * yincr[(ss->dir + 2) % 8]; X X for (i = -1; i <= ss->length; ++i) X { X int x, y; X X x = bx + i * xincr[ss->dir]; X y = by + i * yincr[ss->dir]; X if (ONBOARD(x, y)) X { X hits[turn][x][y] = MARK_MISS; X if (turn % 2 == PLAYER) X { X cgoto(y, x); X (void)addch(MARK_MISS); X } X } X } X } X X for (i = 0; i < ss->length; ++i) X { X int x = ss->x + i * xincr[ss->dir]; X int y = ss->y + i * yincr[ss->dir]; X X hits[turn][x][y] = ss->symbol; X if (turn % 2 == PLAYER) X { X cgoto(y, x); X (void) addch(ss->symbol); X } X } X X (void) move(oldy, oldx); X return(ss); X } X } X (void) move(oldy, oldx); X return((ship_t *)NULL); X} X Xstatic int plyturn() X{ X ship_t *ss; X bool hit; X char *m; X X prompt(1, "Where do you want to shoot? "); X for (;;) X { X (void) getcoord(COMPUTER); X if (hits[PLAYER][curx][cury]) X { X prompt(1, "You shelled this spot already! Try again."); X beep(); X } X else X break; X } X hit = IS_SHIP(board[COMPUTER][curx][cury]); X hits[PLAYER][curx][cury] = hit ? MARK_HIT : MARK_MISS; X cgoto(cury, curx); X (void) addch((chtype)hits[PLAYER][curx][cury]); X X prompt(1, "You %s.", hit ? "scored a hit" : "missed"); X if(hit && (ss = hitship(curx, cury))) X { X switch(rnd(5)) X { X case 0: X m = " You sank my %s!"; X break; X case 1: X m = " I have this sinking feeling about my %s...."; X break; X case 2: X m = " My %s has gone to Davy Jones's locker!"; X break; X case 3: X m = " Glub, glub -- my %s is headed for the bottom!"; X break; X case 4: X m = " You'll pick up survivors from my my %s, I hope...!"; X break; X } X (void)printw(m, ss->name); X (void)beep(); X return(awinna() == -1); X } X return(hit); X} X Xstatic int sgetc(s) Xchar *s; X{ X char *s1; X int ch; X X (void)refresh(); X for(;;) X { X ch = toupper(getch()); X if (ch == 3) X uninitgame(); X for (s1=s; *s1 && ch != *s1; ++s1) X continue; X if (*s1) X { X (void) addch((chtype)ch); X (void)refresh(); X return(ch); X } X } X} X X Xstatic void randomfire(px, py) X/* random-fire routine -- implements simple diagonal-striping strategy */ Xint *px, *py; X{ X static int turncount = 0; X static int srchstep = BEGINSTEP; X static int huntoffs; /* Offset on search strategy */ X int ypossible[BWIDTH * BDEPTH], xpossible[BWIDTH * BDEPTH], nposs; X int ypreferred[BWIDTH * BDEPTH], xpreferred[BWIDTH * BDEPTH], npref; X int x, y, i; X X if (turncount++ == 0) X huntoffs = rnd(srchstep); X X /* first, list all possible moves */ X nposs = npref = 0; X for (x = 0; x < BWIDTH; x++) X for (y = 0; y < BDEPTH; y++) X if (!hits[COMPUTER][x][y]) X { X xpossible[nposs] = x; X ypossible[nposs] = y; X nposs++; X if (((x+huntoffs) % srchstep) != (y % srchstep)) X { X xpreferred[npref] = x; X ypreferred[npref] = y; X npref++; X } X } X X if (npref) X { X i = rnd(npref); X X *px = xpreferred[i]; X *py = ypreferred[i]; X } X else if (nposs) X { X i = rnd(nposs); X X *px = xpossible[i]; X *py = ypossible[i]; X X if (srchstep > 1) X --srchstep; X } X else X { X error("No moves possible?? Help!"); X exit(1); X /*NOTREACHED*/ X } X} X X#define S_MISS 0 X#define S_HIT 1 X#define S_SUNK -1 X Xstatic bool cpufire(x, y) X/* fire away at given location */ Xint x, y; X{ X bool hit, sunk; X ship_t *ss; X X hits[COMPUTER][x][y] = (hit = (board[PLAYER][x][y])) ? MARK_HIT : MARK_MISS; X (void) mvprintw(PROMPTLINE, 0, X "I shoot at %c%d. I %s!", y + 'A', x, hit ? "hit" : "miss"); X if (sunk = (hit && (ss = hitship(x, y)))) X (void) printw(" I've sunk your %s", ss->name); X (void)clrtoeol(); X X pgoto(y, x); X (void)addch((chtype)(hit ? SHOWHIT : SHOWSPLASH)); X X return(hit ? (sunk ? S_SUNK : S_HIT) : S_MISS); X} X X/* X * This code implements a fairly irregular FSM, so please forgive the rampant X * unstructuredness below. The five labels are states which need to be held X * between computer turns. X */ Xstatic bool cputurn() X{ X#define POSSIBLE(x, y) (ONBOARD(x, y) && !hits[COMPUTER][x][y]) X#define RANDOM_FIRE 0 X#define RANDOM_HIT 1 X#define HUNT_DIRECT 2 X#define FIRST_PASS 3 X#define REVERSE_JUMP 4 X#define SECOND_PASS 5 X static int next = RANDOM_FIRE; X static bool used[4]; X static ship_t ts; X int navail, x, y, d, n, hit = S_MISS; X X switch(next) X { X case RANDOM_FIRE: /* last shot was random and missed */ X refire: X randomfire(&x, &y); X if (!(hit = cpufire(x, y))) X next = RANDOM_FIRE; X else X { X ts.x = x; ts.y = y; X ts.hits = 1; X next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT; X } X break; X X case RANDOM_HIT: /* last shot was random and hit */ X used[E/2] = used[S/2] = used[W/2] = used[N/2] = FALSE; X /* FALLTHROUGH */ X X case HUNT_DIRECT: /* last shot hit, we're looking for ship's long axis */ X for (d = navail = 0; d < 4; d++) X { X x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2]; X if (!used[d] && POSSIBLE(x, y)) X navail++; X else X used[d] = TRUE; X } X if (navail == 0) /* no valid places for shots adjacent... */ X goto refire; /* ...so we must random-fire */ X else X { X for (d = 0, n = rnd(navail) + 1; n; n--) X while (used[d]) X d++; X X assert(d <= 4); X X used[d] = FALSE; X x = ts.x + xincr[d*2]; X y = ts.y + yincr[d*2]; X X assert(POSSIBLE(x, y)); X X if (!(hit = cpufire(x, y))) X next = HUNT_DIRECT; X else X { X ts.x = x; ts.y = y; ts.dir = d*2; ts.hits++; X next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS; X } X } X break; X X case FIRST_PASS: /* we have a start and a direction now */ X x = ts.x + xincr[ts.dir]; X y = ts.y + yincr[ts.dir]; X if (POSSIBLE(x, y) && (hit = cpufire(x, y))) X { X ts.x = x; ts.y = y; ts.hits++; X next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS; X } X else X next = REVERSE_JUMP; X break; X X case REVERSE_JUMP: /* nail down the ship's other end */ X d = ts.dir + 4; X x = ts.x + ts.hits * xincr[d]; X y = ts.y + ts.hits * yincr[d]; X if (POSSIBLE(x, y) && (hit = cpufire(x, y))) X { X ts.x = x; ts.y = y; ts.dir = d; ts.hits++; X next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS; X } X else X next = RANDOM_FIRE; X break; X X case SECOND_PASS: /* kill squares not caught on first pass */ X x = ts.x + xincr[ts.dir]; X y = ts.y + yincr[ts.dir]; X if (POSSIBLE(x, y) && (hit = cpufire(x, y))) X { X ts.x = x; ts.y = y; ts.hits++; X next = (hit == S_SUNK) ? RANDOM_FIRE: SECOND_PASS; X break; X } X else X next = RANDOM_FIRE; X break; X } X X /* check for continuation and/or winner */ X if (salvo) X { X (void)refresh(); X (void)sleep(1); X } X if (awinna() != -1) X return(FALSE); X X#ifdef DEBUG X (void) mvprintw(PROMPTLINE + 2, 0, X "New state %d, x=%d, y=%d, d=%d", X next, x, y, d); X#endif /* DEBUG */ X return(hit); X} X Xplayagain() X{ X int j; X ship_t *ss; X X for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++) X for(j = 0; j < ss->length; j++) X { X cgoto(ss->y + j * yincr[ss->dir], ss->x + j * xincr[ss->dir]); X (void)addch((chtype)ss->symbol); X } X X if(awinna()) X ++cpuwon; X else X ++plywon; X j = 18 + strlen(name); X if(plywon >= 10) X ++j; X if(cpuwon >= 10) X ++j; X (void) mvprintw(1,(COLWIDTH-j)/2, X "%s: %d Computer: %d",name,plywon,cpuwon); X X prompt(2, (awinna()) ? "Want to be humiliated again, %s [yn]? " X : "Going to give me a chance for revenge, %s [yn]? ",name); X return(sgetc("YN") == 'Y'); X} X Xstatic void do_options(c,op) Xint c; Xchar *op[]; X{ X register int i; X X if (c > 1) X { X for (i=1; i<c; i++) X { X switch(op[i][0]) X { X default: X case '?': X (void) fprintf(stderr, "Usage: battle [-s | -b] [-c]\n"); X (void) fprintf(stderr, "\tWhere the options are:\n"); X (void) fprintf(stderr, "\t-s : play a salvo game\n"); X (void) fprintf(stderr, "\t-b : play a blitz game\n"); X (void) fprintf(stderr, "\t-c : ships may be adjacent\n"); X exit(1); X break; X case '-': X switch(op[i][1]) X { X case 'b': X blitz = 1; X if (salvo == 1) X { X (void) fprintf(stderr, X "Bad Arg: -b and -s are mutually exclusive\n"); X exit(1); X } X break; X case 's': X salvo = 1; X if (blitz == 1) X { X (void) fprintf(stderr, X "Bad Arg: -s and -b are mutually exclusive\n"); X exit(1); X } X break; X case 'c': X closepack = 1; X break; X default: X (void) fprintf(stderr, X "Bad arg: type \"%s ?\" for usage message\n", op[0]); X exit(1); X } X } X } X } X} X Xstatic int scount(who) Xint who; X{ X register int i, shots; X register ship_t *sp; X X if (who) X sp = cpuship; /* count cpu shots */ X else X sp = plyship; /* count player shots */ X X for (i=0, shots = 0; i < SHIPTYPES; i++, sp++) X { X if (sp->hits >= sp->length) X continue; /* dead ship */ X else X shots++; X } X return(shots); X} X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X do_options(argc, argv); X X intro(); X do { X initgame(); X while(awinna() == -1) X { X if (!blitz) X { X if (!salvo) X { X if(turn) X (void) cputurn(); X else X (void) plyturn(); X } X else X { X register int i; X X i = scount(turn); X while (i--) X { X if (turn) X { X if (cputurn() && awinna() != -1) X i = 0; X } X else X { X if (plyturn() && awinna() != -1) X i = 0; X } X } X } X } X else X while(turn ? cputurn() : plyturn()) X continue; X turn = OTHER; X } X } while X (playagain()); X uninitgame(); X /*NOTREACHED*/ X} X X/* bs.c ends here */ END_OF_FILE if test 25206 -ne `wc -c <'bs.c'`; then echo shar: \"'bs.c'\" unpacked with wrong size! fi # end of 'bs.c' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0