xref: /openbsd/games/bs/bs.c (revision a6445c1d)
1 /*	$OpenBSD: bs.c,v 1.26 2014/11/16 04:49:48 guenther 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/types.h>
43 #include <curses.h>
44 #include <ctype.h>
45 #include <err.h>
46 #include <limits.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[LOGIN_NAME_MAX];
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 #define	PR	(void)addstr
264     (void)clear();
265     (void)mvaddstr(4,29,"Welcome to Battleship!");
266     (void)move(8,0);
267     PR("                                                  \\\n");
268     PR("                           \\                     \\ \\\n");
269     PR("                          \\ \\                   \\ \\ \\_____________\n");
270     PR("                         \\ \\ \\_____________      \\ \\/            |\n");
271     PR("                          \\ \\/     \\__/    \\      \\/             |\n");
272     PR("                           \\/     \\/  \\/    \\_____/              |__\n");
273     PR("           ________________/    /\\/  ..\\/                         |\n");
274     PR("           \\  S.S. Puffy        \\/\\___o/                          |\n");
275     PR("            \\                     / /\\ \\                          /\n");
276     PR("             \\___________________________________________________/\n");
277 
278     (void) mvaddstr(22,27,"Hit any key to continue..."); (void)refresh();
279     (void) getch();
280 
281 #ifdef A_COLOR
282     start_color();
283 
284     init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
285     init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
286     init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
287     init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
288     init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
289     init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
290     init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
291     init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
292 #endif /* A_COLOR */
293 
294 #ifdef NCURSES_MOUSE_VERSION
295     (void) mousemask(BUTTON1_CLICKED, (mmask_t *)NULL);
296 #endif /* NCURSES_MOUSE_VERSION*/
297 }
298 
299 /* VARARGS1 */
300 static void prompt(int n, char *f, char *s)
301 /* print a message at the prompt line */
302 {
303     (void) move(PROMPTLINE + n, 0);
304     (void) clrtoeol();
305     (void) printw(f, s);
306     (void) refresh();
307 }
308 
309 static void error(char *s)
310 {
311     (void) move(PROMPTLINE + 2, 0);
312     (void) clrtoeol();
313     if (s)
314     {
315 	(void) addstr(s);
316 	(void) beep();
317     }
318 }
319 
320 static void placeship(int b, ship_t *ss, int vis)
321 {
322     int l;
323 
324     for(l = 0; l < ss->length; ++l)
325     {
326 	int newx = ss->x + l * xincr[ss->dir];
327 	int newy = ss->y + l * yincr[ss->dir];
328 
329 	board[b][newx][newy] = ss->symbol;
330 	if (vis)
331 	{
332 	    pgoto(newy, newx);
333 	    (void) addch((chtype)ss->symbol);
334 	}
335     }
336     ss->hits = 0;
337 }
338 
339 static int rnd(int n)
340 {
341     return(arc4random_uniform(n));
342 }
343 
344 static void randomplace(int b, ship_t *ss)
345 /* generate a valid random ship placement into px,py */
346 {
347     do {
348 	ss->dir = rnd(2) ? E : S;
349 	ss->x = rnd(BWIDTH - (ss->dir == E ? ss->length : 0));
350 	ss->y = rnd(BDEPTH - (ss->dir == S ? ss->length : 0));
351     } while
352 	(!checkplace(b, ss, FALSE));
353 }
354 
355 static void initgame(void)
356 {
357     int i, j, unplaced;
358     ship_t *ss;
359 
360     (void) clear();
361     (void) mvaddstr(0,35,"BATTLESHIPS");
362     (void) move(PROMPTLINE + 2, 0);
363     announceopts();
364 
365     /* Set up global CPU algorithm variables. */
366     next = RANDOM_FIRE;
367     turncount = 0;
368     srchstep = BEGINSTEP;
369     /* set up cpulongest and cpushortest (computer targetting variables) */
370     cpushortest = cpulongest = cpuship->length;
371 
372     memset(board, 0, sizeof(char) * BWIDTH * BDEPTH * 2);
373     memset(hits,  0, sizeof(char) * BWIDTH * BDEPTH * 2);
374     for (i = 0; i < SHIPTYPES; i++)
375     {
376 	ss = cpuship + i;
377 	ss->x = ss->y = ss->dir = ss->hits = 0;
378 	ss->placed = FALSE;
379 	ss = plyship + i;
380 	ss->x = ss->y = ss->dir = ss->hits = 0;
381 	ss->placed = FALSE;
382 
383      if (ss->length > cpulongest)
384 		cpulongest  = ss->length;
385      if (ss->length < cpushortest)
386 		cpushortest = ss->length;
387     }
388 
389     /* draw empty boards */
390     (void) mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board");
391     (void) mvaddstr(PYBASE - 1, PXBASE - 3,numbers);
392     for(i=0; i < BDEPTH; ++i)
393     {
394 	(void) mvaddch(PYBASE + i, PXBASE - 3, (chtype)(i + 'A'));
395 #ifdef A_COLOR
396 	if (has_colors())
397 	    attron(COLOR_PAIR(COLOR_BLUE));
398 #endif /* A_COLOR */
399 	(void) addch(' ');
400 	for (j = 0; j < BWIDTH; j++)
401 	    (void) addstr(" . ");
402 #ifdef A_COLOR
403 	attrset(0);
404 #endif /* A_COLOR */
405 	(void) addch(' ');
406 	(void) addch((chtype)(i + 'A'));
407     }
408     (void) mvaddstr(PYBASE + BDEPTH, PXBASE - 3,numbers);
409     (void) mvaddstr(CYBASE - 2, CXBASE + 7,"Hit/Miss Board");
410     (void) mvaddstr(CYBASE - 1, CXBASE - 3, numbers);
411     for(i=0; i < BDEPTH; ++i)
412     {
413 	(void) mvaddch(CYBASE + i, CXBASE - 3, (chtype)(i + 'A'));
414 #ifdef A_COLOR
415 	if (has_colors())
416 	    attron(COLOR_PAIR(COLOR_BLUE));
417 #endif /* A_COLOR */
418 	(void) addch(' ');
419 	for (j = 0; j < BWIDTH; j++)
420 	    (void) addstr(" . ");
421 #ifdef A_COLOR
422 	attrset(0);
423 #endif /* A_COLOR */
424 	(void) addch(' ');
425 	(void) addch((chtype)(i + 'A'));
426     }
427 
428     (void) mvaddstr(CYBASE + BDEPTH,CXBASE - 3,numbers);
429 
430     (void) mvprintw(HYBASE,  HXBASE,
431 		    "To position your ships: move the cursor to a spot, then");
432     (void) mvprintw(HYBASE+1,HXBASE,
433 		    "type the first letter of a ship type to select it, then");
434     (void) mvprintw(HYBASE+2,HXBASE,
435 		    "type a direction ([hjkl] or [4862]), indicating how the");
436     (void) mvprintw(HYBASE+3,HXBASE,
437 		    "ship should be pointed. You may also type a ship letter");
438     (void) mvprintw(HYBASE+4,HXBASE,
439 		    "followed by `r' to position it randomly, or type `R' to");
440     (void) mvprintw(HYBASE+5,HXBASE,
441 		    "place all remaining ships randomly.");
442 
443     (void) mvaddstr(MYBASE,   MXBASE, "Aiming keys:");
444     (void) mvaddstr(SYBASE,   SXBASE, "y k u    7 8 9");
445     (void) mvaddstr(SYBASE+1, SXBASE, " \\|/      \\|/ ");
446     (void) mvaddstr(SYBASE+2, SXBASE, "h-+-l    4-+-6");
447     (void) mvaddstr(SYBASE+3, SXBASE, " /|\\      /|\\ ");
448     (void) mvaddstr(SYBASE+4, SXBASE, "b j n    1 2 3");
449 
450     /* have the computer place ships */
451     for(ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
452     {
453 	randomplace(COMPUTER, ss);
454 	placeship(COMPUTER, ss, FALSE);
455     }
456 
457     ss = (ship_t *)NULL;
458     do {
459 	char c, docked[SHIPTYPES + 2], *cp = docked;
460 
461 	/* figure which ships still wait to be placed */
462 	*cp++ = 'R';
463 	for (i = 0; i < SHIPTYPES; i++)
464 	    if (!plyship[i].placed)
465 		*cp++ = plyship[i].symbol;
466 	*cp = '\0';
467 
468 	/* get a command letter */
469 	prompt(1, "Type one of [%s] to pick a ship.", docked+1);
470 	do {
471 	    c = getcoord(PLAYER);
472 	} while
473 	    (!strchr(docked, c));
474 
475 	if (c == 'R')
476 	    (void) ungetch('R');
477 	else
478 	{
479 	    /* map that into the corresponding symbol */
480 	    for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
481 		if (ss->symbol == c)
482 		    break;
483 
484 	    prompt(1, "Type one of [hjklrR] to place your %s.", ss->name);
485 	    pgoto(cury, curx);
486 	}
487 
488 	do {
489 	    c = getch();
490 	} while
491 	    (!strchr("hjklrR", c) || c == FF);
492 
493 	if (c == FF)
494 	{
495 	    (void)clearok(stdscr, TRUE);
496 	    (void)refresh();
497 	}
498 	else if (c == 'r')
499 	{
500 	    prompt(1, "Random-placing your %s", ss->name);
501 	    randomplace(PLAYER, ss);
502 	    placeship(PLAYER, ss, TRUE);
503 	    error((char *)NULL);
504 	    ss->placed = TRUE;
505 	}
506 	else if (c == 'R')
507 	{
508 	    prompt(1, "Placing the rest of your fleet at random...", "");
509 	    for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
510 		if (!ss->placed)
511 		{
512 		    randomplace(PLAYER, ss);
513 		    placeship(PLAYER, ss, TRUE);
514 		    ss->placed = TRUE;
515 		}
516 	    error((char *)NULL);
517 	}
518 	else if (strchr("hjkl8462", c))
519 	{
520 	    ss->x = curx;
521 	    ss->y = cury;
522 
523 	    switch(c)
524 	    {
525 	    case 'k': case '8': ss->dir = N; break;
526 	    case 'j': case '2': ss->dir = S; break;
527 	    case 'h': case '4': ss->dir = W; break;
528 	    case 'l': case '6': ss->dir = E; break;
529 	    }
530 
531 	    if (checkplace(PLAYER, ss, TRUE))
532 	    {
533 		placeship(PLAYER, ss, TRUE);
534 		error((char *)NULL);
535 		ss->placed = TRUE;
536 	    }
537 	}
538 
539 	for (unplaced = i = 0; i < SHIPTYPES; i++)
540 	    unplaced += !plyship[i].placed;
541     } while
542 	(unplaced);
543 
544     turn = rnd(2);
545 
546     (void) mvprintw(HYBASE,  HXBASE,
547 		    "To fire, move the cursor to your chosen aiming point   ");
548     (void) mvprintw(HYBASE+1,  HXBASE,
549 		    "and strike any key other than a motion key.            ");
550     (void) mvprintw(HYBASE+2,  HXBASE,
551 		    "                                                       ");
552     (void) mvprintw(HYBASE+3,  HXBASE,
553 		    "                                                       ");
554     (void) mvprintw(HYBASE+4,  HXBASE,
555 		    "                                                       ");
556     (void) mvprintw(HYBASE+5,  HXBASE,
557 		    "                                                       ");
558 
559     (void) prompt(0, "Press any key to start...", "");
560     (void) getch();
561 }
562 
563 static int getcoord(int atcpu)
564 {
565     int ny, nx, c;
566 
567     if (atcpu)
568 	cgoto(cury,curx);
569     else
570 	pgoto(cury, curx);
571     (void)refresh();
572     for (;;)
573     {
574 	if (atcpu)
575 	{
576 	    (void) mvprintw(CYBASE + BDEPTH+1, CXBASE+11, "(%d, %c)", curx, 'A'+cury);
577 	    cgoto(cury, curx);
578 	}
579 	else
580 	{
581 	    (void) mvprintw(PYBASE + BDEPTH+1, PXBASE+11, "(%d, %c)", curx, 'A'+cury);
582 	    pgoto(cury, curx);
583 	}
584 
585 	switch(c = getch())
586 	{
587 	case 'k': case '8':
588 #ifdef KEY_MIN
589 	case KEY_UP:
590 #endif /* KEY_MIN */
591 	    ny = cury+BDEPTH-1; nx = curx;
592 	    break;
593 	case 'j': case '2':
594 #ifdef KEY_MIN
595 	case KEY_DOWN:
596 #endif /* KEY_MIN */
597 	    ny = cury+1;        nx = curx;
598 	    break;
599 	case 'h': case '4':
600 #ifdef KEY_MIN
601 	case KEY_LEFT:
602 #endif /* KEY_MIN */
603 	    ny = cury;          nx = curx+BWIDTH-1;
604 	    break;
605 	case 'l': case '6':
606 #ifdef KEY_MIN
607 	case KEY_RIGHT:
608 #endif /* KEY_MIN */
609 	    ny = cury;          nx = curx+1;
610 	    break;
611 	case 'y': case '7':
612 #ifdef KEY_MIN
613 	case KEY_A1:
614 #endif /* KEY_MIN */
615 	    ny = cury+BDEPTH-1; nx = curx+BWIDTH-1;
616 	    break;
617 	case 'b': case '1':
618 #ifdef KEY_MIN
619 	case KEY_C1:
620 #endif /* KEY_MIN */
621 	    ny = cury+1;        nx = curx+BWIDTH-1;
622 	    break;
623 	case 'u': case '9':
624 #ifdef KEY_MIN
625 	case KEY_A3:
626 #endif /* KEY_MIN */
627 	    ny = cury+BDEPTH-1; nx = curx+1;
628 	    break;
629 	case 'n': case '3':
630 #ifdef KEY_MIN
631 	case KEY_C3:
632 #endif /* KEY_MIN */
633 	    ny = cury+1;        nx = curx+1;
634 	    break;
635 	case FF:
636 	    nx = curx; ny = cury;
637 	    (void)clearok(stdscr, TRUE);
638 	    (void)refresh();
639 	    break;
640 #ifdef NCURSES_MOUSE_VERSION
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 #endif /* NCURSES_MOUSE_VERSION */
659 	case ERR:
660 	    uninitgame(1);
661 	    break;
662 	default:
663 	    if (atcpu)
664 		(void) mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, "      ");
665 	    else
666 		(void) mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, "      ");
667 	    return(c);
668 	}
669 
670 	curx = nx % BWIDTH;
671 	cury = ny % BDEPTH;
672     }
673 }
674 
675 static int collidecheck(int b, int y, int x)
676 /* is this location on the selected zboard adjacent to a ship? */
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 checkplace(int b, ship_t *ss, int vis)
703 {
704     int l, xend, yend;
705 
706     /* first, check for board edges */
707     xend = ss->x + (ss->length - 1) * xincr[ss->dir];
708     yend = ss->y + (ss->length - 1) * yincr[ss->dir];
709     if (!ONBOARD(xend, yend))
710     {
711 	if (vis)
712 	    switch(rnd(3))
713 	    {
714 	    case 0:
715 		error("Ship is hanging from the edge of the world");
716 		break;
717 	    case 1:
718 		error("Try fitting it on the board");
719 		break;
720 	    case 2:
721 		error("Figure I won't find it if you put it there?");
722 		break;
723 	    }
724 	return(FALSE);
725     }
726 
727     for(l = 0; l < ss->length; ++l)
728     {
729 	if(collidecheck(b, ss->y+l*yincr[ss->dir], ss->x+l*xincr[ss->dir]))
730 	{
731 	    if (vis)
732 		switch(rnd(3))
733 		{
734 		    case 0:
735 			error("There's already a ship there");
736 			break;
737 		    case 1:
738 			error("Collision alert!  Aaaaaagh!");
739 			break;
740 		    case 2:
741 			error("Er, Admiral, what about the other ship?");
742 			break;
743 		    }
744 	    return(FALSE);
745 	    }
746 	}
747     return(TRUE);
748 }
749 
750 static int awinna(void)
751 {
752     int i, j;
753     ship_t *ss;
754 
755     for(i=0; i<2; ++i)
756     {
757 	ss = (i) ? cpuship : plyship;
758 	for(j=0; j < SHIPTYPES; ++j, ++ss)
759 	    if(ss->length > ss->hits)
760 		break;
761 	if (j == SHIPTYPES)
762 	    return(OTHER);
763     }
764     return(-1);
765 }
766 
767 static ship_t *hitship(int x, int y)
768 /* register a hit on the targeted ship */
769 {
770     ship_t *sb, *ss;
771     char sym;
772     int oldx, oldy;
773 
774     getyx(stdscr, oldy, oldx);
775     sb = (turn) ? plyship : cpuship;
776     if(!(sym = board[OTHER][x][y]))
777 	return((ship_t *)NULL);
778     for(ss = sb; ss < sb + SHIPTYPES; ++ss)
779 	if(ss->symbol == sym)
780 	{
781 	    if (++ss->hits < ss->length)	/* still afloat? */
782 		return((ship_t *)NULL);
783 	    else				/* sunk! */
784 	    {
785 		int i, j;
786 
787 		if (!closepack)
788 		    for (j = -1; j <= 1; j++)
789 		    {
790 			int bx = ss->x + j * xincr[(ss->dir + 2) % 8];
791 			int by = ss->y + j * yincr[(ss->dir + 2) % 8];
792 
793 			for (i = -1; i <= ss->length; ++i)
794 			{
795 			    int x1, y1;
796 
797 			    x1 = bx + i * xincr[ss->dir];
798 			    y1 = by + i * yincr[ss->dir];
799 			    if (ONBOARD(x1, y1))
800 			    {
801 				hits[turn][x1][y1] = MARK_MISS;
802 				if (turn == PLAYER)
803 				{
804 				    cgoto(y1, x1);
805 #ifdef A_COLOR
806 				    if (has_colors())
807 					attron(COLOR_PAIR(COLOR_GREEN));
808 #endif /* A_COLOR */
809 				    (void)addch(MARK_MISS);
810 #ifdef A_COLOR
811 				    attrset(0);
812 #endif /* A_COLOR */
813 				}
814 			    }
815 			}
816 		    }
817 
818 		for (i = 0; i < ss->length; ++i)
819 		{
820 		    int x1 = ss->x + i * xincr[ss->dir];
821 		    int y1 = ss->y + i * yincr[ss->dir];
822 
823 		    hits[turn][x1][y1] = ss->symbol;
824 		    if (turn  == PLAYER)
825 		    {
826 			cgoto(y1, x1);
827 			(void) addch((chtype)(ss->symbol));
828 		    }
829 		}
830 
831 		(void) move(oldy, oldx);
832 		return(ss);
833 	    }
834 	}
835     (void) move(oldy, oldx);
836     return((ship_t *)NULL);
837 }
838 
839 static int 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 #ifdef A_COLOR
861     if (has_colors()) {
862 	if (hit)
863 	    attron(COLOR_PAIR(COLOR_RED));
864 	else
865 	    attron(COLOR_PAIR(COLOR_GREEN));
866     }
867 #endif /* A_COLOR */
868     (void) addch((chtype)hits[PLAYER][curx][cury]);
869 #ifdef A_COLOR
870     attrset(0);
871 #endif /* A_COLOR */
872 
873     prompt(1, "You %s.", hit ? "scored a hit" : "missed");
874     if(hit && (ss = hitship(curx, cury)))
875     {
876 	switch(rnd(5))
877 	{
878 	case 0:
879 	    m = " You sank my %s!";
880 	    break;
881 	case 1:
882 	    m = " I have this sinking feeling about my %s....";
883 	    break;
884 	case 2:
885 	    m = " My %s has gone to Davy Jones's locker!";
886 	    break;
887 	case 3:
888 	    m = " Glub, glub -- my %s is headed for the bottom!";
889 	    break;
890 	case 4:
891 	    m = " You'll pick up survivors from my %s, I hope...!";
892 	    break;
893 	}
894 	(void)printw(m, ss->name);
895 	(void)beep();
896     }
897     return(hit);
898 }
899 
900 static int sgetc(char *s)
901 {
902     char *s1;
903     int ch;
904 
905     (void)refresh();
906     for(;;)
907     {
908 	ch = getch();
909 	if (islower(ch))
910 	    ch = toupper(ch);
911 	if (ch == CTRLC)
912 	    uninitgame(0);
913 	for (s1=s; *s1 && ch != *s1; ++s1)
914 	    continue;
915 	if (*s1)
916 	{
917 	    (void) addch((chtype)ch);
918 	    (void) refresh();
919 	    return(ch);
920 	    }
921 	}
922 }
923 
924 static bool cpushipcanfit(int x, int y, int length, int direction)
925 /* Checks to see if there's room for a ship of a given length in a given
926  * direction.  If direction is negative, check in all directions.  Note
927  * that North and South are equivalent, as are East and West.
928  */
929 {
930 	int len = 1;
931 	int x1, y1;
932 
933 	if (direction >= 0)
934 	{
935 		direction %= 4;
936 		while (direction < 8)
937 		{
938 			x1 = x + xincr[direction];
939 			y1 = y + yincr[direction];
940 			while (POSSIBLE(x1,y1))
941 			{
942 			    len++;
943 			    x1 += xincr[direction];
944 			    y1 += yincr[direction];
945 			}
946 			direction += 4;
947 		}
948 		return (len >= length);
949 	}
950 	else
951 	{
952 		return ((cpushipcanfit(x,y,length,E)) ||
953 			    (cpushipcanfit(x,y,length,S)));
954 	}
955 }
956 
957 
958 static void randomfire(int *px, int *py)
959 /* random-fire routine -- implements simple diagonal-striping strategy */
960 {
961     static int huntoffs;		/* Offset on search strategy */
962     int ypossible[BWIDTH * BDEPTH], xpossible[BWIDTH * BDEPTH], nposs;
963     int x, y, i;
964 
965     if (turncount++ == 0)
966 	huntoffs = rnd(srchstep);
967 
968     /* first, list all possible moves on the diagonal stripe */
969     nposs = 0;
970     for (x = 0; x < BWIDTH; x++)
971 	for (y = 0; y < BDEPTH; y++)
972 	    if ((!hits[COMPUTER][x][y]) &&
973 		 	(((x+huntoffs) % srchstep) == (y % srchstep)) &&
974 			(cpushipcanfit(x,y,cpulongest,-1)))
975 	    {
976 		    xpossible[nposs] = x;
977 		    ypossible[nposs] = y;
978 		    nposs++;
979 		}
980     if (nposs)
981     {
982 	i = rnd(nposs);
983 
984 	*px = xpossible[i];
985 	*py = ypossible[i];
986     }
987 	else if (srchstep > cpulongest)
988     {
989 	     --srchstep;
990 	     randomfire(px, py);
991     }
992 	else
993     {
994 		error("No moves possible?? Help!");
995 		exit(1);
996     }
997 }
998 
999 #define S_MISS	0
1000 #define S_HIT	1
1001 #define S_SUNK	-1
1002 
1003 static int cpufire(int x, int y)
1004 /* fire away at given location */
1005 {
1006     int hit;
1007     bool sunk;
1008     ship_t *ss = NULL;
1009 
1010     hits[COMPUTER][x][y] = (hit = (board[PLAYER][x][y])) ? MARK_HIT : MARK_MISS;
1011     (void) mvprintw(PROMPTLINE, 0,
1012 	"I shoot at %c%d. I %s!", y + 'A', x, hit ? "hit" : "miss");
1013     if ((sunk = (hit && (ss = hitship(x, y)))))
1014 	(void) printw(" I've sunk your %s", ss->name);
1015     (void)clrtoeol();
1016 
1017     pgoto(y, x);
1018 #ifdef A_COLOR
1019     if (has_colors()) {
1020 	if (hit)
1021 	    attron(COLOR_PAIR(COLOR_RED));
1022 	else
1023 	    attron(COLOR_PAIR(COLOR_GREEN));
1024     }
1025 #endif /* A_COLOR */
1026     (void) addch((chtype)(hit ? SHOWHIT : SHOWSPLASH));
1027 #ifdef A_COLOR
1028     attrset(0);
1029 #endif /* A_COLOR */
1030 
1031     return(hit ? (sunk ? S_SUNK : S_HIT) : S_MISS);
1032 }
1033 
1034 /*
1035  * This code implements a fairly irregular FSM, so please forgive the rampant
1036  * unstructuredness below. The five labels are states which need to be held
1037  * between computer turns.
1038  */
1039 static int cputurn(void)
1040 {
1041     static bool used[4];
1042     static ship_t ts;
1043     int navail, x, y, d, n, hit = S_MISS;
1044     bool closenoshot = FALSE;
1045 
1046     switch(next)
1047     {
1048     case RANDOM_FIRE:	/* last shot was random and missed */
1049     refire:
1050 	randomfire(&x, &y);
1051 	if (!(hit = cpufire(x, y)))
1052 	    next = RANDOM_FIRE;
1053 	else
1054 	{
1055 	    ts.x = x; ts.y = y;
1056 	    ts.hits = 1;
1057 	    next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT;
1058 	}
1059 	break;
1060 
1061     case RANDOM_HIT:	/* last shot was random and hit */
1062 	used[E/2] = used[W/2] = (!(cpushipcanfit(ts.x,ts.y,cpushortest,E)));
1063 	used[S/2] = used[N/2] = (!(cpushipcanfit(ts.x,ts.y,cpushortest,S)));
1064 	/* FALLTHROUGH */
1065 
1066     case HUNT_DIRECT:	/* last shot hit, we're looking for ship's long axis */
1067 	for (d = navail = 0; d < 4; d++)
1068 	{
1069 	    x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2];
1070 	    if (!used[d] && POSSIBLE(x, y))
1071 		navail++;
1072 	    else
1073 		used[d] = TRUE;
1074 	}
1075 	if (navail == 0)	/* no valid places for shots adjacent... */
1076 	    goto refire;	/* ...so we must random-fire */
1077 	else
1078 	{
1079 	    for (d = 0, n = rnd(navail) + 1; n; n--,d++)
1080 		while (used[d])
1081 		    d++;
1082 	    d--;
1083 
1084 	    x = ts.x + xincr[d*2];
1085 	    y = ts.y + yincr[d*2];
1086 
1087 	    if (!(hit = cpufire(x, y)))
1088 		next = HUNT_DIRECT;
1089 	    else
1090 	    {
1091 		ts.x = x; ts.y = y; ts.dir = d*2; ts.hits++;
1092 		next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
1093 	    }
1094 	}
1095 	break;
1096 
1097     case FIRST_PASS:	/* we have a start and a direction now */
1098 	x = ts.x + xincr[ts.dir];
1099 	y = ts.y + yincr[ts.dir];
1100 	if (POSSIBLE(x, y))
1101 	{
1102 	    if ((hit = cpufire(x, y)))
1103 	    {
1104 		    ts.x = x; ts.y = y; ts.hits++;
1105 		    next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
1106 	    }
1107 	    else
1108 	         next = REVERSE_JUMP;
1109 	    break;
1110 	}
1111 	else
1112 	    next = REVERSE_JUMP;
1113 	/* FALL THROUGH */
1114 
1115     case REVERSE_JUMP:	/* nail down the ship's other end */
1116 	ts.dir = (ts.dir + 4) % 8;
1117 	ts.x += (ts.hits-1) * xincr[ts.dir];
1118 	ts.y += (ts.hits-1) * yincr[ts.dir];
1119 	/* FALL THROUGH */
1120 
1121     case SECOND_PASS:	/* kill squares not caught on first pass */
1122 	x = ts.x + xincr[ts.dir];
1123 	y = ts.y + yincr[ts.dir];
1124 	if (POSSIBLE(x, y))
1125 	{
1126 	    if ((hit = cpufire(x, y)))
1127 	    {
1128 		    ts.x = x; ts.y = y; ts.hits++;
1129 		    next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS;
1130 	    }
1131 	    else
1132 	    {
1133 	    /* The only way to get here is if closepack is on; otherwise,
1134 	     * we _have_ sunk the ship.  I set hit to S_SUNK just to get
1135 	     * the additional closepack logic at the end of the switch.
1136 	     */
1137 /*assert closepack*/
1138 if (!closepack)  error("Assertion failed: not closepack 1");
1139 		    hit = S_SUNK;
1140 		    next = RANDOM_FIRE;
1141 	    }
1142 	}
1143 	else
1144 	{
1145 /*assert closepack*/
1146 if (!closepack)  error("Assertion failed: not closepack 2");
1147 	    hit = S_SUNK;
1148 	    closenoshot = TRUE;  /* Didn't shoot yet! */
1149 	    next = RANDOM_FIRE;
1150 	}
1151 	break;
1152     }   /* switch(next) */
1153 
1154     if (hit == S_SUNK)
1155     {
1156 	   /* Update cpulongest and cpushortest.  We could increase srchstep
1157 	    * if it's smaller than cpushortest but that makes strategic sense
1158 	    * only if we've been doing continuous diagonal stripes, and that's
1159 	    * less interesting to watch.
1160 	    */
1161 	    ship_t *sp = plyship;
1162 
1163 	    cpushortest = cpulongest;
1164 	    cpulongest  = 0;
1165 	    for (d=0 ; d < SHIPTYPES; d++, sp++)
1166 	    {
1167 		   if (sp->hits < sp->length)
1168 		   {
1169 		cpushortest = (cpushortest < sp->length) ? cpushortest : sp->length;
1170 		cpulongest  = (cpulongest  > sp->length) ? cpulongest  : sp->length;
1171 		   }
1172 	    }
1173 	    /* Now, if we're in closepack mode, we may have knocked off part of
1174 	     * another ship, in which case we shouldn't do RANDOM_FIRE.  A
1175 		* more robust implementation would probably do this check regardless
1176 		* of whether closepack was set or not.
1177 		* Note that MARK_HIT is set only for ships that aren't sunk;
1178 		* hitship() changes the marker to the ship's character when the
1179 		* ship is sunk.
1180 	     */
1181 	    if (closepack)
1182 	    {
1183 		  ts.hits = 0;
1184 		  for (x = 0; x < BWIDTH; x++)
1185 			for (y = 0; y < BDEPTH; y++)
1186 			{
1187 				if (hits[COMPUTER][x][y] == MARK_HIT)
1188 				{
1189 				/* So we found part of another ship.  It may have more
1190 				 * than one hit on it.  Check to see if it does.  If no
1191 				 * hit does, take the last MARK_HIT and be RANDOM_HIT.
1192 				 */
1193 	ts.x = x; ts.y = y; ts.hits = 1;
1194 	for (d = 0; d < 8; d += 2)
1195 	{
1196 	    while ((ONBOARD(ts.x, ts.y)) &&
1197 			(hits[COMPUTER][(int)ts.x][(int)ts.y] == MARK_HIT))
1198 	    {
1199 		    ts.x += xincr[d]; ts.y += yincr[d]; ts.hits++;
1200 	    }
1201 	    if ((--ts.hits > 1) && (ONBOARD(ts.x, ts.y)) &&
1202 		    (hits[COMPUTER][(int)ts.x][(int)ts.y] == 0))
1203 	    {
1204 		    ts.dir = d;
1205 		    ts.x -= xincr[d]; ts.y -= yincr[d];
1206 		    d = 100;                  /* use as a flag */
1207 		    x = BWIDTH; y = BDEPTH;   /* end the loop */
1208 	    } else {
1209 	         ts.x = x; ts.y = y; ts.hits = 1;
1210 	    }
1211 
1212 	}
1213 				}
1214 				if (ts.hits)
1215 				{
1216 					next = (d >= 100) ? FIRST_PASS : RANDOM_HIT;
1217 				} else
1218 					next = RANDOM_FIRE;
1219 				}
1220 	    }
1221 	    if (closenoshot)
1222 	    {
1223 		   return(cputurn());
1224 	    }
1225     }
1226 
1227     /* check for continuation and/or winner */
1228     if (salvo)
1229     {
1230 	(void)refresh();
1231 	(void)sleep(1);
1232     }
1233 
1234 #ifdef DEBUG
1235     (void) mvprintw(PROMPTLINE + 2, 0,
1236 		    "New state %d, x=%d, y=%d, d=%d",
1237 		    next, x, y, d);
1238 #endif /* DEBUG */
1239     return(hit);
1240 }
1241 
1242 static
1243 int playagain(void)
1244 {
1245     int j;
1246     ship_t *ss;
1247 
1248     for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
1249 	for(j = 0; j < ss->length; j++)
1250 	{
1251 	    cgoto(ss->y + j * yincr[ss->dir], ss->x + j * xincr[ss->dir]);
1252 	    (void) addch((chtype)ss->symbol);
1253 	}
1254 
1255     if(awinna())
1256 	++cpuwon;
1257     else
1258 	++plywon;
1259     j = 18 + strlen(name);
1260 	/* If you play a hundred games or more at a go, you deserve a badly
1261 	 * centred score output.
1262 	 */
1263     if(plywon >= 10)
1264 	++j;
1265     if(cpuwon >= 10)
1266 	++j;
1267     (void) mvprintw(1,(COLWIDTH-j)/2,
1268 		    "%s: %d     Computer: %d",name,plywon,cpuwon);
1269 
1270     prompt(2, (awinna()) ? "Want to be humiliated again, %s [yn]? "
1271 	   : "Going to give me a chance for revenge, %s [yn]? ",name);
1272     return(sgetc("YN") == 'Y');
1273 }
1274 
1275 void usage()
1276 {
1277 	(void) fprintf(stderr, "usage: bs [-b | -s] [-c]\n");
1278 	(void) fprintf(stderr, "\tWhere the options are:\n");
1279 	(void) fprintf(stderr, "\t-b : play a blitz game\n");
1280 	(void) fprintf(stderr, "\t-s : play a salvo game\n");
1281 	(void) fprintf(stderr, "\t-c : ships may be adjacent\n");
1282 	exit(1);
1283 }
1284 
1285 static void do_options(int c, char *op[])
1286 {
1287     int ch;
1288 
1289     while ((ch = getopt(c, op, "bchs")) != -1) {
1290 	switch (ch) {
1291 	case 'b':
1292 	    blitz = 1;
1293 	    if (salvo == 1)
1294 	    {
1295 		(void) fprintf(stderr,
1296 			"Bad Arg: -b and -s are mutually exclusive\n");
1297 		exit(1);
1298 	    }
1299 	    break;
1300 	case 's':
1301 	    salvo = 1;
1302 	    if (blitz == 1)
1303 	    {
1304 		(void) fprintf(stderr,
1305 			"Bad Arg: -s and -b are mutually exclusive\n");
1306 		exit(1);
1307 	    }
1308 	    break;
1309 	case 'c':
1310 	    closepack = 1;
1311 	    break;
1312 	case 'h':
1313 	default:
1314 	    (void) usage();
1315 	    exit(1);
1316 	}
1317     }
1318     if (op[optind] != NULL)
1319 	(void) usage();
1320 }
1321 
1322 static int scount(int who)
1323 {
1324     int i, shots;
1325     ship_t *sp;
1326 
1327     if (who)
1328 	sp = cpuship;	/* count cpu shots */
1329     else
1330 	sp = plyship;	/* count player shots */
1331 
1332     for (i = 0, shots = 0; i < SHIPTYPES; i++, sp++)
1333     {
1334 	if (sp->hits >= sp->length)
1335 	    continue;		/* dead ship */
1336 	else
1337 	    shots++;
1338     }
1339     return(shots);
1340 }
1341 
1342 int main(int argc, char *argv[])
1343 {
1344     do_options(argc, argv);
1345 
1346     intro();
1347     do {
1348 	initgame();
1349 	while(awinna() == -1)
1350 	{
1351 	    if (!blitz)
1352 	    {
1353 		if (!salvo)
1354 		{
1355 	    	    if(turn)
1356 			(void) cputurn();
1357 		    else
1358 			(void) plyturn();
1359 		}
1360 		else  /* salvo */
1361 		{
1362 		    int i;
1363 
1364 		    i = scount(turn);
1365 		    while (i--)
1366 		    {
1367 			if (turn)
1368 			{
1369 			    if (cputurn() && awinna() != -1)
1370 				i = 0;
1371 			}
1372 			else
1373 			{
1374 			    if (plyturn() && awinna() != -1)
1375 				i = 0;
1376 			}
1377 		    }
1378 		}
1379 	    }
1380 	    else  /* blitz */
1381 	    	while(turn ? cputurn() : plyturn())
1382 		{
1383 		    if (turn)   /* Pause between successive computer shots */
1384 		    {
1385 			(void)refresh();
1386 			(void)sleep(1);
1387 		    }
1388 		    if (awinna() != -1)
1389 		     break;
1390 		}
1391 	    turn = OTHER;
1392 	}
1393     } while
1394 	(playagain());
1395     uninitgame(0);
1396     /*NOTREACHED*/
1397     exit(0);
1398 }
1399