📄 bs.c
字号:
/* * bs.c - original author: Bruce Holloway * salvo option by: Chuck A DeGaul * with improved user interface, autoconfiguration and code cleanup * by Eric S. Raymond <esr@snark.thyrsus.com> * v1.2 with color support and minor portability fixes, November 1990 * v2.0 featuring strict ANSI/POSIX conformance, November 1993. * v2.1 with ncurses mouse support, September 1995 *//* #define _POSIX_SOURCE -- incompatible with solaris termios.h */#include <signal.h>#include <ctype.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <assert.h>#include <time.h>#if HAVE_TERMIOS_H#include <sys/termios.h> /* required before solaris curses.h */#endif#include <curses.h>#ifndef SIGIOT#define SIGIOT SIGABRT#endif#ifndef A_UNDERLINE /* BSD curses */#define beep() write(1,"\007",1);#define cbreak crmode#define saveterm savetty#define resetterm resetty#define nocbreak nocrmode#define strchr index#endif /* !A_UNDERLINE */static int getcoord(int);/* * Constants for tuning the random-fire algorithm. It prefers moves that * diagonal-stripe the board with a stripe separation of srchstep. If * no such preferred moves are found, srchstep is decremented. */#define BEGINSTEP 3 /* initial value of srchstep *//* miscellaneous constants */#define SHIPTYPES 5#define OTHER (1-turn)#define PLAYER 0#define COMPUTER 1#define MARK_HIT 'H'#define MARK_MISS 'o'#define CTRLC '\003' /* used as terminate command */#define FF '\014' /* used as redraw command *//* coordinate handling */#define BWIDTH 10#define BDEPTH 10/* display symbols */#define SHOWHIT '*'#define SHOWSPLASH ' '#define IS_SHIP(c) isupper(c)/* how to position us on player board */#define PYBASE 3#define PXBASE 3#define PY(y) (PYBASE + (y))#define PX(x) (PXBASE + (x)*3)#define pgoto(y, x) (void)move(PY(y), PX(x))/* how to position us on cpu board */#define CYBASE 3#define CXBASE 48#define CY(y) (CYBASE + (y))#define CX(x) (CXBASE + (x)*3)#define CYINV(y) ((y) - CYBASE)#define CXINV(x) (((x) - CXBASE) / 3)#define cgoto(y, x) (void)move(CY(y), CX(x))#define ONBOARD(x, y) (x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH)/* other board locations */#define COLWIDTH 80#define PROMPTLINE 21 /* prompt line */#define SYBASE CYBASE + BDEPTH + 3 /* move key diagram */#define SXBASE 63#define MYBASE SYBASE - 1 /* diagram caption */#define MXBASE 64#define HYBASE SYBASE - 1 /* help area */#define HXBASE 0/* this will need to be changed if BWIDTH changes */static char numbers[] = " 0 1 2 3 4 5 6 7 8 9";static char carrier[] = "Aircraft Carrier";static char battle[] = "Battleship";static char sub[] = "Submarine";static char destroy[] = "Destroyer";static char ptboat[] = "PT Boat";static char name[40];static char dftname[] = "stranger";/* direction constants */#define E 0#define SE 1#define S 2#define SW 3#define W 4#define NW 5#define N 6#define NE 7static int xincr[8] = {1, 1, 0, -1, -1, -1, 0, 1};static int yincr[8] = {0, 1, 1, 1, 0, -1, -1, -1};/* current ship position and direction */static int curx = (BWIDTH / 2);static int cury = (BDEPTH / 2);typedef struct{ char *name; /* name of the ship type */ unsigned hits; /* how many times has this ship been hit? */ char symbol; /* symbol for game purposes */ char length; /* length of ship */ char x, y; /* coordinates of ship start point */ unsigned char dir; /* direction of `bow' */ bool placed; /* has it been placed on the board? */}ship_t;static bool checkplace(int b, ship_t *ss, int vis);ship_t plyship[SHIPTYPES] ={ { carrier, 0, 'A', 5}, { battle, 0, 'B', 4}, { destroy, 0, 'D', 3}, { sub, 0, 'S', 3}, { ptboat, 0, 'P', 2},};ship_t cpuship[SHIPTYPES] ={ { carrier, 0, 'A', 5}, { battle, 0, 'B', 4}, { destroy, 0, 'D', 3}, { sub, 0, 'S', 3}, { ptboat, 0, 'P', 2},};/* "Hits" board, and main board. */static char hits[2][BWIDTH][BDEPTH], board[2][BWIDTH][BDEPTH];static int turn; /* 0=player, 1=computer */static int plywon=0, cpuwon=0; /* How many games has each won? */static int salvo, blitz, closepack;#define PR (void)addstrstatic void uninitgame(int sig)/* end the game, either normally or due to signal */{ clear(); (void)refresh(); (void)resetterm(); (void)echo(); (void)endwin(); exit(sig);}static void announceopts(void)/* announce which game options are enabled */{ if (salvo || blitz || closepack) { (void) printw("Playing optional game ("); if (salvo) (void) printw("salvo, "); else (void) printw("nosalvo, "); if (blitz) (void) printw("blitz "); else (void) printw("noblitz, "); if (closepack) (void) printw("closepack)"); else (void) printw("noclosepack)"); } else (void) printw( "Playing standard game (noblitz, nosalvo, noclosepack)");}static void intro(void){ extern char *getlogin(void); char *tmpname; srand((unsigned)(time(0L)+getpid())); /* Kick the random number generator */ (void) signal(SIGINT,uninitgame); (void) signal(SIGINT,uninitgame); (void) signal(SIGIOT,uninitgame); /* for assert(3) */ if(signal(SIGQUIT,SIG_IGN) != SIG_IGN) (void)signal(SIGQUIT,uninitgame); if((tmpname = getlogin()) != 0) { (void)strcpy(name,tmpname); name[0] = toupper(name[0]); } else (void)strcpy(name,dftname); (void)initscr();#ifdef KEY_MIN keypad(stdscr, TRUE);#endif /* KEY_MIN */ (void)saveterm(); (void)nonl(); (void)cbreak(); (void)noecho();#ifdef PENGUIN (void)clear(); (void)mvaddstr(4,29,"Welcome to Battleship!"); (void)move(8,0); PR(" \\\n"); PR(" \\ \\ \\\n"); PR(" \\ \\ \\ \\ \\_____________\n"); PR(" \\ \\ \\_____________ \\ \\/ |\n"); PR(" \\ \\/ \\ \\/ |\n"); PR(" \\/ \\_____/ |__\n"); PR(" ________________/ |\n"); PR(" \\ S.S. Penguin |\n"); PR(" \\ /\n"); PR(" \\___________________________________________________/\n"); (void) mvaddstr(22,27,"Hit any key to continue..."); (void)refresh(); (void) getch();#endif /* PENGUIN */#ifdef A_COLOR start_color(); init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK); init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK); init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK); init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK); init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK); init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);#endif /* A_COLOR */#ifdef NCURSES_MOUSE_VERSION (void) mousemask(BUTTON1_CLICKED, (mmask_t *)NULL);#endif /* NCURSES_MOUSE_VERSION*/} /* VARARGS1 */static void prompt(int n, char *f, char *s)/* print a message at the prompt line */{ (void) move(PROMPTLINE + n, 0); (void) clrtoeol(); (void) printw(f, s); (void) refresh();}static void error(char *s){ (void) move(PROMPTLINE + 2, 0); (void) clrtoeol(); if (s) { (void) addstr(s); (void) beep(); }}static void placeship(int b, ship_t *ss, int vis){ int l; for(l = 0; l < ss->length; ++l) { int newx = ss->x + l * xincr[ss->dir]; int newy = ss->y + l * yincr[ss->dir]; board[b][newx][newy] = ss->symbol; if (vis) { pgoto(newy, newx); (void) addch((chtype)ss->symbol); } } ss->hits = 0;}static int rnd(int n){ return(((rand() & 0x7FFF) % n));}static void randomplace(int b, ship_t *ss)/* generate a valid random ship placement into px,py */{ register int bwidth = BWIDTH - ss->length; register int bdepth = BDEPTH - ss->length; do { ss->y = rnd(bdepth); ss->x = rnd(bwidth); ss->dir = rnd(2) ? E : S; } while (!checkplace(b, ss, FALSE));}static void initgame(void){ int i, j, unplaced; ship_t *ss; (void) clear(); (void) mvaddstr(0,35,"BATTLESHIPS"); (void) move(PROMPTLINE + 2, 0); announceopts(); memset(board, 0, sizeof(char) * BWIDTH * BDEPTH * 2); memset(hits, 0, sizeof(char) * BWIDTH * BDEPTH * 2); for (i = 0; i < SHIPTYPES; i++) { ss = cpuship + i; ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0; ss = plyship + i; ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0; } /* draw empty boards */ (void) mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board"); (void) mvaddstr(PYBASE - 1, PXBASE - 3,numbers); for(i=0; i < BDEPTH; ++i) { (void) mvaddch(PYBASE + i, PXBASE - 3, (chtype)(i + 'A'));#ifdef A_COLOR if (has_colors()) attron(COLOR_PAIR(COLOR_BLUE));#endif /* A_COLOR */ (void) addch(' '); for (j = 0; j < BWIDTH; j++) (void) addstr(" . ");#ifdef A_COLOR attrset(0);#endif /* A_COLOR */ (void) addch(' '); (void) addch((chtype)(i + 'A')); } (void) mvaddstr(PYBASE + BDEPTH, PXBASE - 3,numbers); (void) mvaddstr(CYBASE - 2, CXBASE + 7,"Hit/Miss Board"); (void) mvaddstr(CYBASE - 1, CXBASE - 3, numbers); for(i=0; i < BDEPTH; ++i) { (void) mvaddch(CYBASE + i, CXBASE - 3, (chtype)(i + 'A'));#ifdef A_COLOR if (has_colors()) attron(COLOR_PAIR(COLOR_BLUE));#endif /* A_COLOR */ (void) addch(' '); for (j = 0; j < BWIDTH; j++) (void) addstr(" . ");#ifdef A_COLOR attrset(0);#endif /* A_COLOR */ (void) addch(' '); (void) addch((chtype)(i + 'A')); } (void) mvaddstr(CYBASE + BDEPTH,CXBASE - 3,numbers); (void) mvprintw(HYBASE, HXBASE, "To position your ships: move the cursor to a spot, then"); (void) mvprintw(HYBASE+1,HXBASE, "type the first letter of a ship type to select it, then"); (void) mvprintw(HYBASE+2,HXBASE, "type a direction ([hjkl] or [4862]), indicating how the"); (void) mvprintw(HYBASE+3,HXBASE, "ship should be pointed. You may also type a ship letter"); (void) mvprintw(HYBASE+4,HXBASE, "followed by `r' to position it randomly, or type `R' to"); (void) mvprintw(HYBASE+5,HXBASE, "place all remaining ships randomly."); (void) mvaddstr(MYBASE, MXBASE, "Aiming keys:"); (void) mvaddstr(SYBASE, SXBASE, "y k u 7 8 9"); (void) mvaddstr(SYBASE+1, SXBASE, " \\|/ \\|/ "); (void) mvaddstr(SYBASE+2, SXBASE, "h-+-l 4-+-6"); (void) mvaddstr(SYBASE+3, SXBASE, " /|\\ /|\\ "); (void) mvaddstr(SYBASE+4, SXBASE, "b j n 1 2 3"); /* have the computer place ships */ for(ss = cpuship; ss < cpuship + SHIPTYPES; ss++) { randomplace(COMPUTER, ss); placeship(COMPUTER, ss, FALSE); } ss = (ship_t *)NULL; do { char c, docked[SHIPTYPES + 2], *cp = docked; /* figure which ships still wait to be placed */ *cp++ = 'R'; for (i = 0; i < SHIPTYPES; i++) if (!plyship[i].placed) *cp++ = plyship[i].symbol; *cp = '\0'; /* get a command letter */ prompt(1, "Type one of [%s] to pick a ship.", docked+1); do { c = getcoord(PLAYER); } while (!strchr(docked, c)); if (c == 'R') (void) ungetch('R'); else { /* map that into the corresponding symbol */ for (ss = plyship; ss < plyship + SHIPTYPES; ss++) if (ss->symbol == c) break; prompt(1, "Type one of [hjklrR] to place your %s.", ss->name); pgoto(cury, curx); } do { c = getch(); } while (!strchr("hjklrR", c) || c == FF); if (c == FF) { (void)clearok(stdscr, TRUE); (void)refresh(); } else if (c == 'r') { prompt(1, "Random-placing your %s", ss->name); randomplace(PLAYER, ss); placeship(PLAYER, ss, TRUE); error((char *)NULL); ss->placed = TRUE; } else if (c == 'R') { prompt(1, "Placing the rest of your fleet at random...", ""); for (ss = plyship; ss < plyship + SHIPTYPES; ss++) if (!ss->placed) { randomplace(PLAYER, ss); placeship(PLAYER, ss, TRUE); ss->placed = TRUE; } error((char *)NULL); } else if (strchr("hjkl8462", c)) { ss->x = curx; ss->y = cury; switch(c) { case 'k': case '8': ss->dir = N; break; case 'j': case '2': ss->dir = S; break; case 'h': case '4': ss->dir = W; break; case 'l': case '6': ss->dir = E; break; } if (checkplace(PLAYER, ss, TRUE)) { placeship(PLAYER, ss, TRUE); error((char *)NULL); ss->placed = TRUE; } } for (unplaced = i = 0; i < SHIPTYPES; i++) unplaced += !plyship[i].placed; } while (unplaced); turn = rnd(2); (void) mvprintw(HYBASE, HXBASE, "To fire, move the cursor to your chosen aiming point "); (void) mvprintw(HYBASE+1, HXBASE, "and strike any key other than a motion key. "); (void) mvprintw(HYBASE+2, HXBASE, " "); (void) mvprintw(HYBASE+3, HXBASE, " "); (void) mvprintw(HYBASE+4, HXBASE, " "); (void) mvprintw(HYBASE+5, HXBASE, " "); (void) prompt(0, "Press any key to start...", ""); (void) getch();}static int getcoord(int atcpu){ int ny, nx, c; if (atcpu) cgoto(cury,curx); else pgoto(cury, curx); (void)refresh(); for (;;) { if (atcpu) { (void) mvprintw(CYBASE + BDEPTH+1, CXBASE+11, "(%d, %c)", curx, 'A'+cury); cgoto(cury, curx); } else { (void) mvprintw(PYBASE + BDEPTH+1, PXBASE+11, "(%d, %c)", curx, 'A'+cury); pgoto(cury, curx); } switch(c = getch()) { case 'k': case '8':#ifdef KEY_MIN case KEY_UP:#endif /* KEY_MIN */ ny = cury+BDEPTH-1; nx = curx; break; case 'j': case '2':#ifdef KEY_MIN case KEY_DOWN:#endif /* KEY_MIN */ ny = cury+1; nx = curx; break; case 'h': case '4':#ifdef KEY_MIN case KEY_LEFT:#endif /* KEY_MIN */ ny = cury; nx = curx+BWIDTH-1; break; case 'l': case '6':#ifdef KEY_MIN case KEY_RIGHT:#endif /* KEY_MIN */ ny = cury; nx = curx+1; break; case 'y': case '7':#ifdef KEY_MIN case KEY_A1:#endif /* KEY_MIN */ ny = cury+BDEPTH-1; nx = curx+BWIDTH-1; break; case 'b': case '1':#ifdef KEY_MIN case KEY_C1:#endif /* KEY_MIN */ ny = cury+1; nx = curx+BWIDTH-1; break; case 'u': case '9':#ifdef KEY_MIN case KEY_A3:#endif /* KEY_MIN */ ny = cury+BDEPTH-1; nx = curx+1; break; case 'n': case '3':#ifdef KEY_MIN case KEY_C3:#endif /* KEY_MIN */ ny = cury+1; nx = curx+1; break; case FF: nx = curx; ny = cury; (void)clearok(stdscr, TRUE); (void)refresh(); break;#ifdef NCURSES_MOUSE_VERSION case KEY_MOUSE: { MEVENT myevent; getmouse(&myevent); if (atcpu && myevent.y >= CY(0) && myevent.y <= CY(BDEPTH) && myevent.x >= CX(0) && myevent.x <= CX(BDEPTH)) { curx = CXINV(myevent.x); cury = CYINV(myevent.y); return(' '); } else { beep(); continue; } } /* no fall through */#endif /* NCURSES_MOUSE_VERSION */ default: if (atcpu) (void) mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, " "); else (void) mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, " "); return(c); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -