bs.c 30 KB

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