xref: /openbsd-src/games/bs/bs.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: bs.c,v 1.39 2016/03/07 12:07:55 mestre Exp $	*/
2 /*
3  * Copyright (c) 1986, Bruce Holloway
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * - Redistributions of source code must retain the above copyright
11  *  notice, this list of conditions and the following disclaimer.
12  * - Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  * - Neither the name of the <ORGANIZATION> nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*
32  * bs.c - original author: Bruce Holloway
33  *		salvo option by: Chuck A DeGaul
34  * with improved user interface, autoconfiguration and code cleanup
35  *		by Eric S. Raymond <esr@snark.thyrsus.com>
36  * v1.2 with color support and minor portability fixes, November 1990
37  * v2.0 featuring strict ANSI/POSIX conformance, November 1993.
38  * v2.1 with ncurses mouse support, September 1995
39  * v2.2 with bugfixes and strategical improvements, March 1998.
40  */
41 
42 #include <ctype.h>
43 #include <curses.h>
44 #include <err.h>
45 #include <limits.h>
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 typedef struct {
52 	char *name;		/* name of the ship type */
53 	char hits;		/* how many times has this ship been hit? */
54 	char symbol;		/* symbol for game purposes */
55 	char length;		/* length of ship */
56 	signed char x, y;	/* coordinates of ship start point */
57 	unsigned char dir;	/* direction of `bow' */
58 	bool placed;		/* has it been placed on the board? */
59 } ship_t;
60 
61 static void	 announceopts(void);
62 static int	 awinna(void);
63 static bool	 checkplace(int, ship_t *, int);
64 static int	 collidecheck(int, int, int);
65 static int	 cpufire(int, int);
66 static bool	 cpushipcanfit(int, int, int, int);
67 static int	 cputurn(void);
68 static void	 do_options(int, char *[]);
69 static void	 error(char *);
70 static int	 getcoord(int);
71 static ship_t	*hitship(int, int);
72 static void	 initgame(void);
73 static void	 intro(void);
74 static void	 placeship(int, ship_t *, int);
75 static int	 playagain(void);
76 static int	 plyturn(void);
77 static void	 prompt(int, const char *, ...)
78     __attribute__((__format__ (printf, 2, 3)));
79 static void	 randomfire(int *, int *);
80 static void	 randomplace(int, ship_t *);
81 static int	 rnd(int);
82 static int	 scount(int);
83 static int	 sgetc(char *);
84 __dead static void	 uninitgame(int);
85 __dead void	 usage(void);
86 
87 /*
88  * Constants for tuning the random-fire algorithm. It prefers moves that
89  * diagonal-stripe the board with a stripe separation of srchstep. If
90  * no such preferred moves are found, srchstep is decremented.
91  */
92 #define BEGINSTEP	3	/* initial value of srchstep */
93 
94 /* miscellaneous constants */
95 #define SHIPTYPES	5
96 #define OTHER		(1-turn)
97 #define PLAYER		0
98 #define COMPUTER	1
99 #define MARK_HIT	'H'
100 #define MARK_MISS	'o'
101 #define CTRLC		'\003'	/* used as terminate command */
102 #define FF		'\014'	/* used as redraw command */
103 
104 /* coordinate handling */
105 #define BWIDTH		10
106 #define BDEPTH		10
107 
108 /* display symbols */
109 #define SHOWHIT		'*'
110 #define SHOWSPLASH	' '
111 #define IS_SHIP(c)	isupper(c)
112 
113 /* how to position us on player board */
114 #define PYBASE	3
115 #define PXBASE	3
116 #define PY(y)	(PYBASE + (y))
117 #define PX(x)	(PXBASE + (x)*3)
118 #define pgoto(y, x)	(void)move(PY(y), PX(x))
119 
120 /* how to position us on cpu board */
121 #define CYBASE	3
122 #define CXBASE	48
123 #define CY(y)	(CYBASE + (y))
124 #define CX(x)	(CXBASE + (x)*3)
125 #define CYINV(y)	((y) - CYBASE)
126 #define CXINV(x)	(((x) - CXBASE) / 3)
127 #define cgoto(y, x)	(void)move(CY(y), CX(x))
128 
129 #define ONBOARD(x, y)	(x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH)
130 
131 /* other board locations */
132 #define COLWIDTH	80
133 #define PROMPTLINE	21			/* prompt line */
134 #define SYBASE		CYBASE + BDEPTH + 3	/* move key diagram */
135 #define SXBASE		63
136 #define MYBASE		SYBASE - 1		/* diagram caption */
137 #define MXBASE		64
138 #define HYBASE		SYBASE - 1		/* help area */
139 #define HXBASE		0
140 
141 /* this will need to be changed if BWIDTH changes */
142 static char numbers[] = "   0  1  2  3  4  5  6  7  8  9";
143 
144 static char carrier[] = "Aircraft Carrier";
145 static char battle[] = "Battleship";
146 static char sub[] = "Submarine";
147 static char destroy[] = "Destroyer";
148 static char ptboat[] = "PT Boat";
149 
150 static char name[LOGIN_NAME_MAX];
151 static char dftname[] = "stranger";
152 
153 /* direction constants */
154 #define E	0
155 #define SE	1
156 #define S	2
157 #define SW	3
158 #define W	4
159 #define NW	5
160 #define N	6
161 #define NE	7
162 static int xincr[8] = { 1,  1,  0, -1, -1, -1,  0,  1 };
163 static int yincr[8] = { 0,  1,  1,  1,  0, -1, -1, -1 };
164 
165 /* current ship position and direction */
166 static int curx = (BWIDTH / 2);
167 static int cury = (BDEPTH / 2);
168 
169 ship_t plyship[SHIPTYPES] =
170 {
171 	{ carrier,	0, 'A', 5, 0, 0, 0, FALSE },
172 	{ battle,	0, 'B', 4, 0, 0, 0, FALSE },
173 	{ destroy,	0, 'D', 3, 0, 0, 0, FALSE },
174 	{ sub,		0, 'S', 3, 0, 0, 0, FALSE },
175 	{ ptboat,	0, 'P', 2, 0, 0, 0, FALSE }
176 };
177 
178 ship_t cpuship[SHIPTYPES] =
179 {
180 	{ carrier,	0, 'A', 5, 0, 0, 0, FALSE },
181 	{ battle,	0, 'B', 4, 0, 0, 0, FALSE },
182 	{ destroy,	0, 'D', 3, 0, 0, 0, FALSE },
183 	{ sub,		0, 'S', 3, 0, 0, 0, FALSE },
184 	{ ptboat,	0, 'P', 2, 0, 0, 0, FALSE }
185 };
186 
187 /* The following variables (and associated defines), used for computer
188  * targetting, must be global so that they can be reset for each new game
189  * played without restarting the program.
190  */
191 #define POSSIBLE(x, y)	(ONBOARD(x, y) && !hits[COMPUTER][x][y])
192 #define RANDOM_FIRE	0
193 #define RANDOM_HIT	1
194 #define HUNT_DIRECT	2
195 #define FIRST_PASS	3
196 #define REVERSE_JUMP	4
197 #define SECOND_PASS	5
198     static int next = RANDOM_FIRE;
199     static int turncount = 0;
200     static int srchstep = BEGINSTEP;
201 /* Computer needs to keep track of longest and shortest player ships still
202  * not sunk, for better targetting.
203  */
204 static int cpushortest;
205 static int cpulongest;
206 
207 /* "Hits" board, and main board. */
208 static char hits[2][BWIDTH][BDEPTH], board[2][BWIDTH][BDEPTH];
209 
210 static int turn;			/* 0=player, 1=computer */
211 static int plywon=0, cpuwon=0;		/* How many games has each won? */
212 
213 static int salvo, blitz, closepack;
214 
215 /* end the game, either normally or due to signal */
216 static void
217 uninitgame(int sig)
218 {
219     clear();
220     (void)refresh();
221     (void)resetterm();
222     (void)echo();
223     (void)endwin();
224     exit(sig);
225 }
226 
227 /* announce which game options are enabled */
228 static void
229 announceopts(void)
230 {
231     if (salvo || blitz || closepack)
232     {
233 	(void) printw("Playing optional game (");
234 	if (salvo)
235 	    (void) printw("salvo, ");
236 	else
237 	    (void) printw("nosalvo, ");
238 	if (blitz)
239 	    (void) printw("blitz ");
240 	else
241 	    (void) printw("noblitz, ");
242 	if (closepack)
243 	    (void) printw("closepack)");
244 	else
245 	    (void) printw("noclosepack)");
246     }
247     else
248 	(void) printw(
249 	"Playing standard game (noblitz, nosalvo, noclosepack)");
250 }
251 
252 static void
253 intro(void)
254 {
255     char *tmpname;
256 
257     (void) signal(SIGINT,uninitgame);
258     (void) signal(SIGINT,uninitgame);
259     if(signal(SIGQUIT,SIG_IGN) != SIG_IGN)
260 	(void)signal(SIGQUIT,uninitgame);
261 
262     if ((tmpname = getlogin()) != NULL)
263     {
264 	(void)strlcpy(name, tmpname, sizeof(name));
265 	name[0] = toupper((unsigned char)name[0]);
266     }
267     else
268 	(void)strlcpy(name, dftname, sizeof(name));
269 
270     (void)initscr();
271     keypad(stdscr, TRUE);
272     (void)saveterm();
273     (void)nonl();
274     (void)cbreak();
275     (void)noecho();
276 
277     if ((LINES < PROMPTLINE + 3) || (COLS < COLWIDTH)) {
278 	endwin();
279 	errx(1, "screen must be at least %dx%d.", PROMPTLINE + 3, COLWIDTH);
280     }
281 
282 #define	PR	(void)addstr
283     (void)clear();
284     (void)mvaddstr(4,29,"Welcome to Battleship!");
285     (void)move(8,0);
286     PR("                                                  \\\n");
287     PR("                           \\                     \\ \\\n");
288     PR("                          \\ \\                   \\ \\ \\_____________\n");
289     PR("                         \\ \\ \\_____________      \\ \\/            |\n");
290     PR("                          \\ \\/     \\__/    \\      \\/             |\n");
291     PR("                           \\/     \\/  \\/    \\_____/              |__\n");
292     PR("           ________________/    /\\/  ..\\/                         |\n");
293     PR("           \\  S.S. Puffy        \\/\\___o/                          |\n");
294     PR("            \\                     / /\\ \\                          /\n");
295     PR("             \\___________________________________________________/\n");
296 
297     (void) mvaddstr(22,27,"Hit any key to continue..."); (void)refresh();
298     (void) getch();
299 
300     start_color();
301 
302     init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
303     init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
304     init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
305     init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
306     init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
307     init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
308     init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
309     init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
310 
311     (void) mousemask(BUTTON1_CLICKED, (mmask_t *)NULL);
312 }
313 
314 /* print a message at the prompt line */
315 static void
316 prompt(int n, const char *f, ...)
317 {
318     va_list va;
319 
320     (void) move(PROMPTLINE + n, 0);
321     (void) clrtoeol();
322     va_start(va, f);
323     (void) vw_printw(stdscr, f, va);
324     va_end(va);
325     (void) refresh();
326 }
327 
328 static void
329 error(char *s)
330 {
331     (void) move(PROMPTLINE + 2, 0);
332     (void) clrtoeol();
333     if (s)
334     {
335 	(void) addstr(s);
336 	(void) beep();
337     }
338 }
339 
340 static void
341 placeship(int b, ship_t *ss, int vis)
342 {
343     int l;
344 
345     for(l = 0; l < ss->length; ++l)
346     {
347 	int newx = ss->x + l * xincr[ss->dir];
348 	int newy = ss->y + l * yincr[ss->dir];
349 
350 	board[b][newx][newy] = ss->symbol;
351 	if (vis)
352 	{
353 	    pgoto(newy, newx);
354 	    (void) addch((chtype)ss->symbol);
355 	}
356     }
357     ss->hits = 0;
358 }
359 
360 static int
361 rnd(int n)
362 {
363     return(arc4random_uniform(n));
364 }
365 
366 /* generate a valid random ship placement into px,py */
367 static void
368 randomplace(int b, ship_t *ss)
369 {
370     do {
371 	ss->dir = rnd(2) ? E : S;
372 	ss->x = rnd(BWIDTH - (ss->dir == E ? ss->length : 0));
373 	ss->y = rnd(BDEPTH - (ss->dir == S ? ss->length : 0));
374     } while
375 	(!checkplace(b, ss, FALSE));
376 }
377 
378 static void
379 initgame(void)
380 {
381     int i, j, unplaced;
382     ship_t *ss;
383 
384     (void) clear();
385     (void) mvaddstr(0,35,"BATTLESHIPS");
386     (void) move(PROMPTLINE + 2, 0);
387     announceopts();
388 
389     /* Set up global CPU algorithm variables. */
390     next = RANDOM_FIRE;
391     turncount = 0;
392     srchstep = BEGINSTEP;
393     /* set up cpulongest and cpushortest (computer targetting variables) */
394     cpushortest = cpulongest = cpuship->length;
395 
396     memset(board, 0, sizeof(char) * BWIDTH * BDEPTH * 2);
397     memset(hits,  0, sizeof(char) * BWIDTH * BDEPTH * 2);
398     for (i = 0; i < SHIPTYPES; i++)
399     {
400 	ss = cpuship + i;
401 	ss->x = ss->y = ss->dir = ss->hits = 0;
402 	ss->placed = FALSE;
403 	ss = plyship + i;
404 	ss->x = ss->y = ss->dir = ss->hits = 0;
405 	ss->placed = FALSE;
406 
407      if (ss->length > cpulongest)
408 		cpulongest  = ss->length;
409      if (ss->length < cpushortest)
410 		cpushortest = ss->length;
411     }
412 
413     /* draw empty boards */
414     (void) mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board");
415     (void) mvaddstr(PYBASE - 1, PXBASE - 3,numbers);
416     for(i=0; i < BDEPTH; ++i)
417     {
418 	(void) mvaddch(PYBASE + i, PXBASE - 3, (chtype)(i + 'A'));
419 	if (has_colors())
420 	    attron(COLOR_PAIR(COLOR_BLUE));
421 	(void) addch(' ');
422 	for (j = 0; j < BWIDTH; j++)
423 	    (void) addstr(" . ");
424 	attrset(0);
425 	(void) addch(' ');
426 	(void) addch((chtype)(i + 'A'));
427     }
428     (void) mvaddstr(PYBASE + BDEPTH, PXBASE - 3,numbers);
429     (void) mvaddstr(CYBASE - 2, CXBASE + 7,"Hit/Miss Board");
430     (void) mvaddstr(CYBASE - 1, CXBASE - 3, numbers);
431     for(i=0; i < BDEPTH; ++i)
432     {
433 	(void) mvaddch(CYBASE + i, CXBASE - 3, (chtype)(i + 'A'));
434 	if (has_colors())
435 	    attron(COLOR_PAIR(COLOR_BLUE));
436 	(void) addch(' ');
437 	for (j = 0; j < BWIDTH; j++)
438 	    (void) addstr(" . ");
439 	attrset(0);
440 	(void) addch(' ');
441 	(void) addch((chtype)(i + 'A'));
442     }
443 
444     (void) mvaddstr(CYBASE + BDEPTH,CXBASE - 3,numbers);
445 
446     (void) mvprintw(HYBASE,  HXBASE,
447 		    "To position your ships: move the cursor to a spot, then");
448     (void) mvprintw(HYBASE+1,HXBASE,
449 		    "type the first letter of a ship type to select it, then");
450     (void) mvprintw(HYBASE+2,HXBASE,
451 		    "type a direction ([hjkl] or [4862]), indicating how the");
452     (void) mvprintw(HYBASE+3,HXBASE,
453 		    "ship should be pointed. You may also type a ship letter");
454     (void) mvprintw(HYBASE+4,HXBASE,
455 		    "followed by `r' to position it randomly, or type `R' to");
456     (void) mvprintw(HYBASE+5,HXBASE,
457 		    "place all remaining ships randomly.");
458 
459     (void) mvaddstr(MYBASE,   MXBASE, "Aiming keys:");
460     (void) mvaddstr(SYBASE,   SXBASE, "y k u    7 8 9");
461     (void) mvaddstr(SYBASE+1, SXBASE, " \\|/      \\|/ ");
462     (void) mvaddstr(SYBASE+2, SXBASE, "h-+-l    4-+-6");
463     (void) mvaddstr(SYBASE+3, SXBASE, " /|\\      /|\\ ");
464     (void) mvaddstr(SYBASE+4, SXBASE, "b j n    1 2 3");
465 
466     /* have the computer place ships */
467     for(ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
468     {
469 	randomplace(COMPUTER, ss);
470 	placeship(COMPUTER, ss, FALSE);
471     }
472 
473     ss = (ship_t *)NULL;
474     do {
475 	char docked[SHIPTYPES + 2], *cp = docked;
476 	int c;
477 
478 	/* figure which ships still wait to be placed */
479 	*cp++ = 'R';
480 	for (i = 0; i < SHIPTYPES; i++)
481 	    if (!plyship[i].placed)
482 		*cp++ = plyship[i].symbol;
483 	*cp = '\0';
484 
485 	/* get a command letter */
486 	prompt(1, "Type one of [%s] to pick a ship.", docked+1);
487 	do {
488 	    c = getcoord(PLAYER);
489 	} while
490 	    (!strchr(docked, c));
491 
492 	if (c == 'R')
493 	    (void) ungetch('R');
494 	else
495 	{
496 	    /* map that into the corresponding symbol */
497 	    for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
498 		if (ss->symbol == c)
499 		    break;
500 
501 	    prompt(1, "Type one of [hjklrR] to place your %s.", ss->name);
502 	    pgoto(cury, curx);
503 	}
504 regetchar:
505 	c = getch();
506 	switch (c) {
507 	case FF:
508 	    (void)clearok(stdscr, TRUE);
509 	    (void)refresh();
510 	    break;
511 	case 'r':
512 	    prompt(1, "Random-placing your %s", ss->name);
513 	    randomplace(PLAYER, ss);
514 	    placeship(PLAYER, ss, TRUE);
515 		error(NULL);
516 	    ss->placed = TRUE;
517 	    break;
518 	case 'R':
519 	    prompt(1, "Placing the rest of your fleet at random...");
520 	    for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
521 		if (!ss->placed)
522 		{
523 		    randomplace(PLAYER, ss);
524 		    placeship(PLAYER, ss, TRUE);
525 		    ss->placed = TRUE;
526 		}
527 	    error(NULL);
528 	    break;
529 
530 	case 'k': case 'j': case 'h': case 'l':
531 	case '8': case '2': case '4': case '6':
532 	case KEY_LEFT: case KEY_RIGHT: case KEY_UP: case KEY_DOWN:
533 	    ss->x = curx;
534 	    ss->y = cury;
535 
536 	    switch(c)
537 	    {
538 	    case 'k': case '8': case KEY_UP: ss->dir = N; break;
539 	    case 'j': case '2': case KEY_DOWN: ss->dir = S; break;
540 	    case 'h': case '4': case KEY_LEFT: ss->dir = W; break;
541 	    case 'l': case '6': case KEY_RIGHT: ss->dir = E; break;
542 	    }
543 
544 	    if (checkplace(PLAYER, ss, TRUE))
545 	    {
546 		placeship(PLAYER, ss, TRUE);
547 		error(NULL);
548 		ss->placed = TRUE;
549 	    }
550 	    break;
551 	default:
552 	    goto regetchar;
553 	}
554 
555 	for (unplaced = i = 0; i < SHIPTYPES; i++)
556 	    unplaced += !plyship[i].placed;
557     } while
558 	(unplaced);
559 
560     turn = rnd(2);
561 
562     (void) mvprintw(HYBASE,  HXBASE,
563 		    "To fire, move the cursor to your chosen aiming point   ");
564     (void) mvprintw(HYBASE+1,  HXBASE,
565 		    "and strike any key other than a motion key.            ");
566     (void) mvprintw(HYBASE+2,  HXBASE,
567 		    "                                                       ");
568     (void) mvprintw(HYBASE+3,  HXBASE,
569 		    "                                                       ");
570     (void) mvprintw(HYBASE+4,  HXBASE,
571 		    "                                                       ");
572     (void) mvprintw(HYBASE+5,  HXBASE,
573 		    "                                                       ");
574 
575     (void) prompt(0, "Press any key to start...");
576     (void) getch();
577 }
578 
579 static int
580 getcoord(int atcpu)
581 {
582     int ny, nx, c;
583 
584     if (atcpu)
585 	cgoto(cury,curx);
586     else
587 	pgoto(cury, curx);
588     (void)refresh();
589     for (;;)
590     {
591 	if (atcpu)
592 	{
593 	    (void) mvprintw(CYBASE + BDEPTH+1, CXBASE+11, "(%d, %c)", curx, 'A'+cury);
594 	    cgoto(cury, curx);
595 	}
596 	else
597 	{
598 	    (void) mvprintw(PYBASE + BDEPTH+1, PXBASE+11, "(%d, %c)", curx, 'A'+cury);
599 	    pgoto(cury, curx);
600 	}
601 
602 	switch(c = getch())
603 	{
604 	case 'k': case '8':
605 	case KEY_UP:
606 	    ny = cury+BDEPTH-1; nx = curx;
607 	    break;
608 	case 'j': case '2':
609 	case KEY_DOWN:
610 	    ny = cury+1;        nx = curx;
611 	    break;
612 	case 'h': case '4':
613 	case KEY_LEFT:
614 	    ny = cury;          nx = curx+BWIDTH-1;
615 	    break;
616 	case 'l': case '6':
617 	case KEY_RIGHT:
618 	    ny = cury;          nx = curx+1;
619 	    break;
620 	case 'y': case '7':
621 	case KEY_A1:
622 	    ny = cury+BDEPTH-1; nx = curx+BWIDTH-1;
623 	    break;
624 	case 'b': case '1':
625 	case KEY_C1:
626 	    ny = cury+1;        nx = curx+BWIDTH-1;
627 	    break;
628 	case 'u': case '9':
629 	case KEY_A3:
630 	    ny = cury+BDEPTH-1; nx = curx+1;
631 	    break;
632 	case 'n': case '3':
633 	case KEY_C3:
634 	    ny = cury+1;        nx = curx+1;
635 	    break;
636 	case FF:
637 	    nx = curx; ny = cury;
638 	    (void)clearok(stdscr, TRUE);
639 	    (void)refresh();
640 	    break;
641 	case KEY_MOUSE:
642 	    {
643 		MEVENT	myevent;
644 
645 		getmouse(&myevent);
646 		if (atcpu
647 			&& myevent.y >= CY(0) && myevent.y < CY(BDEPTH)
648 			&& myevent.x >= CX(0) && myevent.x < CX(BWIDTH))
649 		{
650 		    curx = CXINV(myevent.x);
651 		    cury = CYINV(myevent.y);
652 		    return(' ');
653 		}
654 		else
655 		    beep();
656 	    }
657 	    break;
658 	case ERR:
659 	    uninitgame(1);
660 	    break;
661 	default:
662 	    if (atcpu)
663 		(void) mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, "      ");
664 	    else
665 		(void) mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, "      ");
666 	    return(c);
667 	}
668 
669 	curx = nx % BWIDTH;
670 	cury = ny % BDEPTH;
671     }
672 }
673 
674 /* is this location on the selected zboard adjacent to a ship? */
675 static int
676 collidecheck(int b, int y, int x)
677 {
678     int	collide;
679 
680     /* anything on the square */
681     if ((collide = IS_SHIP(board[b][x][y])) != 0)
682 	return(collide);
683 
684     /* anything on the neighbors */
685     if (!closepack)
686     {
687 	int i;
688 
689 	for (i = 0; i < 8; i++)
690 	{
691 	    int xend, yend;
692 
693 	    yend = y + yincr[i];
694 	    xend = x + xincr[i];
695 	    if (ONBOARD(xend, yend))
696 		collide += IS_SHIP(board[b][xend][yend]);
697 	}
698     }
699     return(collide);
700 }
701 
702 static bool
703 checkplace(int b, ship_t *ss, int vis)
704 {
705     int l, xend, yend;
706 
707     /* first, check for board edges */
708     xend = ss->x + (ss->length - 1) * xincr[ss->dir];
709     yend = ss->y + (ss->length - 1) * yincr[ss->dir];
710     if (!ONBOARD(xend, yend))
711     {
712 	if (vis)
713 	    switch(rnd(3))
714 	    {
715 	    case 0:
716 		error("Ship is hanging from the edge of the world");
717 		break;
718 	    case 1:
719 		error("Try fitting it on the board");
720 		break;
721 	    case 2:
722 		error("Figure I won't find it if you put it there?");
723 		break;
724 	    }
725 	return(FALSE);
726     }
727 
728     for(l = 0; l < ss->length; ++l)
729     {
730 	if(collidecheck(b, ss->y+l*yincr[ss->dir], ss->x+l*xincr[ss->dir]))
731 	{
732 	    if (vis)
733 		switch(rnd(3))
734 		{
735 		    case 0:
736 			error("There's already a ship there");
737 			break;
738 		    case 1:
739 			error("Collision alert!  Aaaaaagh!");
740 			break;
741 		    case 2:
742 			error("Er, Admiral, what about the other ship?");
743 			break;
744 		    }
745 	    return(FALSE);
746 	    }
747 	}
748     return(TRUE);
749 }
750 
751 static int
752 awinna(void)
753 {
754     int i, j;
755     ship_t *ss;
756 
757     for(i=0; i<2; ++i)
758     {
759 	ss = (i) ? cpuship : plyship;
760 	for(j=0; j < SHIPTYPES; ++j, ++ss)
761 	    if(ss->length > ss->hits)
762 		break;
763 	if (j == SHIPTYPES)
764 	    return(OTHER);
765     }
766     return(-1);
767 }
768 
769 /* register a hit on the targeted ship */
770 static ship_t *
771 hitship(int x, int y)
772 {
773     ship_t *sb, *ss;
774     char sym;
775     int oldx, oldy;
776 
777     getyx(stdscr, oldy, oldx);
778     sb = (turn) ? plyship : cpuship;
779     if(!(sym = board[OTHER][x][y]))
780 	return((ship_t *)NULL);
781     for(ss = sb; ss < sb + SHIPTYPES; ++ss)
782 	if(ss->symbol == sym)
783 	{
784 	    if (++ss->hits < ss->length)	/* still afloat? */
785 		return((ship_t *)NULL);
786 	    else				/* sunk! */
787 	    {
788 		int i, j;
789 
790 		if (!closepack)
791 		    for (j = -1; j <= 1; j++)
792 		    {
793 			int bx = ss->x + j * xincr[(ss->dir + 2) % 8];
794 			int by = ss->y + j * yincr[(ss->dir + 2) % 8];
795 
796 			for (i = -1; i <= ss->length; ++i)
797 			{
798 			    int x1, y1;
799 
800 			    x1 = bx + i * xincr[ss->dir];
801 			    y1 = by + i * yincr[ss->dir];
802 			    if (ONBOARD(x1, y1))
803 			    {
804 				hits[turn][x1][y1] = MARK_MISS;
805 				if (turn == PLAYER)
806 				{
807 				    cgoto(y1, x1);
808 				    if (has_colors())
809 					attron(COLOR_PAIR(COLOR_GREEN));
810 				    (void)addch(MARK_MISS);
811 				    attrset(0);
812 				}
813 			    }
814 			}
815 		    }
816 
817 		for (i = 0; i < ss->length; ++i)
818 		{
819 		    int x1 = ss->x + i * xincr[ss->dir];
820 		    int y1 = ss->y + i * yincr[ss->dir];
821 
822 		    hits[turn][x1][y1] = ss->symbol;
823 		    if (turn  == PLAYER)
824 		    {
825 			cgoto(y1, x1);
826 			(void) addch((chtype)(ss->symbol));
827 		    }
828 		}
829 
830 		(void) move(oldy, oldx);
831 		return(ss);
832 	    }
833 	}
834     (void) move(oldy, oldx);
835     return((ship_t *)NULL);
836 }
837 
838 static int
839 plyturn(void)
840 {
841     ship_t *ss;
842     int hit;
843     char *m = NULL;
844 
845     prompt(1, "Where do you want to shoot? ");
846     for (;;)
847     {
848 	(void) getcoord(COMPUTER);
849 	if (hits[PLAYER][curx][cury])
850 	{
851 	    prompt(1, "You shelled this spot already! Try again.");
852 	    beep();
853 	}
854 	else
855 	    break;
856     }
857     hit = IS_SHIP(board[COMPUTER][curx][cury]);
858     hits[PLAYER][curx][cury] = hit ? MARK_HIT : MARK_MISS;
859     cgoto(cury, curx);
860     if (has_colors()) {
861 	if (hit)
862 	    attron(COLOR_PAIR(COLOR_RED));
863 	else
864 	    attron(COLOR_PAIR(COLOR_GREEN));
865     }
866     (void) addch((chtype)hits[PLAYER][curx][cury]);
867     attrset(0);
868 
869     prompt(1, "You %s.", hit ? "scored a hit" : "missed");
870     if(hit && (ss = hitship(curx, cury)))
871     {
872 	switch(rnd(5))
873 	{
874 	case 0:
875 	    m = " You sank my %s!";
876 	    break;
877 	case 1:
878 	    m = " I have this sinking feeling about my %s....";
879 	    break;
880 	case 2:
881 	    m = " My %s has gone to Davy Jones's locker!";
882 	    break;
883 	case 3:
884 	    m = " Glub, glub -- my %s is headed for the bottom!";
885 	    break;
886 	case 4:
887 	    m = " You'll pick up survivors from my %s, I hope...!";
888 	    break;
889 	}
890 	(void)printw(m, ss->name);
891 	(void)beep();
892     }
893     return(hit);
894 }
895 
896 static int
897 sgetc(char *s)
898 {
899     char *s1;
900     int ch;
901 
902     (void)refresh();
903     for(;;)
904     {
905 	ch = getch();
906 	if (islower(ch))
907 	    ch = toupper(ch);
908 	if (ch == CTRLC)
909 	    uninitgame(0);
910 	for (s1=s; *s1 && ch != *s1; ++s1)
911 	    continue;
912 	if (*s1)
913 	{
914 	    (void) addch((chtype)ch);
915 	    (void) refresh();
916 	    return(ch);
917 	    }
918 	}
919 }
920 
921 /* Checks to see if there's room for a ship of a given length in a given
922  * direction.  If direction is negative, check in all directions.  Note
923  * that North and South are equivalent, as are East and West.
924  */
925 static bool
926 cpushipcanfit(int x, int y, int length, int direction)
927 {
928 	int len = 1;
929 	int x1, y1;
930 
931 	if (direction >= 0)
932 	{
933 		direction %= 4;
934 		while (direction < 8)
935 		{
936 			x1 = x + xincr[direction];
937 			y1 = y + yincr[direction];
938 			while (POSSIBLE(x1,y1))
939 			{
940 			    len++;
941 			    x1 += xincr[direction];
942 			    y1 += yincr[direction];
943 			}
944 			direction += 4;
945 		}
946 		return (len >= length);
947 	}
948 	else
949 	{
950 		return ((cpushipcanfit(x,y,length,E)) ||
951 			    (cpushipcanfit(x,y,length,S)));
952 	}
953 }
954 
955 /* random-fire routine -- implements simple diagonal-striping strategy */
956 static void
957 randomfire(int *px, int *py)
958 {
959     static int huntoffs;		/* Offset on search strategy */
960     int ypossible[BWIDTH * BDEPTH], xpossible[BWIDTH * BDEPTH], nposs;
961     int x, y, i;
962 
963     if (turncount++ == 0)
964 	huntoffs = rnd(srchstep);
965 
966     /* first, list all possible moves on the diagonal stripe */
967     nposs = 0;
968     for (x = 0; x < BWIDTH; x++)
969 	for (y = 0; y < BDEPTH; y++)
970 	    if ((!hits[COMPUTER][x][y]) &&
971 		 	(((x+huntoffs) % srchstep) == (y % srchstep)) &&
972 			(cpushipcanfit(x,y,cpulongest,-1)))
973 	    {
974 		    xpossible[nposs] = x;
975 		    ypossible[nposs] = y;
976 		    nposs++;
977 		}
978     if (nposs)
979     {
980 	i = rnd(nposs);
981 
982 	*px = xpossible[i];
983 	*py = ypossible[i];
984     }
985 	else if (srchstep > cpulongest)
986     {
987 	     --srchstep;
988 	     randomfire(px, py);
989     }
990 	else
991     {
992 		error("No moves possible?? Help!");
993 		exit(1);
994     }
995 }
996 
997 #define S_MISS	0
998 #define S_HIT	1
999 #define S_SUNK	-1
1000 
1001 /* fire away at given location */
1002 static int
1003 cpufire(int x, int y)
1004 {
1005     int hit;
1006     bool sunk;
1007     ship_t *ss = NULL;
1008 
1009     hits[COMPUTER][x][y] = (hit = (board[PLAYER][x][y])) ? MARK_HIT : MARK_MISS;
1010     (void) mvprintw(PROMPTLINE, 0,
1011 	"I shoot at %c%d. I %s!", y + 'A', x, hit ? "hit" : "miss");
1012     if ((sunk = (hit && (ss = hitship(x, y)))))
1013 	(void) printw(" I've sunk your %s", ss->name);
1014     (void)clrtoeol();
1015 
1016     pgoto(y, x);
1017     if (has_colors()) {
1018 	if (hit)
1019 	    attron(COLOR_PAIR(COLOR_RED));
1020 	else
1021 	    attron(COLOR_PAIR(COLOR_GREEN));
1022     }
1023     (void) addch((chtype)(hit ? SHOWHIT : SHOWSPLASH));
1024     attrset(0);
1025 
1026     return(hit ? (sunk ? S_SUNK : S_HIT) : S_MISS);
1027 }
1028 
1029 /*
1030  * This code implements a fairly irregular FSM, so please forgive the rampant
1031  * unstructuredness below. The five labels are states which need to be held
1032  * between computer turns.
1033  */
1034 static int
1035 cputurn(void)
1036 {
1037     static bool used[4];
1038     static ship_t ts;
1039     int navail, x, y, d, n, hit = S_MISS;
1040     bool closenoshot = FALSE;
1041 
1042     switch(next)
1043     {
1044     case RANDOM_FIRE:	/* last shot was random and missed */
1045     refire:
1046 	randomfire(&x, &y);
1047 	if (!(hit = cpufire(x, y)))
1048 	    next = RANDOM_FIRE;
1049 	else
1050 	{
1051 	    ts.x = x; ts.y = y;
1052 	    ts.hits = 1;
1053 	    next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT;
1054 	}
1055 	break;
1056 
1057     case RANDOM_HIT:	/* last shot was random and hit */
1058 	used[E/2] = used[W/2] = (!(cpushipcanfit(ts.x,ts.y,cpushortest,E)));
1059 	used[S/2] = used[N/2] = (!(cpushipcanfit(ts.x,ts.y,cpushortest,S)));
1060 	/* FALLTHROUGH */
1061 
1062     case HUNT_DIRECT:	/* last shot hit, we're looking for ship's long axis */
1063 	for (d = navail = 0; d < 4; d++)
1064 	{
1065 	    x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2];
1066 	    if (!used[d] && POSSIBLE(x, y))
1067 		navail++;
1068 	    else
1069 		used[d] = TRUE;
1070 	}
1071 	if (navail == 0)	/* no valid places for shots adjacent... */
1072 	    goto refire;	/* ...so we must random-fire */
1073 	else
1074 	{
1075 	    for (d = 0, n = rnd(navail) + 1; n; n--,d++)
1076 		while (used[d])
1077 		    d++;
1078 	    d--;
1079 
1080 	    x = ts.x + xincr[d*2];
1081 	    y = ts.y + yincr[d*2];
1082 
1083 	    if (!(hit = cpufire(x, y)))
1084 		next = HUNT_DIRECT;
1085 	    else
1086 	    {
1087 		ts.x = x; ts.y = y; ts.dir = d*2; ts.hits++;
1088 		next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
1089 	    }
1090 	}
1091 	break;
1092 
1093     case FIRST_PASS:	/* we have a start and a direction now */
1094 	x = ts.x + xincr[ts.dir];
1095 	y = ts.y + yincr[ts.dir];
1096 	if (POSSIBLE(x, y))
1097 	{
1098 	    if ((hit = cpufire(x, y)))
1099 	    {
1100 		    ts.x = x; ts.y = y; ts.hits++;
1101 		    next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
1102 	    }
1103 	    else
1104 	         next = REVERSE_JUMP;
1105 	    break;
1106 	}
1107 	else
1108 	    next = REVERSE_JUMP;
1109 	/* FALL THROUGH */
1110 
1111     case REVERSE_JUMP:	/* nail down the ship's other end */
1112 	ts.dir = (ts.dir + 4) % 8;
1113 	ts.x += (ts.hits-1) * xincr[ts.dir];
1114 	ts.y += (ts.hits-1) * yincr[ts.dir];
1115 	/* FALL THROUGH */
1116 
1117     case SECOND_PASS:	/* kill squares not caught on first pass */
1118 	x = ts.x + xincr[ts.dir];
1119 	y = ts.y + yincr[ts.dir];
1120 	if (POSSIBLE(x, y))
1121 	{
1122 	    if ((hit = cpufire(x, y)))
1123 	    {
1124 		    ts.x = x; ts.y = y; ts.hits++;
1125 		    next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS;
1126 	    }
1127 	    else
1128 	    {
1129 	    /* The only way to get here is if closepack is on; otherwise,
1130 	     * we _have_ sunk the ship.  I set hit to S_SUNK just to get
1131 	     * the additional closepack logic at the end of the switch.
1132 	     */
1133 /*assert closepack*/
1134 if (!closepack)  error("Assertion failed: not closepack 1");
1135 		    hit = S_SUNK;
1136 		    next = RANDOM_FIRE;
1137 	    }
1138 	}
1139 	else
1140 	{
1141 /*assert closepack*/
1142 if (!closepack)  error("Assertion failed: not closepack 2");
1143 	    hit = S_SUNK;
1144 	    closenoshot = TRUE;  /* Didn't shoot yet! */
1145 	    next = RANDOM_FIRE;
1146 	}
1147 	break;
1148     }   /* switch(next) */
1149 
1150     if (hit == S_SUNK)
1151     {
1152 	   /* Update cpulongest and cpushortest.  We could increase srchstep
1153 	    * if it's smaller than cpushortest but that makes strategic sense
1154 	    * only if we've been doing continuous diagonal stripes, and that's
1155 	    * less interesting to watch.
1156 	    */
1157 	    ship_t *sp = plyship;
1158 
1159 	    cpushortest = cpulongest;
1160 	    cpulongest  = 0;
1161 	    for (d=0 ; d < SHIPTYPES; d++, sp++)
1162 	    {
1163 		   if (sp->hits < sp->length)
1164 		   {
1165 		cpushortest = (cpushortest < sp->length) ? cpushortest : sp->length;
1166 		cpulongest  = (cpulongest  > sp->length) ? cpulongest  : sp->length;
1167 		   }
1168 	    }
1169 	    /* Now, if we're in closepack mode, we may have knocked off part of
1170 	     * another ship, in which case we shouldn't do RANDOM_FIRE.  A
1171 		* more robust implementation would probably do this check regardless
1172 		* of whether closepack was set or not.
1173 		* Note that MARK_HIT is set only for ships that aren't sunk;
1174 		* hitship() changes the marker to the ship's character when the
1175 		* ship is sunk.
1176 	     */
1177 	    if (closepack)
1178 	    {
1179 		  ts.hits = 0;
1180 		  for (x = 0; x < BWIDTH; x++)
1181 			for (y = 0; y < BDEPTH; y++)
1182 			{
1183 				if (hits[COMPUTER][x][y] == MARK_HIT)
1184 				{
1185 				/* So we found part of another ship.  It may have more
1186 				 * than one hit on it.  Check to see if it does.  If no
1187 				 * hit does, take the last MARK_HIT and be RANDOM_HIT.
1188 				 */
1189 	ts.x = x; ts.y = y; ts.hits = 1;
1190 	for (d = 0; d < 8; d += 2)
1191 	{
1192 	    while ((ONBOARD(ts.x, ts.y)) &&
1193 			(hits[COMPUTER][(int)ts.x][(int)ts.y] == MARK_HIT))
1194 	    {
1195 		    ts.x += xincr[d]; ts.y += yincr[d]; ts.hits++;
1196 	    }
1197 	    if ((--ts.hits > 1) && (ONBOARD(ts.x, ts.y)) &&
1198 		    (hits[COMPUTER][(int)ts.x][(int)ts.y] == 0))
1199 	    {
1200 		    ts.dir = d;
1201 		    ts.x -= xincr[d]; ts.y -= yincr[d];
1202 		    d = 100;                  /* use as a flag */
1203 		    x = BWIDTH; y = BDEPTH;   /* end the loop */
1204 	    } else {
1205 	         ts.x = x; ts.y = y; ts.hits = 1;
1206 	    }
1207 
1208 	}
1209 				}
1210 				if (ts.hits)
1211 				{
1212 					next = (d >= 100) ? FIRST_PASS : RANDOM_HIT;
1213 				} else
1214 					next = RANDOM_FIRE;
1215 				}
1216 	    }
1217 	    if (closenoshot)
1218 	    {
1219 		   return(cputurn());
1220 	    }
1221     }
1222 
1223     /* check for continuation and/or winner */
1224     if (salvo)
1225     {
1226 	(void)refresh();
1227 	(void)sleep(1);
1228     }
1229 
1230     return(hit);
1231 }
1232 
1233 static int
1234 playagain(void)
1235 {
1236     int j;
1237     ship_t *ss;
1238 
1239     for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
1240 	for(j = 0; j < ss->length; j++)
1241 	{
1242 	    cgoto(ss->y + j * yincr[ss->dir], ss->x + j * xincr[ss->dir]);
1243 	    (void) addch((chtype)ss->symbol);
1244 	}
1245 
1246     if(awinna())
1247 	++cpuwon;
1248     else
1249 	++plywon;
1250     j = 18 + strlen(name);
1251 	/* If you play a hundred games or more at a go, you deserve a badly
1252 	 * centred score output.
1253 	 */
1254     if(plywon >= 10)
1255 	++j;
1256     if(cpuwon >= 10)
1257 	++j;
1258     (void) mvprintw(1,(COLWIDTH-j)/2,
1259 		    "%s: %d     Computer: %d",name,plywon,cpuwon);
1260 
1261     prompt(2, (awinna()) ? "Want to be humiliated again, %s [yn]? "
1262 	   : "Going to give me a chance for revenge, %s [yn]? ",name);
1263     return(sgetc("YN") == 'Y');
1264 }
1265 
1266 __dead void
1267 usage(void)
1268 {
1269 	(void) fprintf(stderr, "usage: %s [-b | -s] [-c]\n", getprogname());
1270 	(void) fprintf(stderr, "\tWhere the options are:\n");
1271 	(void) fprintf(stderr, "\t-b : play a blitz game\n");
1272 	(void) fprintf(stderr, "\t-s : play a salvo game\n");
1273 	(void) fprintf(stderr, "\t-c : ships may be adjacent\n");
1274 	exit(1);
1275 }
1276 
1277 static void
1278 do_options(int c, char *op[])
1279 {
1280     int ch;
1281 
1282     while ((ch = getopt(c, op, "bchs")) != -1) {
1283 	switch (ch) {
1284 	case 'b':
1285 	    blitz = 1;
1286 	    if (salvo == 1)
1287 	    {
1288 		(void) fprintf(stderr,
1289 			"Bad Arg: -b and -s are mutually exclusive\n");
1290 		exit(1);
1291 	    }
1292 	    break;
1293 	case 's':
1294 	    salvo = 1;
1295 	    if (blitz == 1)
1296 	    {
1297 		(void) fprintf(stderr,
1298 			"Bad Arg: -s and -b are mutually exclusive\n");
1299 		exit(1);
1300 	    }
1301 	    break;
1302 	case 'c':
1303 	    closepack = 1;
1304 	    break;
1305 	case 'h':
1306 	default:
1307 	    (void) usage();
1308 	    exit(1);
1309 	}
1310     }
1311     if (op[optind] != NULL)
1312 	(void) usage();
1313 }
1314 
1315 static int
1316 scount(int who)
1317 {
1318     int i, shots;
1319     ship_t *sp;
1320 
1321     if (who)
1322 	sp = cpuship;	/* count cpu shots */
1323     else
1324 	sp = plyship;	/* count player shots */
1325 
1326     for (i = 0, shots = 0; i < SHIPTYPES; i++, sp++)
1327     {
1328 	if (sp->hits >= sp->length)
1329 	    continue;		/* dead ship */
1330 	else
1331 	    shots++;
1332     }
1333     return(shots);
1334 }
1335 
1336 int
1337 main(int argc, char *argv[])
1338 {
1339     if (pledge("stdio rpath tty", NULL) == -1)
1340         err(1, "pledge");
1341 
1342     do_options(argc, argv);
1343 
1344     intro();
1345     do {
1346 	initgame();
1347 	while(awinna() == -1)
1348 	{
1349 	    if (!blitz)
1350 	    {
1351 		if (!salvo)
1352 		{
1353 	    	    if(turn)
1354 			(void) cputurn();
1355 		    else
1356 			(void) plyturn();
1357 		}
1358 		else  /* salvo */
1359 		{
1360 		    int i;
1361 
1362 		    i = scount(turn);
1363 		    while (i--)
1364 		    {
1365 			if (turn)
1366 			{
1367 			    if (cputurn() && awinna() != -1)
1368 				i = 0;
1369 			}
1370 			else
1371 			{
1372 			    if (plyturn() && awinna() != -1)
1373 				i = 0;
1374 			}
1375 		    }
1376 		}
1377 	    }
1378 	    else  /* blitz */
1379 	    	while(turn ? cputurn() : plyturn())
1380 		{
1381 		    if (turn)   /* Pause between successive computer shots */
1382 		    {
1383 			(void)refresh();
1384 			(void)sleep(1);
1385 		    }
1386 		    if (awinna() != -1)
1387 		     break;
1388 		}
1389 	    turn = OTHER;
1390 	}
1391     } while
1392 	(playagain());
1393     uninitgame(0);
1394     return 0;
1395 }
1396