xref: /openbsd-src/games/bs/bs.c (revision bda84ce940729ea62ecb251ada05533d1b1163fc)
1*bda84ce9Smestre /*	$OpenBSD: bs.c,v 1.42 2021/10/23 11:22:48 mestre Exp $	*/
23eb18cd7Spjanzen /*
33eb18cd7Spjanzen  * Copyright (c) 1986, Bruce Holloway
43eb18cd7Spjanzen  * All rights reserved.
53eb18cd7Spjanzen  *
63eb18cd7Spjanzen  * Redistribution and use in source and binary forms, with or without
73eb18cd7Spjanzen  * modification, are permitted provided that the following conditions are
83eb18cd7Spjanzen  * met:
93eb18cd7Spjanzen  *
103eb18cd7Spjanzen  * - Redistributions of source code must retain the above copyright
113eb18cd7Spjanzen  *  notice, this list of conditions and the following disclaimer.
123eb18cd7Spjanzen  * - Redistributions in binary form must reproduce the above copyright
133eb18cd7Spjanzen  * notice, this list of conditions and the following disclaimer in the
143eb18cd7Spjanzen  * documentation and/or other materials provided with the distribution.
153eb18cd7Spjanzen  * - Neither the name of the <ORGANIZATION> nor the names of its
163eb18cd7Spjanzen  * contributors may be used to endorse or promote products derived from
173eb18cd7Spjanzen  * this software without specific prior written permission.
183eb18cd7Spjanzen  *
193eb18cd7Spjanzen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
203eb18cd7Spjanzen  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
213eb18cd7Spjanzen  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
223eb18cd7Spjanzen  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
233eb18cd7Spjanzen  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
243eb18cd7Spjanzen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
253eb18cd7Spjanzen  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
263eb18cd7Spjanzen  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
273eb18cd7Spjanzen  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
283eb18cd7Spjanzen  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
293eb18cd7Spjanzen  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
303eb18cd7Spjanzen  */
317f482600Sdownsj /*
327f482600Sdownsj  * bs.c - original author: Bruce Holloway
337f482600Sdownsj  *		salvo option by: Chuck A DeGaul
347f482600Sdownsj  * with improved user interface, autoconfiguration and code cleanup
357f482600Sdownsj  *		by Eric S. Raymond <esr@snark.thyrsus.com>
367f482600Sdownsj  * v1.2 with color support and minor portability fixes, November 1990
377f482600Sdownsj  * v2.0 featuring strict ANSI/POSIX conformance, November 1993.
38efc3f44dSpjanzen  * v2.1 with ncurses mouse support, September 1995
39efc3f44dSpjanzen  * v2.2 with bugfixes and strategical improvements, March 1998.
407f482600Sdownsj  */
41ecbe4913Spjanzen 
42a60bcfa4Spjanzen #include <ctype.h>
4344d3f3fbSmestre #include <curses.h>
44a60bcfa4Spjanzen #include <err.h>
4534278d36Sguenther #include <limits.h>
46a60bcfa4Spjanzen #include <signal.h>
47a60bcfa4Spjanzen #include <stdlib.h>
48f7d92492Sniklas #include <string.h>
49a60bcfa4Spjanzen #include <unistd.h>
507f482600Sdownsj 
5115d5404dStb typedef struct {
5215d5404dStb 	char *name;		/* name of the ship type */
5315d5404dStb 	char hits;		/* how many times has this ship been hit? */
5415d5404dStb 	char symbol;		/* symbol for game purposes */
5515d5404dStb 	char length;		/* length of ship */
5615d5404dStb 	signed char x, y;	/* coordinates of ship start point */
5715d5404dStb 	unsigned char dir;	/* direction of `bow' */
5815d5404dStb 	bool placed;		/* has it been placed on the board? */
5915d5404dStb } ship_t;
6015d5404dStb 
6115d5404dStb static void	 announceopts(void);
6215d5404dStb static int	 awinna(void);
6315d5404dStb static bool	 checkplace(int, ship_t *, int);
6415d5404dStb static int	 collidecheck(int, int, int);
6515d5404dStb static int	 cpufire(int, int);
6615d5404dStb static bool	 cpushipcanfit(int, int, int, int);
6715d5404dStb static int	 cputurn(void);
6815d5404dStb static void	 do_options(int, char *[]);
6915d5404dStb static void	 error(char *);
7015d5404dStb static int	 getcoord(int);
7115d5404dStb static ship_t	*hitship(int, int);
7215d5404dStb static void	 initgame(void);
7315d5404dStb static void	 intro(void);
7415d5404dStb static void	 placeship(int, ship_t *, int);
7515d5404dStb static int	 playagain(void);
7615d5404dStb static int	 plyturn(void);
7715d5404dStb static void	 prompt(int, const char *, ...)
7815d5404dStb     __attribute__((__format__ (printf, 2, 3)));
7915d5404dStb static void	 randomfire(int *, int *);
8015d5404dStb static void	 randomplace(int, ship_t *);
8115d5404dStb static int	 rnd(int);
8215d5404dStb static int	 scount(int);
8315d5404dStb static int	 sgetc(char *);
847ad55f55Smestre __dead static void	 uninitgame(int);
8515d5404dStb __dead void	 usage(void);
867f482600Sdownsj 
877f482600Sdownsj /*
887f482600Sdownsj  * Constants for tuning the random-fire algorithm. It prefers moves that
897f482600Sdownsj  * diagonal-stripe the board with a stripe separation of srchstep. If
907f482600Sdownsj  * no such preferred moves are found, srchstep is decremented.
917f482600Sdownsj  */
927f482600Sdownsj #define BEGINSTEP	3	/* initial value of srchstep */
937f482600Sdownsj 
947f482600Sdownsj /* miscellaneous constants */
957f482600Sdownsj #define SHIPTYPES	5
967f482600Sdownsj #define OTHER		(1-turn)
977f482600Sdownsj #define PLAYER		0
987f482600Sdownsj #define COMPUTER	1
997f482600Sdownsj #define MARK_HIT	'H'
1007f482600Sdownsj #define MARK_MISS	'o'
1017f482600Sdownsj #define CTRLC		'\003'	/* used as terminate command */
1027f482600Sdownsj #define FF		'\014'	/* used as redraw command */
1037f482600Sdownsj 
1047f482600Sdownsj /* coordinate handling */
1057f482600Sdownsj #define BWIDTH		10
1067f482600Sdownsj #define BDEPTH		10
1077f482600Sdownsj 
1087f482600Sdownsj /* display symbols */
1097f482600Sdownsj #define SHOWHIT		'*'
1107f482600Sdownsj #define SHOWSPLASH	' '
1117f482600Sdownsj #define IS_SHIP(c)	isupper(c)
1127f482600Sdownsj 
1137f482600Sdownsj /* how to position us on player board */
1147f482600Sdownsj #define PYBASE	3
1157f482600Sdownsj #define PXBASE	3
1167f482600Sdownsj #define PY(y)	(PYBASE + (y))
1177f482600Sdownsj #define PX(x)	(PXBASE + (x)*3)
1187f482600Sdownsj #define pgoto(y, x)	(void)move(PY(y), PX(x))
1197f482600Sdownsj 
1207f482600Sdownsj /* how to position us on cpu board */
1217f482600Sdownsj #define CYBASE	3
1227f482600Sdownsj #define CXBASE	48
1237f482600Sdownsj #define CY(y)	(CYBASE + (y))
1247f482600Sdownsj #define CX(x)	(CXBASE + (x)*3)
125efc3f44dSpjanzen #define CYINV(y)	((y) - CYBASE)
126efc3f44dSpjanzen #define CXINV(x)	(((x) - CXBASE) / 3)
1277f482600Sdownsj #define cgoto(y, x)	(void)move(CY(y), CX(x))
1287f482600Sdownsj 
1297f482600Sdownsj #define ONBOARD(x, y)	(x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH)
1307f482600Sdownsj 
1317f482600Sdownsj /* other board locations */
1327f482600Sdownsj #define COLWIDTH	80
1337f482600Sdownsj #define PROMPTLINE	21			/* prompt line */
1347f482600Sdownsj #define SYBASE		CYBASE + BDEPTH + 3	/* move key diagram */
1357f482600Sdownsj #define SXBASE		63
1367f482600Sdownsj #define MYBASE		SYBASE - 1		/* diagram caption */
1377f482600Sdownsj #define MXBASE		64
1387f482600Sdownsj #define HYBASE		SYBASE - 1		/* help area */
1397f482600Sdownsj #define HXBASE		0
1407f482600Sdownsj 
1417f482600Sdownsj /* this will need to be changed if BWIDTH changes */
1427f482600Sdownsj static char numbers[] = "   0  1  2  3  4  5  6  7  8  9";
1437f482600Sdownsj 
1447f482600Sdownsj static char carrier[] = "Aircraft Carrier";
1457f482600Sdownsj static char battle[] = "Battleship";
1467f482600Sdownsj static char sub[] = "Submarine";
1477f482600Sdownsj static char destroy[] = "Destroyer";
1487f482600Sdownsj static char ptboat[] = "PT Boat";
1497f482600Sdownsj 
15034278d36Sguenther static char name[LOGIN_NAME_MAX];
1517f482600Sdownsj static char dftname[] = "stranger";
1527f482600Sdownsj 
1537f482600Sdownsj /* direction constants */
1547f482600Sdownsj #define E	0
1557f482600Sdownsj #define SE	1
1567f482600Sdownsj #define S	2
1577f482600Sdownsj #define SW	3
1587f482600Sdownsj #define W	4
1597f482600Sdownsj #define NW	5
1607f482600Sdownsj #define N	6
1617f482600Sdownsj #define NE	7
1627f482600Sdownsj static int xincr[8] = { 1,  1,  0, -1, -1, -1,  0,  1 };
1637f482600Sdownsj static int yincr[8] = { 0,  1,  1,  1,  0, -1, -1, -1 };
1647f482600Sdownsj 
1657f482600Sdownsj /* current ship position and direction */
1667f482600Sdownsj static int curx = (BWIDTH / 2);
1677f482600Sdownsj static int cury = (BDEPTH / 2);
1687f482600Sdownsj 
1697f482600Sdownsj ship_t plyship[SHIPTYPES] =
1707f482600Sdownsj {
171e29de8b1Spjanzen 	{ carrier,	0, 'A', 5, 0, 0, 0, FALSE },
172e29de8b1Spjanzen 	{ battle,	0, 'B', 4, 0, 0, 0, FALSE },
173e29de8b1Spjanzen 	{ destroy,	0, 'D', 3, 0, 0, 0, FALSE },
174e29de8b1Spjanzen 	{ sub,		0, 'S', 3, 0, 0, 0, FALSE },
175e29de8b1Spjanzen 	{ ptboat,	0, 'P', 2, 0, 0, 0, FALSE }
1767f482600Sdownsj };
1777f482600Sdownsj 
1787f482600Sdownsj ship_t cpuship[SHIPTYPES] =
1797f482600Sdownsj {
180e29de8b1Spjanzen 	{ carrier,	0, 'A', 5, 0, 0, 0, FALSE },
181e29de8b1Spjanzen 	{ battle,	0, 'B', 4, 0, 0, 0, FALSE },
182e29de8b1Spjanzen 	{ destroy,	0, 'D', 3, 0, 0, 0, FALSE },
183e29de8b1Spjanzen 	{ sub,		0, 'S', 3, 0, 0, 0, FALSE },
184e29de8b1Spjanzen 	{ ptboat,	0, 'P', 2, 0, 0, 0, FALSE }
1857f482600Sdownsj };
1867f482600Sdownsj 
187efc3f44dSpjanzen /* The following variables (and associated defines), used for computer
188efc3f44dSpjanzen  * targetting, must be global so that they can be reset for each new game
189efc3f44dSpjanzen  * played without restarting the program.
190efc3f44dSpjanzen  */
191efc3f44dSpjanzen #define POSSIBLE(x, y)	(ONBOARD(x, y) && !hits[COMPUTER][x][y])
192efc3f44dSpjanzen #define RANDOM_FIRE	0
193efc3f44dSpjanzen #define RANDOM_HIT	1
194efc3f44dSpjanzen #define HUNT_DIRECT	2
195efc3f44dSpjanzen #define FIRST_PASS	3
196efc3f44dSpjanzen #define REVERSE_JUMP	4
197efc3f44dSpjanzen #define SECOND_PASS	5
198efc3f44dSpjanzen     static int next = RANDOM_FIRE;
199efc3f44dSpjanzen     static int turncount = 0;
200efc3f44dSpjanzen     static int srchstep = BEGINSTEP;
201efc3f44dSpjanzen /* Computer needs to keep track of longest and shortest player ships still
202efc3f44dSpjanzen  * not sunk, for better targetting.
203efc3f44dSpjanzen  */
204efc3f44dSpjanzen static int cpushortest;
205efc3f44dSpjanzen static int cpulongest;
206efc3f44dSpjanzen 
2077f482600Sdownsj /* "Hits" board, and main board. */
2087f482600Sdownsj static char hits[2][BWIDTH][BDEPTH], board[2][BWIDTH][BDEPTH];
2097f482600Sdownsj 
2107f482600Sdownsj static int turn;			/* 0=player, 1=computer */
2117f482600Sdownsj static int plywon=0, cpuwon=0;		/* How many games has each won? */
2127f482600Sdownsj 
2137f482600Sdownsj static int salvo, blitz, closepack;
2147f482600Sdownsj 
2157f482600Sdownsj /* end the game, either normally or due to signal */
216cd84dbdaSmestre static void
uninitgame(int sig)217cd84dbdaSmestre uninitgame(int sig)
2187f482600Sdownsj {
2197f482600Sdownsj     clear();
2207f482600Sdownsj     (void)refresh();
2217f482600Sdownsj     (void)resetterm();
2227f482600Sdownsj     (void)echo();
2237f482600Sdownsj     (void)endwin();
224efc3f44dSpjanzen     exit(sig);
2257f482600Sdownsj }
2267f482600Sdownsj 
2277f482600Sdownsj /* announce which game options are enabled */
228cd84dbdaSmestre static void
announceopts(void)229cd84dbdaSmestre announceopts(void)
2307f482600Sdownsj {
2317f482600Sdownsj     if (salvo || blitz || closepack)
2327f482600Sdownsj     {
2337f482600Sdownsj 	(void) printw("Playing optional game (");
2347f482600Sdownsj 	if (salvo)
2357f482600Sdownsj 	    (void) printw("salvo, ");
2367f482600Sdownsj 	else
2377f482600Sdownsj 	    (void) printw("nosalvo, ");
2387f482600Sdownsj 	if (blitz)
2397f482600Sdownsj 	    (void) printw("blitz ");
2407f482600Sdownsj 	else
2417f482600Sdownsj 	    (void) printw("noblitz, ");
2427f482600Sdownsj 	if (closepack)
2437f482600Sdownsj 	    (void) printw("closepack)");
2447f482600Sdownsj 	else
2457f482600Sdownsj 	    (void) printw("noclosepack)");
2467f482600Sdownsj     }
2477f482600Sdownsj     else
2487f482600Sdownsj 	(void) printw(
2497f482600Sdownsj 	"Playing standard game (noblitz, nosalvo, noclosepack)");
2507f482600Sdownsj }
2517f482600Sdownsj 
252cd84dbdaSmestre static void
intro(void)253cd84dbdaSmestre intro(void)
2547f482600Sdownsj {
2557f482600Sdownsj     char *tmpname;
2567f482600Sdownsj 
2577f482600Sdownsj     (void) signal(SIGINT,uninitgame);
2587f482600Sdownsj     if(signal(SIGQUIT,SIG_IGN) != SIG_IGN)
2597f482600Sdownsj 	(void)signal(SIGQUIT,uninitgame);
2607f482600Sdownsj 
261e29de8b1Spjanzen     if ((tmpname = getlogin()) != NULL)
2627f482600Sdownsj     {
263e29de8b1Spjanzen 	(void)strlcpy(name, tmpname, sizeof(name));
264866f718fSmmcc 	name[0] = toupper((unsigned char)name[0]);
2657f482600Sdownsj     }
2667f482600Sdownsj     else
267e29de8b1Spjanzen 	(void)strlcpy(name, dftname, sizeof(name));
2687f482600Sdownsj 
2697f482600Sdownsj     (void)initscr();
2707f482600Sdownsj     keypad(stdscr, TRUE);
2717f482600Sdownsj     (void)saveterm();
2727f482600Sdownsj     (void)nonl();
2737f482600Sdownsj     (void)cbreak();
2747f482600Sdownsj     (void)noecho();
2757f482600Sdownsj 
276a60bcfa4Spjanzen     if ((LINES < PROMPTLINE + 3) || (COLS < COLWIDTH)) {
277a60bcfa4Spjanzen 	endwin();
278a60bcfa4Spjanzen 	errx(1, "screen must be at least %dx%d.", PROMPTLINE + 3, COLWIDTH);
279a60bcfa4Spjanzen     }
280a60bcfa4Spjanzen 
281efc3f44dSpjanzen #define	PR	(void)addstr
2827f482600Sdownsj     (void)clear();
2837f482600Sdownsj     (void)mvaddstr(4,29,"Welcome to Battleship!");
2847f482600Sdownsj     (void)move(8,0);
2857f482600Sdownsj     PR("                                                  \\\n");
2867f482600Sdownsj     PR("                           \\                     \\ \\\n");
2877f482600Sdownsj     PR("                          \\ \\                   \\ \\ \\_____________\n");
2887f482600Sdownsj     PR("                         \\ \\ \\_____________      \\ \\/            |\n");
28968413d08Stedu     PR("                          \\ \\/     \\__/    \\      \\/             |\n");
29068413d08Stedu     PR("                           \\/     \\/  \\/    \\_____/              |__\n");
29168413d08Stedu     PR("           ________________/    /\\/  ..\\/                         |\n");
29268413d08Stedu     PR("           \\  S.S. Puffy        \\/\\___o/                          |\n");
29368413d08Stedu     PR("            \\                     / /\\ \\                          /\n");
2947f482600Sdownsj     PR("             \\___________________________________________________/\n");
2957f482600Sdownsj 
2967f482600Sdownsj     (void) mvaddstr(22,27,"Hit any key to continue..."); (void)refresh();
2977f482600Sdownsj     (void) getch();
2987f482600Sdownsj 
2997f482600Sdownsj     start_color();
3007f482600Sdownsj 
3017f482600Sdownsj     init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
3027f482600Sdownsj     init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
3037f482600Sdownsj     init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
3047f482600Sdownsj     init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
3057f482600Sdownsj     init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
3067f482600Sdownsj     init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
3077f482600Sdownsj     init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
3087f482600Sdownsj     init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
3097f482600Sdownsj 
310efc3f44dSpjanzen     (void) mousemask(BUTTON1_CLICKED, (mmask_t *)NULL);
3117f482600Sdownsj }
3127f482600Sdownsj 
3137f482600Sdownsj /* print a message at the prompt line */
3143e07b96bSguenther static void
prompt(int n,const char * f,...)3153e07b96bSguenther prompt(int n, const char *f, ...)
3167f482600Sdownsj {
3173e07b96bSguenther     va_list va;
3183e07b96bSguenther 
3197f482600Sdownsj     (void) move(PROMPTLINE + n, 0);
3207f482600Sdownsj     (void) clrtoeol();
3213e07b96bSguenther     va_start(va, f);
3223e07b96bSguenther     (void) vw_printw(stdscr, f, va);
3233e07b96bSguenther     va_end(va);
3247f482600Sdownsj     (void) refresh();
3257f482600Sdownsj }
3267f482600Sdownsj 
327cd84dbdaSmestre static void
error(char * s)328cd84dbdaSmestre error(char *s)
3297f482600Sdownsj {
3307f482600Sdownsj     (void) move(PROMPTLINE + 2, 0);
3317f482600Sdownsj     (void) clrtoeol();
3327f482600Sdownsj     if (s)
3337f482600Sdownsj     {
3347f482600Sdownsj 	(void) addstr(s);
3357f482600Sdownsj 	(void) beep();
3367f482600Sdownsj     }
3377f482600Sdownsj }
3387f482600Sdownsj 
339cd84dbdaSmestre static void
placeship(int b,ship_t * ss,int vis)340cd84dbdaSmestre placeship(int b, ship_t *ss, int vis)
3417f482600Sdownsj {
3427f482600Sdownsj     int l;
3437f482600Sdownsj 
3447f482600Sdownsj     for(l = 0; l < ss->length; ++l)
3457f482600Sdownsj     {
3467f482600Sdownsj 	int newx = ss->x + l * xincr[ss->dir];
3477f482600Sdownsj 	int newy = ss->y + l * yincr[ss->dir];
3487f482600Sdownsj 
3497f482600Sdownsj 	board[b][newx][newy] = ss->symbol;
3507f482600Sdownsj 	if (vis)
3517f482600Sdownsj 	{
3527f482600Sdownsj 	    pgoto(newy, newx);
3537f482600Sdownsj 	    (void) addch((chtype)ss->symbol);
3547f482600Sdownsj 	}
3557f482600Sdownsj     }
3567f482600Sdownsj     ss->hits = 0;
3577f482600Sdownsj }
3587f482600Sdownsj 
359cd84dbdaSmestre static int
rnd(int n)360cd84dbdaSmestre rnd(int n)
3617f482600Sdownsj {
36266e49541Snaddy     return(arc4random_uniform(n));
3637f482600Sdownsj }
3647f482600Sdownsj 
3657f482600Sdownsj /* generate a valid random ship placement into px,py */
366cd84dbdaSmestre static void
randomplace(int b,ship_t * ss)367cd84dbdaSmestre randomplace(int b, ship_t *ss)
3687f482600Sdownsj {
3697f482600Sdownsj     do {
3707f482600Sdownsj 	ss->dir = rnd(2) ? E : S;
3715e29f442Spjanzen 	ss->x = rnd(BWIDTH - (ss->dir == E ? ss->length : 0));
3725e29f442Spjanzen 	ss->y = rnd(BDEPTH - (ss->dir == S ? ss->length : 0));
3737f482600Sdownsj     } while
3747f482600Sdownsj 	(!checkplace(b, ss, FALSE));
3757f482600Sdownsj }
3767f482600Sdownsj 
377cd84dbdaSmestre static void
initgame(void)378cd84dbdaSmestre initgame(void)
3797f482600Sdownsj {
3807f482600Sdownsj     int i, j, unplaced;
3817f482600Sdownsj     ship_t *ss;
3827f482600Sdownsj 
3837f482600Sdownsj     (void) clear();
3847f482600Sdownsj     (void) mvaddstr(0,35,"BATTLESHIPS");
3857f482600Sdownsj     (void) move(PROMPTLINE + 2, 0);
3867f482600Sdownsj     announceopts();
3877f482600Sdownsj 
388efc3f44dSpjanzen     /* Set up global CPU algorithm variables. */
389efc3f44dSpjanzen     next = RANDOM_FIRE;
390efc3f44dSpjanzen     turncount = 0;
391efc3f44dSpjanzen     srchstep = BEGINSTEP;
392efc3f44dSpjanzen     /* set up cpulongest and cpushortest (computer targetting variables) */
393efc3f44dSpjanzen     cpushortest = cpulongest = cpuship->length;
394efc3f44dSpjanzen 
395efc3f44dSpjanzen     memset(board, 0, sizeof(char) * BWIDTH * BDEPTH * 2);
396efc3f44dSpjanzen     memset(hits,  0, sizeof(char) * BWIDTH * BDEPTH * 2);
3977f482600Sdownsj     for (i = 0; i < SHIPTYPES; i++)
3987f482600Sdownsj     {
3997f482600Sdownsj 	ss = cpuship + i;
40001ee717fSpjanzen 	ss->x = ss->y = ss->dir = ss->hits = 0;
40101ee717fSpjanzen 	ss->placed = FALSE;
4027f482600Sdownsj 	ss = plyship + i;
40301ee717fSpjanzen 	ss->x = ss->y = ss->dir = ss->hits = 0;
40401ee717fSpjanzen 	ss->placed = FALSE;
405efc3f44dSpjanzen 
406efc3f44dSpjanzen      if (ss->length > cpulongest)
407efc3f44dSpjanzen 		cpulongest  = ss->length;
408efc3f44dSpjanzen      if (ss->length < cpushortest)
409efc3f44dSpjanzen 		cpushortest = ss->length;
4107f482600Sdownsj     }
4117f482600Sdownsj 
4127f482600Sdownsj     /* draw empty boards */
4137f482600Sdownsj     (void) mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board");
4147f482600Sdownsj     (void) mvaddstr(PYBASE - 1, PXBASE - 3,numbers);
4157f482600Sdownsj     for(i=0; i < BDEPTH; ++i)
4167f482600Sdownsj     {
417efc3f44dSpjanzen 	(void) mvaddch(PYBASE + i, PXBASE - 3, (chtype)(i + 'A'));
4187f482600Sdownsj 	if (has_colors())
4197f482600Sdownsj 	    attron(COLOR_PAIR(COLOR_BLUE));
4207f482600Sdownsj 	(void) addch(' ');
4217f482600Sdownsj 	for (j = 0; j < BWIDTH; j++)
4227f482600Sdownsj 	    (void) addstr(" . ");
4237f482600Sdownsj 	attrset(0);
4247f482600Sdownsj 	(void) addch(' ');
425efc3f44dSpjanzen 	(void) addch((chtype)(i + 'A'));
4267f482600Sdownsj     }
4277f482600Sdownsj     (void) mvaddstr(PYBASE + BDEPTH, PXBASE - 3,numbers);
4287f482600Sdownsj     (void) mvaddstr(CYBASE - 2, CXBASE + 7,"Hit/Miss Board");
4297f482600Sdownsj     (void) mvaddstr(CYBASE - 1, CXBASE - 3, numbers);
4307f482600Sdownsj     for(i=0; i < BDEPTH; ++i)
4317f482600Sdownsj     {
432efc3f44dSpjanzen 	(void) mvaddch(CYBASE + i, CXBASE - 3, (chtype)(i + 'A'));
4337f482600Sdownsj 	if (has_colors())
4347f482600Sdownsj 	    attron(COLOR_PAIR(COLOR_BLUE));
4357f482600Sdownsj 	(void) addch(' ');
4367f482600Sdownsj 	for (j = 0; j < BWIDTH; j++)
4377f482600Sdownsj 	    (void) addstr(" . ");
4387f482600Sdownsj 	attrset(0);
4397f482600Sdownsj 	(void) addch(' ');
440efc3f44dSpjanzen 	(void) addch((chtype)(i + 'A'));
4417f482600Sdownsj     }
4427f482600Sdownsj 
4437f482600Sdownsj     (void) mvaddstr(CYBASE + BDEPTH,CXBASE - 3,numbers);
4447f482600Sdownsj 
4457f482600Sdownsj     (void) mvprintw(HYBASE,  HXBASE,
4467f482600Sdownsj 		    "To position your ships: move the cursor to a spot, then");
4477f482600Sdownsj     (void) mvprintw(HYBASE+1,HXBASE,
4487f482600Sdownsj 		    "type the first letter of a ship type to select it, then");
4497f482600Sdownsj     (void) mvprintw(HYBASE+2,HXBASE,
4507f482600Sdownsj 		    "type a direction ([hjkl] or [4862]), indicating how the");
4517f482600Sdownsj     (void) mvprintw(HYBASE+3,HXBASE,
4527f482600Sdownsj 		    "ship should be pointed. You may also type a ship letter");
4537f482600Sdownsj     (void) mvprintw(HYBASE+4,HXBASE,
4547f482600Sdownsj 		    "followed by `r' to position it randomly, or type `R' to");
4557f482600Sdownsj     (void) mvprintw(HYBASE+5,HXBASE,
4567f482600Sdownsj 		    "place all remaining ships randomly.");
4577f482600Sdownsj 
4587f482600Sdownsj     (void) mvaddstr(MYBASE,   MXBASE, "Aiming keys:");
4597f482600Sdownsj     (void) mvaddstr(SYBASE,   SXBASE, "y k u    7 8 9");
4607f482600Sdownsj     (void) mvaddstr(SYBASE+1, SXBASE, " \\|/      \\|/ ");
4617f482600Sdownsj     (void) mvaddstr(SYBASE+2, SXBASE, "h-+-l    4-+-6");
4627f482600Sdownsj     (void) mvaddstr(SYBASE+3, SXBASE, " /|\\      /|\\ ");
4637f482600Sdownsj     (void) mvaddstr(SYBASE+4, SXBASE, "b j n    1 2 3");
4647f482600Sdownsj 
4657f482600Sdownsj     /* have the computer place ships */
4667f482600Sdownsj     for(ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
4677f482600Sdownsj     {
4687f482600Sdownsj 	randomplace(COMPUTER, ss);
4697f482600Sdownsj 	placeship(COMPUTER, ss, FALSE);
4707f482600Sdownsj     }
4717f482600Sdownsj 
4727f482600Sdownsj     ss = (ship_t *)NULL;
4737f482600Sdownsj     do {
4746e5a188aStedu 	char docked[SHIPTYPES + 2], *cp = docked;
4756e5a188aStedu 	int c;
4767f482600Sdownsj 
4777f482600Sdownsj 	/* figure which ships still wait to be placed */
4787f482600Sdownsj 	*cp++ = 'R';
4797f482600Sdownsj 	for (i = 0; i < SHIPTYPES; i++)
4807f482600Sdownsj 	    if (!plyship[i].placed)
4817f482600Sdownsj 		*cp++ = plyship[i].symbol;
4827f482600Sdownsj 	*cp = '\0';
4837f482600Sdownsj 
4847f482600Sdownsj 	/* get a command letter */
4857f482600Sdownsj 	prompt(1, "Type one of [%s] to pick a ship.", docked+1);
4867f482600Sdownsj 	do {
4877f482600Sdownsj 	    c = getcoord(PLAYER);
4887f482600Sdownsj 	} while
4897f482600Sdownsj 	    (!strchr(docked, c));
4907f482600Sdownsj 
4917f482600Sdownsj 	if (c == 'R')
4927f482600Sdownsj 	    (void) ungetch('R');
4937f482600Sdownsj 	else
4947f482600Sdownsj 	{
4957f482600Sdownsj 	    /* map that into the corresponding symbol */
4967f482600Sdownsj 	    for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
4977f482600Sdownsj 		if (ss->symbol == c)
4987f482600Sdownsj 		    break;
4997f482600Sdownsj 
5007f482600Sdownsj 	    prompt(1, "Type one of [hjklrR] to place your %s.", ss->name);
5017f482600Sdownsj 	    pgoto(cury, curx);
5027f482600Sdownsj 	}
5036e5a188aStedu regetchar:
5047f482600Sdownsj 	c = getch();
5056e5a188aStedu 	switch (c) {
5066e5a188aStedu 	case FF:
5077f482600Sdownsj 	    (void)clearok(stdscr, TRUE);
5087f482600Sdownsj 	    (void)refresh();
5096e5a188aStedu 	    break;
5106e5a188aStedu 	case 'r':
5117f482600Sdownsj 	    prompt(1, "Random-placing your %s", ss->name);
5127f482600Sdownsj 	    randomplace(PLAYER, ss);
5137f482600Sdownsj 	    placeship(PLAYER, ss, TRUE);
5147dd160a5Smestre 		error(NULL);
5157f482600Sdownsj 	    ss->placed = TRUE;
5166e5a188aStedu 	    break;
5176e5a188aStedu 	case 'R':
5183e07b96bSguenther 	    prompt(1, "Placing the rest of your fleet at random...");
5197f482600Sdownsj 	    for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
5207f482600Sdownsj 		if (!ss->placed)
5217f482600Sdownsj 		{
5227f482600Sdownsj 		    randomplace(PLAYER, ss);
5237f482600Sdownsj 		    placeship(PLAYER, ss, TRUE);
5247f482600Sdownsj 		    ss->placed = TRUE;
5257f482600Sdownsj 		}
5267dd160a5Smestre 	    error(NULL);
5276e5a188aStedu 	    break;
5286e5a188aStedu 
5296e5a188aStedu 	case 'k': case 'j': case 'h': case 'l':
5306e5a188aStedu 	case '8': case '2': case '4': case '6':
5316e5a188aStedu 	case KEY_LEFT: case KEY_RIGHT: case KEY_UP: case KEY_DOWN:
5327f482600Sdownsj 	    ss->x = curx;
5337f482600Sdownsj 	    ss->y = cury;
5347f482600Sdownsj 
5357f482600Sdownsj 	    switch(c)
5367f482600Sdownsj 	    {
5376e5a188aStedu 	    case 'k': case '8': case KEY_UP: ss->dir = N; break;
5386e5a188aStedu 	    case 'j': case '2': case KEY_DOWN: ss->dir = S; break;
5396e5a188aStedu 	    case 'h': case '4': case KEY_LEFT: ss->dir = W; break;
5406e5a188aStedu 	    case 'l': case '6': case KEY_RIGHT: ss->dir = E; break;
5417f482600Sdownsj 	    }
5427f482600Sdownsj 
5437f482600Sdownsj 	    if (checkplace(PLAYER, ss, TRUE))
5447f482600Sdownsj 	    {
5457f482600Sdownsj 		placeship(PLAYER, ss, TRUE);
5467dd160a5Smestre 		error(NULL);
5477f482600Sdownsj 		ss->placed = TRUE;
5487f482600Sdownsj 	    }
5496e5a188aStedu 	    break;
5506e5a188aStedu 	default:
5516e5a188aStedu 	    goto regetchar;
5527f482600Sdownsj 	}
5537f482600Sdownsj 
5547f482600Sdownsj 	for (unplaced = i = 0; i < SHIPTYPES; i++)
5557f482600Sdownsj 	    unplaced += !plyship[i].placed;
5567f482600Sdownsj     } while
5577f482600Sdownsj 	(unplaced);
5587f482600Sdownsj 
5597f482600Sdownsj     turn = rnd(2);
5607f482600Sdownsj 
5617f482600Sdownsj     (void) mvprintw(HYBASE,  HXBASE,
5627f482600Sdownsj 		    "To fire, move the cursor to your chosen aiming point   ");
5637f482600Sdownsj     (void) mvprintw(HYBASE+1,  HXBASE,
5647f482600Sdownsj 		    "and strike any key other than a motion key.            ");
5657f482600Sdownsj     (void) mvprintw(HYBASE+2,  HXBASE,
5667f482600Sdownsj 		    "                                                       ");
5677f482600Sdownsj     (void) mvprintw(HYBASE+3,  HXBASE,
5687f482600Sdownsj 		    "                                                       ");
5697f482600Sdownsj     (void) mvprintw(HYBASE+4,  HXBASE,
5707f482600Sdownsj 		    "                                                       ");
5717f482600Sdownsj     (void) mvprintw(HYBASE+5,  HXBASE,
5727f482600Sdownsj 		    "                                                       ");
5737f482600Sdownsj 
5743e07b96bSguenther     (void) prompt(0, "Press any key to start...");
5757f482600Sdownsj     (void) getch();
5767f482600Sdownsj }
5777f482600Sdownsj 
578cd84dbdaSmestre static int
getcoord(int atcpu)579cd84dbdaSmestre getcoord(int atcpu)
5807f482600Sdownsj {
5817f482600Sdownsj     int ny, nx, c;
5827f482600Sdownsj 
5837f482600Sdownsj     if (atcpu)
5847f482600Sdownsj 	cgoto(cury,curx);
5857f482600Sdownsj     else
5867f482600Sdownsj 	pgoto(cury, curx);
5877f482600Sdownsj     (void)refresh();
5887f482600Sdownsj     for (;;)
5897f482600Sdownsj     {
5907f482600Sdownsj 	if (atcpu)
5917f482600Sdownsj 	{
5927f482600Sdownsj 	    (void) mvprintw(CYBASE + BDEPTH+1, CXBASE+11, "(%d, %c)", curx, 'A'+cury);
5937f482600Sdownsj 	    cgoto(cury, curx);
5947f482600Sdownsj 	}
5957f482600Sdownsj 	else
5967f482600Sdownsj 	{
5977f482600Sdownsj 	    (void) mvprintw(PYBASE + BDEPTH+1, PXBASE+11, "(%d, %c)", curx, 'A'+cury);
5987f482600Sdownsj 	    pgoto(cury, curx);
5997f482600Sdownsj 	}
6007f482600Sdownsj 
6017f482600Sdownsj 	switch(c = getch())
6027f482600Sdownsj 	{
6037f482600Sdownsj 	case 'k': case '8':
6047f482600Sdownsj 	case KEY_UP:
6057f482600Sdownsj 	    ny = cury+BDEPTH-1; nx = curx;
6067f482600Sdownsj 	    break;
6077f482600Sdownsj 	case 'j': case '2':
6087f482600Sdownsj 	case KEY_DOWN:
6097f482600Sdownsj 	    ny = cury+1;        nx = curx;
6107f482600Sdownsj 	    break;
6117f482600Sdownsj 	case 'h': case '4':
6127f482600Sdownsj 	case KEY_LEFT:
6137f482600Sdownsj 	    ny = cury;          nx = curx+BWIDTH-1;
6147f482600Sdownsj 	    break;
6157f482600Sdownsj 	case 'l': case '6':
6167f482600Sdownsj 	case KEY_RIGHT:
6177f482600Sdownsj 	    ny = cury;          nx = curx+1;
6187f482600Sdownsj 	    break;
6197f482600Sdownsj 	case 'y': case '7':
6207f482600Sdownsj 	case KEY_A1:
6217f482600Sdownsj 	    ny = cury+BDEPTH-1; nx = curx+BWIDTH-1;
6227f482600Sdownsj 	    break;
6237f482600Sdownsj 	case 'b': case '1':
6247f482600Sdownsj 	case KEY_C1:
6257f482600Sdownsj 	    ny = cury+1;        nx = curx+BWIDTH-1;
6267f482600Sdownsj 	    break;
6277f482600Sdownsj 	case 'u': case '9':
6287f482600Sdownsj 	case KEY_A3:
6297f482600Sdownsj 	    ny = cury+BDEPTH-1; nx = curx+1;
6307f482600Sdownsj 	    break;
6317f482600Sdownsj 	case 'n': case '3':
6327f482600Sdownsj 	case KEY_C3:
6337f482600Sdownsj 	    ny = cury+1;        nx = curx+1;
6347f482600Sdownsj 	    break;
6357f482600Sdownsj 	case FF:
6367f482600Sdownsj 	    nx = curx; ny = cury;
6377f482600Sdownsj 	    (void)clearok(stdscr, TRUE);
6387f482600Sdownsj 	    (void)refresh();
6397f482600Sdownsj 	    break;
640efc3f44dSpjanzen 	case KEY_MOUSE:
641efc3f44dSpjanzen 	    {
642efc3f44dSpjanzen 		MEVENT	myevent;
643efc3f44dSpjanzen 
644efc3f44dSpjanzen 		getmouse(&myevent);
645efc3f44dSpjanzen 		if (atcpu
6467a13a1fbSpjanzen 			&& myevent.y >= CY(0) && myevent.y < CY(BDEPTH)
6477a13a1fbSpjanzen 			&& myevent.x >= CX(0) && myevent.x < CX(BWIDTH))
648efc3f44dSpjanzen 		{
649efc3f44dSpjanzen 		    curx = CXINV(myevent.x);
650efc3f44dSpjanzen 		    cury = CYINV(myevent.y);
651efc3f44dSpjanzen 		    return(' ');
652efc3f44dSpjanzen 		}
653efc3f44dSpjanzen 		else
654efc3f44dSpjanzen 		    beep();
655efc3f44dSpjanzen 	    }
656efc3f44dSpjanzen 	    break;
657859369c2Spjanzen 	case ERR:
658859369c2Spjanzen 	    uninitgame(1);
659859369c2Spjanzen 	    break;
6607f482600Sdownsj 	default:
6617f482600Sdownsj 	    if (atcpu)
6627f482600Sdownsj 		(void) mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, "      ");
6637f482600Sdownsj 	    else
6647f482600Sdownsj 		(void) mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, "      ");
6657f482600Sdownsj 	    return(c);
6667f482600Sdownsj 	}
6677f482600Sdownsj 
6687f482600Sdownsj 	curx = nx % BWIDTH;
6697f482600Sdownsj 	cury = ny % BDEPTH;
6707f482600Sdownsj     }
6717f482600Sdownsj }
6727f482600Sdownsj 
6737f482600Sdownsj /* is this location on the selected zboard adjacent to a ship? */
674cd84dbdaSmestre static int
collidecheck(int b,int y,int x)675cd84dbdaSmestre collidecheck(int b, int y, int x)
6767f482600Sdownsj {
6777f482600Sdownsj     int	collide;
6787f482600Sdownsj 
6797f482600Sdownsj     /* anything on the square */
680efc3f44dSpjanzen     if ((collide = IS_SHIP(board[b][x][y])) != 0)
6817f482600Sdownsj 	return(collide);
6827f482600Sdownsj 
6837f482600Sdownsj     /* anything on the neighbors */
6847f482600Sdownsj     if (!closepack)
6857f482600Sdownsj     {
6867f482600Sdownsj 	int i;
6877f482600Sdownsj 
6887f482600Sdownsj 	for (i = 0; i < 8; i++)
6897f482600Sdownsj 	{
6907f482600Sdownsj 	    int xend, yend;
6917f482600Sdownsj 
6927f482600Sdownsj 	    yend = y + yincr[i];
6937f482600Sdownsj 	    xend = x + xincr[i];
6947f482600Sdownsj 	    if (ONBOARD(xend, yend))
6957f482600Sdownsj 		collide += IS_SHIP(board[b][xend][yend]);
6967f482600Sdownsj 	}
6977f482600Sdownsj     }
6987f482600Sdownsj     return(collide);
6997f482600Sdownsj }
7007f482600Sdownsj 
701cd84dbdaSmestre static bool
checkplace(int b,ship_t * ss,int vis)702cd84dbdaSmestre checkplace(int b, ship_t *ss, int vis)
7037f482600Sdownsj {
7047f482600Sdownsj     int l, xend, yend;
7057f482600Sdownsj 
7067f482600Sdownsj     /* first, check for board edges */
707efc3f44dSpjanzen     xend = ss->x + (ss->length - 1) * xincr[ss->dir];
708efc3f44dSpjanzen     yend = ss->y + (ss->length - 1) * yincr[ss->dir];
7097f482600Sdownsj     if (!ONBOARD(xend, yend))
7107f482600Sdownsj     {
7117f482600Sdownsj 	if (vis)
7127f482600Sdownsj 	    switch(rnd(3))
7137f482600Sdownsj 	    {
7147f482600Sdownsj 	    case 0:
7157f482600Sdownsj 		error("Ship is hanging from the edge of the world");
7167f482600Sdownsj 		break;
7177f482600Sdownsj 	    case 1:
7187f482600Sdownsj 		error("Try fitting it on the board");
7197f482600Sdownsj 		break;
7207f482600Sdownsj 	    case 2:
7217f482600Sdownsj 		error("Figure I won't find it if you put it there?");
7227f482600Sdownsj 		break;
7237f482600Sdownsj 	    }
72401ee717fSpjanzen 	return(FALSE);
7257f482600Sdownsj     }
7267f482600Sdownsj 
7277f482600Sdownsj     for(l = 0; l < ss->length; ++l)
7287f482600Sdownsj     {
7297f482600Sdownsj 	if(collidecheck(b, ss->y+l*yincr[ss->dir], ss->x+l*xincr[ss->dir]))
7307f482600Sdownsj 	{
7317f482600Sdownsj 	    if (vis)
7327f482600Sdownsj 		switch(rnd(3))
7337f482600Sdownsj 		{
7347f482600Sdownsj 		    case 0:
7357f482600Sdownsj 			error("There's already a ship there");
7367f482600Sdownsj 			break;
7377f482600Sdownsj 		    case 1:
7387f482600Sdownsj 			error("Collision alert!  Aaaaaagh!");
7397f482600Sdownsj 			break;
7407f482600Sdownsj 		    case 2:
7417f482600Sdownsj 			error("Er, Admiral, what about the other ship?");
7427f482600Sdownsj 			break;
7437f482600Sdownsj 		    }
7447f482600Sdownsj 	    return(FALSE);
7457f482600Sdownsj 	    }
7467f482600Sdownsj 	}
7477f482600Sdownsj     return(TRUE);
7487f482600Sdownsj }
7497f482600Sdownsj 
750cd84dbdaSmestre static int
awinna(void)751cd84dbdaSmestre awinna(void)
7527f482600Sdownsj {
7537f482600Sdownsj     int i, j;
7547f482600Sdownsj     ship_t *ss;
7557f482600Sdownsj 
7567f482600Sdownsj     for(i=0; i<2; ++i)
7577f482600Sdownsj     {
7587f482600Sdownsj 	ss = (i) ? cpuship : plyship;
7597f482600Sdownsj 	for(j=0; j < SHIPTYPES; ++j, ++ss)
7607f482600Sdownsj 	    if(ss->length > ss->hits)
7617f482600Sdownsj 		break;
7627f482600Sdownsj 	if (j == SHIPTYPES)
7637f482600Sdownsj 	    return(OTHER);
7647f482600Sdownsj     }
7657f482600Sdownsj     return(-1);
7667f482600Sdownsj }
7677f482600Sdownsj 
7687f482600Sdownsj /* register a hit on the targeted ship */
769cd84dbdaSmestre static ship_t *
hitship(int x,int y)770cd84dbdaSmestre hitship(int x, int y)
7717f482600Sdownsj {
7727f482600Sdownsj     ship_t *sb, *ss;
7737f482600Sdownsj     char sym;
7747f482600Sdownsj     int oldx, oldy;
7757f482600Sdownsj 
7767f482600Sdownsj     getyx(stdscr, oldy, oldx);
7777f482600Sdownsj     sb = (turn) ? plyship : cpuship;
7787f482600Sdownsj     if(!(sym = board[OTHER][x][y]))
7797f482600Sdownsj 	return((ship_t *)NULL);
7807f482600Sdownsj     for(ss = sb; ss < sb + SHIPTYPES; ++ss)
7817f482600Sdownsj 	if(ss->symbol == sym)
7827f482600Sdownsj 	{
7837f482600Sdownsj 	    if (++ss->hits < ss->length)	/* still afloat? */
7847f482600Sdownsj 		return((ship_t *)NULL);
7857f482600Sdownsj 	    else				/* sunk! */
7867f482600Sdownsj 	    {
7877f482600Sdownsj 		int i, j;
7887f482600Sdownsj 
7897f482600Sdownsj 		if (!closepack)
7907f482600Sdownsj 		    for (j = -1; j <= 1; j++)
7917f482600Sdownsj 		    {
7927f482600Sdownsj 			int bx = ss->x + j * xincr[(ss->dir + 2) % 8];
7937f482600Sdownsj 			int by = ss->y + j * yincr[(ss->dir + 2) % 8];
7947f482600Sdownsj 
7957f482600Sdownsj 			for (i = -1; i <= ss->length; ++i)
7967f482600Sdownsj 			{
797efc3f44dSpjanzen 			    int x1, y1;
7987f482600Sdownsj 
799efc3f44dSpjanzen 			    x1 = bx + i * xincr[ss->dir];
800efc3f44dSpjanzen 			    y1 = by + i * yincr[ss->dir];
801efc3f44dSpjanzen 			    if (ONBOARD(x1, y1))
8027f482600Sdownsj 			    {
803efc3f44dSpjanzen 				hits[turn][x1][y1] = MARK_MISS;
804efc3f44dSpjanzen 				if (turn == PLAYER)
8057f482600Sdownsj 				{
806efc3f44dSpjanzen 				    cgoto(y1, x1);
8077f482600Sdownsj 				    if (has_colors())
8087f482600Sdownsj 					attron(COLOR_PAIR(COLOR_GREEN));
8097f482600Sdownsj 				    (void)addch(MARK_MISS);
8107f482600Sdownsj 				    attrset(0);
8117f482600Sdownsj 				}
8127f482600Sdownsj 			    }
8137f482600Sdownsj 			}
8147f482600Sdownsj 		    }
8157f482600Sdownsj 
8167f482600Sdownsj 		for (i = 0; i < ss->length; ++i)
8177f482600Sdownsj 		{
818efc3f44dSpjanzen 		    int x1 = ss->x + i * xincr[ss->dir];
819efc3f44dSpjanzen 		    int y1 = ss->y + i * yincr[ss->dir];
8207f482600Sdownsj 
821efc3f44dSpjanzen 		    hits[turn][x1][y1] = ss->symbol;
822efc3f44dSpjanzen 		    if (turn  == PLAYER)
8237f482600Sdownsj 		    {
824efc3f44dSpjanzen 			cgoto(y1, x1);
825efc3f44dSpjanzen 			(void) addch((chtype)(ss->symbol));
8267f482600Sdownsj 		    }
8277f482600Sdownsj 		}
8287f482600Sdownsj 
8297f482600Sdownsj 		(void) move(oldy, oldx);
8307f482600Sdownsj 		return(ss);
8317f482600Sdownsj 	    }
8327f482600Sdownsj 	}
8337f482600Sdownsj     (void) move(oldy, oldx);
8347f482600Sdownsj     return((ship_t *)NULL);
8357f482600Sdownsj }
8367f482600Sdownsj 
837cd84dbdaSmestre static int
plyturn(void)838cd84dbdaSmestre plyturn(void)
8397f482600Sdownsj {
8407f482600Sdownsj     ship_t *ss;
84101ee717fSpjanzen     int hit;
842efc3f44dSpjanzen     char *m = NULL;
8437f482600Sdownsj 
8443e07b96bSguenther     prompt(1, "Where do you want to shoot? ");
8457f482600Sdownsj     for (;;)
8467f482600Sdownsj     {
8477f482600Sdownsj 	(void) getcoord(COMPUTER);
8487f482600Sdownsj 	if (hits[PLAYER][curx][cury])
8497f482600Sdownsj 	{
8503e07b96bSguenther 	    prompt(1, "You shelled this spot already! Try again.");
8517f482600Sdownsj 	    beep();
8527f482600Sdownsj 	}
8537f482600Sdownsj 	else
8547f482600Sdownsj 	    break;
8557f482600Sdownsj     }
8567f482600Sdownsj     hit = IS_SHIP(board[COMPUTER][curx][cury]);
8577f482600Sdownsj     hits[PLAYER][curx][cury] = hit ? MARK_HIT : MARK_MISS;
8587f482600Sdownsj     cgoto(cury, curx);
85901ee717fSpjanzen     if (has_colors()) {
8607f482600Sdownsj 	if (hit)
8617f482600Sdownsj 	    attron(COLOR_PAIR(COLOR_RED));
8627f482600Sdownsj 	else
8637f482600Sdownsj 	    attron(COLOR_PAIR(COLOR_GREEN));
86401ee717fSpjanzen     }
8657f482600Sdownsj     (void) addch((chtype)hits[PLAYER][curx][cury]);
8667f482600Sdownsj     attrset(0);
8677f482600Sdownsj 
8687f482600Sdownsj     prompt(1, "You %s.", hit ? "scored a hit" : "missed");
8697f482600Sdownsj     if(hit && (ss = hitship(curx, cury)))
8707f482600Sdownsj     {
8717f482600Sdownsj 	switch(rnd(5))
8727f482600Sdownsj 	{
8737f482600Sdownsj 	case 0:
8747f482600Sdownsj 	    m = " You sank my %s!";
8757f482600Sdownsj 	    break;
8767f482600Sdownsj 	case 1:
8777f482600Sdownsj 	    m = " I have this sinking feeling about my %s....";
8787f482600Sdownsj 	    break;
8797f482600Sdownsj 	case 2:
8807f482600Sdownsj 	    m = " My %s has gone to Davy Jones's locker!";
8817f482600Sdownsj 	    break;
8827f482600Sdownsj 	case 3:
8837f482600Sdownsj 	    m = " Glub, glub -- my %s is headed for the bottom!";
8847f482600Sdownsj 	    break;
8857f482600Sdownsj 	case 4:
886efc3f44dSpjanzen 	    m = " You'll pick up survivors from my %s, I hope...!";
8877f482600Sdownsj 	    break;
8887f482600Sdownsj 	}
8897f482600Sdownsj 	(void)printw(m, ss->name);
8907f482600Sdownsj 	(void)beep();
8917f482600Sdownsj     }
8927f482600Sdownsj     return(hit);
8937f482600Sdownsj }
8947f482600Sdownsj 
895cd84dbdaSmestre static int
sgetc(char * s)896cd84dbdaSmestre sgetc(char *s)
8977f482600Sdownsj {
8987f482600Sdownsj     char *s1;
8997f482600Sdownsj     int ch;
9007f482600Sdownsj 
9017f482600Sdownsj     (void)refresh();
9027f482600Sdownsj     for(;;)
9037f482600Sdownsj     {
9047f482600Sdownsj 	ch = getch();
9057f482600Sdownsj 	if (islower(ch))
9067f482600Sdownsj 	    ch = toupper(ch);
9077f482600Sdownsj 	if (ch == CTRLC)
908efc3f44dSpjanzen 	    uninitgame(0);
9097f482600Sdownsj 	for (s1=s; *s1 && ch != *s1; ++s1)
9107f482600Sdownsj 	    continue;
9117f482600Sdownsj 	if (*s1)
9127f482600Sdownsj 	{
9137f482600Sdownsj 	    (void) addch((chtype)ch);
9147f482600Sdownsj 	    (void) refresh();
9157f482600Sdownsj 	    return(ch);
9167f482600Sdownsj 	    }
9177f482600Sdownsj 	}
9187f482600Sdownsj }
9197f482600Sdownsj 
920efc3f44dSpjanzen /* Checks to see if there's room for a ship of a given length in a given
921efc3f44dSpjanzen  * direction.  If direction is negative, check in all directions.  Note
922efc3f44dSpjanzen  * that North and South are equivalent, as are East and West.
923efc3f44dSpjanzen  */
924cd84dbdaSmestre static bool
cpushipcanfit(int x,int y,int length,int direction)925cd84dbdaSmestre cpushipcanfit(int x, int y, int length, int direction)
9267f482600Sdownsj {
927efc3f44dSpjanzen 	int len = 1;
928efc3f44dSpjanzen 	int x1, y1;
929efc3f44dSpjanzen 
930efc3f44dSpjanzen 	if (direction >= 0)
931efc3f44dSpjanzen 	{
932efc3f44dSpjanzen 		direction %= 4;
933efc3f44dSpjanzen 		while (direction < 8)
934efc3f44dSpjanzen 		{
935efc3f44dSpjanzen 			x1 = x + xincr[direction];
936efc3f44dSpjanzen 			y1 = y + yincr[direction];
937efc3f44dSpjanzen 			while (POSSIBLE(x1,y1))
938efc3f44dSpjanzen 			{
939efc3f44dSpjanzen 			    len++;
940efc3f44dSpjanzen 			    x1 += xincr[direction];
941efc3f44dSpjanzen 			    y1 += yincr[direction];
942efc3f44dSpjanzen 			}
943efc3f44dSpjanzen 			direction += 4;
944efc3f44dSpjanzen 		}
945efc3f44dSpjanzen 		return (len >= length);
946efc3f44dSpjanzen 	}
947efc3f44dSpjanzen 	else
948efc3f44dSpjanzen 	{
949efc3f44dSpjanzen 		return ((cpushipcanfit(x,y,length,E)) ||
950efc3f44dSpjanzen 			    (cpushipcanfit(x,y,length,S)));
951efc3f44dSpjanzen 	}
952efc3f44dSpjanzen }
953efc3f44dSpjanzen 
954efc3f44dSpjanzen /* random-fire routine -- implements simple diagonal-striping strategy */
955cd84dbdaSmestre static void
randomfire(int * px,int * py)956cd84dbdaSmestre randomfire(int *px, int *py)
957efc3f44dSpjanzen {
9587f482600Sdownsj     static int huntoffs;		/* Offset on search strategy */
9597f482600Sdownsj     int ypossible[BWIDTH * BDEPTH], xpossible[BWIDTH * BDEPTH], nposs;
9607f482600Sdownsj     int x, y, i;
9617f482600Sdownsj 
9627f482600Sdownsj     if (turncount++ == 0)
9637f482600Sdownsj 	huntoffs = rnd(srchstep);
9647f482600Sdownsj 
965efc3f44dSpjanzen     /* first, list all possible moves on the diagonal stripe */
966efc3f44dSpjanzen     nposs = 0;
9677f482600Sdownsj     for (x = 0; x < BWIDTH; x++)
9687f482600Sdownsj 	for (y = 0; y < BDEPTH; y++)
969efc3f44dSpjanzen 	    if ((!hits[COMPUTER][x][y]) &&
970efc3f44dSpjanzen 		 	(((x+huntoffs) % srchstep) == (y % srchstep)) &&
971efc3f44dSpjanzen 			(cpushipcanfit(x,y,cpulongest,-1)))
9727f482600Sdownsj 	    {
9737f482600Sdownsj 		    xpossible[nposs] = x;
9747f482600Sdownsj 		    ypossible[nposs] = y;
9757f482600Sdownsj 		    nposs++;
9767f482600Sdownsj 		}
977efc3f44dSpjanzen     if (nposs)
9787f482600Sdownsj     {
9797f482600Sdownsj 	i = rnd(nposs);
9807f482600Sdownsj 
9817f482600Sdownsj 	*px = xpossible[i];
9827f482600Sdownsj 	*py = ypossible[i];
983efc3f44dSpjanzen     }
984efc3f44dSpjanzen 	else if (srchstep > cpulongest)
985efc3f44dSpjanzen     {
9867f482600Sdownsj 	     --srchstep;
987cbddbf59Spjanzen 	     randomfire(px, py);
9887f482600Sdownsj     }
9897f482600Sdownsj 	else
9907f482600Sdownsj     {
9917f482600Sdownsj 		error("No moves possible?? Help!");
9927f482600Sdownsj 		exit(1);
9937f482600Sdownsj     }
9947f482600Sdownsj }
9957f482600Sdownsj 
9967f482600Sdownsj #define S_MISS	0
9977f482600Sdownsj #define S_HIT	1
9987f482600Sdownsj #define S_SUNK	-1
9997f482600Sdownsj 
10007f482600Sdownsj /* fire away at given location */
1001cd84dbdaSmestre static int
cpufire(int x,int y)1002cd84dbdaSmestre cpufire(int x, int y)
10037f482600Sdownsj {
100401ee717fSpjanzen     int hit;
100501ee717fSpjanzen     bool sunk;
1006efc3f44dSpjanzen     ship_t *ss = NULL;
10077f482600Sdownsj 
10087f482600Sdownsj     hits[COMPUTER][x][y] = (hit = (board[PLAYER][x][y])) ? MARK_HIT : MARK_MISS;
10097f482600Sdownsj     (void) mvprintw(PROMPTLINE, 0,
10107f482600Sdownsj 	"I shoot at %c%d. I %s!", y + 'A', x, hit ? "hit" : "miss");
1011efc3f44dSpjanzen     if ((sunk = (hit && (ss = hitship(x, y)))))
10127f482600Sdownsj 	(void) printw(" I've sunk your %s", ss->name);
10137f482600Sdownsj     (void)clrtoeol();
10147f482600Sdownsj 
10157f482600Sdownsj     pgoto(y, x);
101601ee717fSpjanzen     if (has_colors()) {
10177f482600Sdownsj 	if (hit)
10187f482600Sdownsj 	    attron(COLOR_PAIR(COLOR_RED));
10197f482600Sdownsj 	else
10207f482600Sdownsj 	    attron(COLOR_PAIR(COLOR_GREEN));
102101ee717fSpjanzen     }
10227f482600Sdownsj     (void) addch((chtype)(hit ? SHOWHIT : SHOWSPLASH));
10237f482600Sdownsj     attrset(0);
10247f482600Sdownsj 
10257f482600Sdownsj     return(hit ? (sunk ? S_SUNK : S_HIT) : S_MISS);
10267f482600Sdownsj }
10277f482600Sdownsj 
10287f482600Sdownsj /*
10297f482600Sdownsj  * This code implements a fairly irregular FSM, so please forgive the rampant
10307f482600Sdownsj  * unstructuredness below. The five labels are states which need to be held
10317f482600Sdownsj  * between computer turns.
10327f482600Sdownsj  */
1033cd84dbdaSmestre static int
cputurn(void)1034cd84dbdaSmestre cputurn(void)
10357f482600Sdownsj {
10367f482600Sdownsj     static bool used[4];
10377f482600Sdownsj     static ship_t ts;
10387f482600Sdownsj     int navail, x, y, d, n, hit = S_MISS;
1039efc3f44dSpjanzen     bool closenoshot = FALSE;
10407f482600Sdownsj 
10417f482600Sdownsj     switch(next)
10427f482600Sdownsj     {
10437f482600Sdownsj     case RANDOM_FIRE:	/* last shot was random and missed */
10447f482600Sdownsj     refire:
10457f482600Sdownsj 	randomfire(&x, &y);
10467f482600Sdownsj 	if (!(hit = cpufire(x, y)))
10477f482600Sdownsj 	    next = RANDOM_FIRE;
10487f482600Sdownsj 	else
10497f482600Sdownsj 	{
10507f482600Sdownsj 	    ts.x = x; ts.y = y;
10517f482600Sdownsj 	    ts.hits = 1;
10527f482600Sdownsj 	    next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT;
10537f482600Sdownsj 	}
10547f482600Sdownsj 	break;
10557f482600Sdownsj 
10567f482600Sdownsj     case RANDOM_HIT:	/* last shot was random and hit */
1057efc3f44dSpjanzen 	used[E/2] = used[W/2] = (!(cpushipcanfit(ts.x,ts.y,cpushortest,E)));
1058efc3f44dSpjanzen 	used[S/2] = used[N/2] = (!(cpushipcanfit(ts.x,ts.y,cpushortest,S)));
10597f482600Sdownsj 	/* FALLTHROUGH */
10607f482600Sdownsj 
10617f482600Sdownsj     case HUNT_DIRECT:	/* last shot hit, we're looking for ship's long axis */
10627f482600Sdownsj 	for (d = navail = 0; d < 4; d++)
10637f482600Sdownsj 	{
10647f482600Sdownsj 	    x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2];
10657f482600Sdownsj 	    if (!used[d] && POSSIBLE(x, y))
10667f482600Sdownsj 		navail++;
10677f482600Sdownsj 	    else
10687f482600Sdownsj 		used[d] = TRUE;
10697f482600Sdownsj 	}
10707f482600Sdownsj 	if (navail == 0)	/* no valid places for shots adjacent... */
10717f482600Sdownsj 	    goto refire;	/* ...so we must random-fire */
10727f482600Sdownsj 	else
10737f482600Sdownsj 	{
1074efc3f44dSpjanzen 	    for (d = 0, n = rnd(navail) + 1; n; n--,d++)
10757f482600Sdownsj 		while (used[d])
10767f482600Sdownsj 		    d++;
1077efc3f44dSpjanzen 	    d--;
10787f482600Sdownsj 
10797f482600Sdownsj 	    x = ts.x + xincr[d*2];
10807f482600Sdownsj 	    y = ts.y + yincr[d*2];
10817f482600Sdownsj 
10827f482600Sdownsj 	    if (!(hit = cpufire(x, y)))
10837f482600Sdownsj 		next = HUNT_DIRECT;
10847f482600Sdownsj 	    else
10857f482600Sdownsj 	    {
10867f482600Sdownsj 		ts.x = x; ts.y = y; ts.dir = d*2; ts.hits++;
10877f482600Sdownsj 		next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
10887f482600Sdownsj 	    }
10897f482600Sdownsj 	}
10907f482600Sdownsj 	break;
10917f482600Sdownsj 
10927f482600Sdownsj     case FIRST_PASS:	/* we have a start and a direction now */
10937f482600Sdownsj 	x = ts.x + xincr[ts.dir];
10947f482600Sdownsj 	y = ts.y + yincr[ts.dir];
1095efc3f44dSpjanzen 	if (POSSIBLE(x, y))
1096efc3f44dSpjanzen 	{
1097efc3f44dSpjanzen 	    if ((hit = cpufire(x, y)))
10987f482600Sdownsj 	    {
10997f482600Sdownsj 		    ts.x = x; ts.y = y; ts.hits++;
11007f482600Sdownsj 		    next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
11017f482600Sdownsj 	    }
11027f482600Sdownsj 	    else
11037f482600Sdownsj 	         next = REVERSE_JUMP;
11047f482600Sdownsj 	    break;
11057f482600Sdownsj 	}
11067f482600Sdownsj 	else
1107efc3f44dSpjanzen 	    next = REVERSE_JUMP;
1108efc3f44dSpjanzen 	/* FALL THROUGH */
1109efc3f44dSpjanzen 
1110efc3f44dSpjanzen     case REVERSE_JUMP:	/* nail down the ship's other end */
1111efc3f44dSpjanzen 	ts.dir = (ts.dir + 4) % 8;
1112efc3f44dSpjanzen 	ts.x += (ts.hits-1) * xincr[ts.dir];
1113efc3f44dSpjanzen 	ts.y += (ts.hits-1) * yincr[ts.dir];
1114efc3f44dSpjanzen 	/* FALL THROUGH */
11157f482600Sdownsj 
11167f482600Sdownsj     case SECOND_PASS:	/* kill squares not caught on first pass */
11177f482600Sdownsj 	x = ts.x + xincr[ts.dir];
11187f482600Sdownsj 	y = ts.y + yincr[ts.dir];
1119efc3f44dSpjanzen 	if (POSSIBLE(x, y))
1120efc3f44dSpjanzen 	{
1121efc3f44dSpjanzen 	    if ((hit = cpufire(x, y)))
11227f482600Sdownsj 	    {
11237f482600Sdownsj 		    ts.x = x; ts.y = y; ts.hits++;
11247f482600Sdownsj 		    next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS;
11257f482600Sdownsj 	    }
11267f482600Sdownsj 	    else
1127efc3f44dSpjanzen 	    {
1128efc3f44dSpjanzen 	    /* The only way to get here is if closepack is on; otherwise,
1129efc3f44dSpjanzen 	     * we _have_ sunk the ship.  I set hit to S_SUNK just to get
1130efc3f44dSpjanzen 	     * the additional closepack logic at the end of the switch.
1131efc3f44dSpjanzen 	     */
1132efc3f44dSpjanzen /*assert closepack*/
1133efc3f44dSpjanzen if (!closepack)  error("Assertion failed: not closepack 1");
1134efc3f44dSpjanzen 		    hit = S_SUNK;
11357f482600Sdownsj 		    next = RANDOM_FIRE;
1136efc3f44dSpjanzen 	    }
1137efc3f44dSpjanzen 	}
1138efc3f44dSpjanzen 	else
1139efc3f44dSpjanzen 	{
1140efc3f44dSpjanzen /*assert closepack*/
1141efc3f44dSpjanzen if (!closepack)  error("Assertion failed: not closepack 2");
1142efc3f44dSpjanzen 	    hit = S_SUNK;
1143efc3f44dSpjanzen 	    closenoshot = TRUE;  /* Didn't shoot yet! */
1144efc3f44dSpjanzen 	    next = RANDOM_FIRE;
1145efc3f44dSpjanzen 	}
11467f482600Sdownsj 	break;
1147efc3f44dSpjanzen     }   /* switch(next) */
1148efc3f44dSpjanzen 
1149efc3f44dSpjanzen     if (hit == S_SUNK)
1150efc3f44dSpjanzen     {
1151efc3f44dSpjanzen 	   /* Update cpulongest and cpushortest.  We could increase srchstep
1152efc3f44dSpjanzen 	    * if it's smaller than cpushortest but that makes strategic sense
1153efc3f44dSpjanzen 	    * only if we've been doing continuous diagonal stripes, and that's
1154efc3f44dSpjanzen 	    * less interesting to watch.
1155efc3f44dSpjanzen 	    */
1156efc3f44dSpjanzen 	    ship_t *sp = plyship;
1157efc3f44dSpjanzen 
1158efc3f44dSpjanzen 	    cpushortest = cpulongest;
1159efc3f44dSpjanzen 	    cpulongest  = 0;
1160efc3f44dSpjanzen 	    for (d=0 ; d < SHIPTYPES; d++, sp++)
1161efc3f44dSpjanzen 	    {
1162efc3f44dSpjanzen 		   if (sp->hits < sp->length)
1163efc3f44dSpjanzen 		   {
1164efc3f44dSpjanzen 		cpushortest = (cpushortest < sp->length) ? cpushortest : sp->length;
1165efc3f44dSpjanzen 		cpulongest  = (cpulongest  > sp->length) ? cpulongest  : sp->length;
1166efc3f44dSpjanzen 		   }
1167efc3f44dSpjanzen 	    }
1168efc3f44dSpjanzen 	    /* Now, if we're in closepack mode, we may have knocked off part of
1169efc3f44dSpjanzen 	     * another ship, in which case we shouldn't do RANDOM_FIRE.  A
1170efc3f44dSpjanzen 		* more robust implementation would probably do this check regardless
1171efc3f44dSpjanzen 		* of whether closepack was set or not.
1172efc3f44dSpjanzen 		* Note that MARK_HIT is set only for ships that aren't sunk;
1173efc3f44dSpjanzen 		* hitship() changes the marker to the ship's character when the
1174efc3f44dSpjanzen 		* ship is sunk.
1175efc3f44dSpjanzen 	     */
1176efc3f44dSpjanzen 	    if (closepack)
1177efc3f44dSpjanzen 	    {
1178efc3f44dSpjanzen 		  ts.hits = 0;
1179efc3f44dSpjanzen 		  for (x = 0; x < BWIDTH; x++)
1180efc3f44dSpjanzen 			for (y = 0; y < BDEPTH; y++)
1181efc3f44dSpjanzen 			{
1182efc3f44dSpjanzen 				if (hits[COMPUTER][x][y] == MARK_HIT)
1183efc3f44dSpjanzen 				{
1184efc3f44dSpjanzen 				/* So we found part of another ship.  It may have more
1185efc3f44dSpjanzen 				 * than one hit on it.  Check to see if it does.  If no
1186efc3f44dSpjanzen 				 * hit does, take the last MARK_HIT and be RANDOM_HIT.
1187efc3f44dSpjanzen 				 */
1188efc3f44dSpjanzen 	ts.x = x; ts.y = y; ts.hits = 1;
1189efc3f44dSpjanzen 	for (d = 0; d < 8; d += 2)
1190efc3f44dSpjanzen 	{
1191efc3f44dSpjanzen 	    while ((ONBOARD(ts.x, ts.y)) &&
1192efc3f44dSpjanzen 			(hits[COMPUTER][(int)ts.x][(int)ts.y] == MARK_HIT))
1193efc3f44dSpjanzen 	    {
1194efc3f44dSpjanzen 		    ts.x += xincr[d]; ts.y += yincr[d]; ts.hits++;
1195efc3f44dSpjanzen 	    }
1196efc3f44dSpjanzen 	    if ((--ts.hits > 1) && (ONBOARD(ts.x, ts.y)) &&
1197efc3f44dSpjanzen 		    (hits[COMPUTER][(int)ts.x][(int)ts.y] == 0))
1198efc3f44dSpjanzen 	    {
1199efc3f44dSpjanzen 		    ts.dir = d;
1200efc3f44dSpjanzen 		    ts.x -= xincr[d]; ts.y -= yincr[d];
1201efc3f44dSpjanzen 		    d = 100;                  /* use as a flag */
1202efc3f44dSpjanzen 		    x = BWIDTH; y = BDEPTH;   /* end the loop */
1203efc3f44dSpjanzen 	    } else {
1204efc3f44dSpjanzen 	         ts.x = x; ts.y = y; ts.hits = 1;
1205efc3f44dSpjanzen 	    }
1206efc3f44dSpjanzen 
1207efc3f44dSpjanzen 	}
1208efc3f44dSpjanzen 				}
1209efc3f44dSpjanzen 				if (ts.hits)
1210efc3f44dSpjanzen 				{
1211efc3f44dSpjanzen 					next = (d >= 100) ? FIRST_PASS : RANDOM_HIT;
1212efc3f44dSpjanzen 				} else
1213efc3f44dSpjanzen 					next = RANDOM_FIRE;
1214efc3f44dSpjanzen 				}
1215efc3f44dSpjanzen 	    }
1216efc3f44dSpjanzen 	    if (closenoshot)
1217efc3f44dSpjanzen 	    {
1218efc3f44dSpjanzen 		   return(cputurn());
1219efc3f44dSpjanzen 	    }
12207f482600Sdownsj     }
12217f482600Sdownsj 
12227f482600Sdownsj     /* check for continuation and/or winner */
12237f482600Sdownsj     if (salvo)
12247f482600Sdownsj     {
12257f482600Sdownsj 	(void)refresh();
12267f482600Sdownsj 	(void)sleep(1);
12277f482600Sdownsj     }
12287f482600Sdownsj 
12297f482600Sdownsj     return(hit);
12307f482600Sdownsj }
12317f482600Sdownsj 
1232cd84dbdaSmestre static int
playagain(void)1233cd84dbdaSmestre playagain(void)
12347f482600Sdownsj {
12357f482600Sdownsj     int j;
12367f482600Sdownsj     ship_t *ss;
12377f482600Sdownsj 
12387f482600Sdownsj     for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
12397f482600Sdownsj 	for(j = 0; j < ss->length; j++)
12407f482600Sdownsj 	{
12417f482600Sdownsj 	    cgoto(ss->y + j * yincr[ss->dir], ss->x + j * xincr[ss->dir]);
12427f482600Sdownsj 	    (void) addch((chtype)ss->symbol);
12437f482600Sdownsj 	}
12447f482600Sdownsj 
12457f482600Sdownsj     if(awinna())
12467f482600Sdownsj 	++cpuwon;
12477f482600Sdownsj     else
12487f482600Sdownsj 	++plywon;
12497f482600Sdownsj     j = 18 + strlen(name);
1250efc3f44dSpjanzen 	/* If you play a hundred games or more at a go, you deserve a badly
1251efc3f44dSpjanzen 	 * centred score output.
1252efc3f44dSpjanzen 	 */
12537f482600Sdownsj     if(plywon >= 10)
12547f482600Sdownsj 	++j;
12557f482600Sdownsj     if(cpuwon >= 10)
12567f482600Sdownsj 	++j;
12577f482600Sdownsj     (void) mvprintw(1,(COLWIDTH-j)/2,
12587f482600Sdownsj 		    "%s: %d     Computer: %d",name,plywon,cpuwon);
12597f482600Sdownsj 
12607f482600Sdownsj     prompt(2, (awinna()) ? "Want to be humiliated again, %s [yn]? "
12617f482600Sdownsj 	   : "Going to give me a chance for revenge, %s [yn]? ",name);
12627f482600Sdownsj     return(sgetc("YN") == 'Y');
12637f482600Sdownsj }
12647f482600Sdownsj 
1265cd84dbdaSmestre __dead void
usage(void)1266cd84dbdaSmestre usage(void)
1267efc3f44dSpjanzen {
12686fa5e1daSmestre 	(void) fprintf(stderr, "usage: %s [-b | -s] [-c]\n", getprogname());
1269efc3f44dSpjanzen 	(void) fprintf(stderr, "\tWhere the options are:\n");
1270efc3f44dSpjanzen 	(void) fprintf(stderr, "\t-b : play a blitz game\n");
1271d90ff1a6Ssobrado 	(void) fprintf(stderr, "\t-s : play a salvo game\n");
1272efc3f44dSpjanzen 	(void) fprintf(stderr, "\t-c : ships may be adjacent\n");
1273efc3f44dSpjanzen 	exit(1);
1274efc3f44dSpjanzen }
1275efc3f44dSpjanzen 
1276cd84dbdaSmestre static void
do_options(int c,char * op[])1277cd84dbdaSmestre do_options(int c, char *op[])
12787f482600Sdownsj {
127995bd4873Sguenther     int ch;
12807f482600Sdownsj 
128195bd4873Sguenther     while ((ch = getopt(c, op, "bchs")) != -1) {
128295bd4873Sguenther 	switch (ch) {
12837f482600Sdownsj 	case 'b':
12847f482600Sdownsj 	    blitz = 1;
12857f482600Sdownsj 	    if (salvo == 1)
12867f482600Sdownsj 	    {
12877f482600Sdownsj 		(void) fprintf(stderr,
12887f482600Sdownsj 			"Bad Arg: -b and -s are mutually exclusive\n");
12897f482600Sdownsj 		exit(1);
12907f482600Sdownsj 	    }
12917f482600Sdownsj 	    break;
12927f482600Sdownsj 	case 's':
12937f482600Sdownsj 	    salvo = 1;
12947f482600Sdownsj 	    if (blitz == 1)
12957f482600Sdownsj 	    {
12967f482600Sdownsj 		(void) fprintf(stderr,
12977f482600Sdownsj 			"Bad Arg: -s and -b are mutually exclusive\n");
12987f482600Sdownsj 		exit(1);
12997f482600Sdownsj 	    }
13007f482600Sdownsj 	    break;
13017f482600Sdownsj 	case 'c':
13027f482600Sdownsj 	    closepack = 1;
13037f482600Sdownsj 	    break;
1304efc3f44dSpjanzen 	case 'h':
13057f482600Sdownsj 	default:
130695bd4873Sguenther 	    (void) usage();
13077f482600Sdownsj 	    exit(1);
13087f482600Sdownsj 	}
13097f482600Sdownsj     }
131095bd4873Sguenther     if (op[optind] != NULL)
131195bd4873Sguenther 	(void) usage();
13127f482600Sdownsj }
13137f482600Sdownsj 
1314cd84dbdaSmestre static int
scount(int who)1315cd84dbdaSmestre scount(int who)
13167f482600Sdownsj {
131797419aa0Spjanzen     int i, shots;
131897419aa0Spjanzen     ship_t *sp;
13197f482600Sdownsj 
13207f482600Sdownsj     if (who)
13217f482600Sdownsj 	sp = cpuship;	/* count cpu shots */
13227f482600Sdownsj     else
13237f482600Sdownsj 	sp = plyship;	/* count player shots */
13247f482600Sdownsj 
13257f482600Sdownsj     for (i = 0, shots = 0; i < SHIPTYPES; i++, sp++)
13267f482600Sdownsj     {
13277f482600Sdownsj 	if (sp->hits >= sp->length)
13287f482600Sdownsj 	    continue;		/* dead ship */
13297f482600Sdownsj 	else
13307f482600Sdownsj 	    shots++;
13317f482600Sdownsj     }
13327f482600Sdownsj     return(shots);
13337f482600Sdownsj }
13347f482600Sdownsj 
1335cd84dbdaSmestre int
main(int argc,char * argv[])1336cd84dbdaSmestre main(int argc, char *argv[])
13377f482600Sdownsj {
13387f482600Sdownsj     do_options(argc, argv);
13397f482600Sdownsj 
13407f482600Sdownsj     intro();
1341b9f12921Smestre 
1342b9f12921Smestre     if (pledge("stdio tty", NULL) == -1)
1343b9f12921Smestre         err(1, "pledge");
1344b9f12921Smestre 
13457f482600Sdownsj     do {
13467f482600Sdownsj 	initgame();
13477f482600Sdownsj 	while(awinna() == -1)
13487f482600Sdownsj 	{
13497f482600Sdownsj 	    if (!blitz)
13507f482600Sdownsj 	    {
13517f482600Sdownsj 		if (!salvo)
13527f482600Sdownsj 		{
13537f482600Sdownsj 	    	    if(turn)
13547f482600Sdownsj 			(void) cputurn();
13557f482600Sdownsj 		    else
13567f482600Sdownsj 			(void) plyturn();
13577f482600Sdownsj 		}
1358efc3f44dSpjanzen 		else  /* salvo */
13597f482600Sdownsj 		{
136097419aa0Spjanzen 		    int i;
13617f482600Sdownsj 
13627f482600Sdownsj 		    i = scount(turn);
13637f482600Sdownsj 		    while (i--)
13647f482600Sdownsj 		    {
13657f482600Sdownsj 			if (turn)
13667f482600Sdownsj 			{
13677f482600Sdownsj 			    if (cputurn() && awinna() != -1)
13687f482600Sdownsj 				i = 0;
13697f482600Sdownsj 			}
13707f482600Sdownsj 			else
13717f482600Sdownsj 			{
13727f482600Sdownsj 			    if (plyturn() && awinna() != -1)
13737f482600Sdownsj 				i = 0;
13747f482600Sdownsj 			}
13757f482600Sdownsj 		    }
13767f482600Sdownsj 		}
13777f482600Sdownsj 	    }
1378efc3f44dSpjanzen 	    else  /* blitz */
13797f482600Sdownsj 	    	while(turn ? cputurn() : plyturn())
1380efc3f44dSpjanzen 		{
1381efc3f44dSpjanzen 		    if (turn)   /* Pause between successive computer shots */
1382efc3f44dSpjanzen 		    {
1383efc3f44dSpjanzen 			(void)refresh();
1384efc3f44dSpjanzen 			(void)sleep(1);
1385efc3f44dSpjanzen 		    }
1386efc3f44dSpjanzen 		    if (awinna() != -1)
1387efc3f44dSpjanzen 		     break;
1388efc3f44dSpjanzen 		}
13897f482600Sdownsj 	    turn = OTHER;
13907f482600Sdownsj 	}
13917f482600Sdownsj     } while
13927f482600Sdownsj 	(playagain());
1393efc3f44dSpjanzen     uninitgame(0);
139417641e31Stb     return 0;
13957f482600Sdownsj }
1396