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