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