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