bs.c 30 KB

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