📄 landmine.c
字号:
/* * Landmine, the game. * Written for mini-X by David I. Bell. */#include <stdio.h>#include <stdarg.h>#include <stdlib.h>#if UNIX#include <fcntl.h>#include <unistd.h>#include <time.h>#endif#include "nano-X.h"#define MINSIZE 3 /* minimum size of board */#define MAXSIZE 30 /* maximum size of board */#define SIZE 15 /* default size of playing board */#define MINEPERCENT 15 /* default percentage of mines */#define SAVEFILE "landmine.save" /* default save file name */#define MAGIC 649351261 /* magic number in save files */#define MAXPARAMS 1000 /* maximum different game parameters */#define FULLSIZE (MAXSIZE + 2) /* board size including borders */#define BOARDGAP 10 /* millimeter gap around board */#define RIGHTGAP 15 /* mm gap between board, right side */#define BUTTONGAP 20 /* mm gap between buttons */#define STATUSGAP 35 /* mm gap between buttons and status */#define BUTTONWIDTH 80 /* width of buttons (pixels) */#define BUTTONHEIGHT 25 /* height of buttons (pixels) */#define RIGHTSIDE 150 /* pixels to guarantee for right side */#define BOARDBORDER 2 /* border size around board *//* * Print the number of steps taken. * This is used twice, and is a macro to guarantee that * the two printouts match. */#define PRINTSTEPS printline(2, "Steps: %3d\n", steps)/* * Typedefs local to this program. */typedef unsigned short CELL; /* cell value */typedef int POS; /* cell position *//* * For defining bitmaps easily. */#define X ((unsigned) 1)#define _ ((unsigned) 0)#define BITS(a,b,c,d,e,f,g,h,i) \ (((((((((a*2+b)*2+c)*2+d)*2+e)*2+f)*2+g)*2+h)*2+i) << 7)static GR_BITMAP twolegs_fg[] = { /* two legs foreground */ BITS(_,_,_,_,_,_,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,_,X,_,_,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,X,_,X,_,X,_,X,_), BITS(_,X,_,X,X,X,_,X,_), BITS(_,_,_,X,_,X,_,_,_), BITS(_,_,_,X,_,X,_,_,_), BITS(_,_,X,X,_,X,X,_,_), BITS(_,_,_,_,_,_,_,_,_)};static GR_BITMAP twolegs_bg[] = { /* two legs background */ BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,X,X,X,X,X,X,X,_), BITS(X,X,X,X,X,X,X,X,X), BITS(X,X,X,X,_,X,X,X,X), BITS(X,X,X,X,X,X,X,X,X), BITS(X,X,X,X,X,X,X,X,X), BITS(_,X,X,X,X,X,X,X,_), BITS(_,X,X,X,X,X,X,X,_), BITS(_,X,X,X,X,X,X,X,_)};static GR_BITMAP oneleg_fg[] = { /* one leg foreground */ BITS(_,_,_,_,_,_,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,_,X,_,_,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,X,_,X,_,X,_,X,_), BITS(_,_,_,X,X,X,_,X,_), BITS(_,_,_,_,_,X,_,_,_), BITS(_,_,_,_,_,X,_,_,_), BITS(_,_,_,_,_,X,X,_,_), BITS(_,_,_,_,_,_,_,_,_),};static GR_BITMAP oneleg_bg[] = { /* one leg background */ BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,X,X,X,X,X,X,X,_), BITS(X,X,X,X,X,X,X,X,X), BITS(X,X,X,X,_,X,X,X,X), BITS(X,X,X,X,X,X,X,X,X), BITS(_,_,X,X,X,X,X,X,X), BITS(_,_,_,_,X,X,X,X,_), BITS(_,_,_,_,X,X,X,X,_), BITS(_,_,_,_,X,X,X,X,_)};static GR_BITMAP noleg_fg[] = { /* no legs foreground */ BITS(_,_,_,_,_,_,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,_,X,_,_,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,X,_,X,_,X,_,X,_), BITS(_,_,_,X,X,X,_,_,_), BITS(_,_,_,_,_,_,_,_,_), BITS(_,_,_,_,_,_,_,_,_), BITS(_,_,_,_,_,_,_,_,_), BITS(_,_,_,_,_,_,_,_,_),};static GR_BITMAP noleg_bg[] = { /* no legs background */ BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,X,X,X,X,X,_,_), BITS(_,X,X,X,X,X,X,X,_), BITS(X,X,X,X,X,X,X,X,X), BITS(X,X,X,X,_,X,X,X,X), BITS(X,X,X,X,X,X,X,X,X), BITS(_,_,X,X,X,X,X,_,_), BITS(_,_,_,_,_,_,_,_,_), BITS(_,_,_,_,_,_,_,_,_), BITS(_,_,_,_,_,_,_,_,_)};/* * Components of a cell. */#define F_EMPTY ' ' /* default value for empty square */#define F_REMEMBER '*' /* character to remember mine */#define F_WRONG 'X' /* character to remember wrong guess */#define F_DISPLAY 0xff /* character to be displayed here */#define F_MINE 0x100 /* TRUE if a mine is here */#define F_EDGE 0x200 /* TRUE if this is edge of the world */#define F_OLD 0x400 /* TRUE if been at this square before */#define F_REACH 0x800 /* TRUE if can reach this square */#define F_FLAGS 0xff00 /* all flags *//* * The status of the game. * This structure is read and written from the save file. */static struct status { /* status of games */ long s_magic; /* magic number */ short s_playing; /* TRUE if playing a game */ short s_size; /* current size of board */ short s_mines; /* current number of mines on board */ short s_legs; /* number of legs left */ short s_steps; /* number of steps taken this game */ short s_index; /* current game parameter index */ short s_sizeparam[MAXPARAMS]; /* table of size parameters */ short s_mineparam[MAXPARAMS]; /* table of mine parameters */ long s_games0[MAXPARAMS]; /* games finished with no legs */ long s_games1[MAXPARAMS]; /* games finished with one leg */ long s_games2[MAXPARAMS]; /* games finished with two legs */ long s_steps0[MAXPARAMS]; /* steps taken in no leg games */ long s_steps1[MAXPARAMS]; /* steps taken in one leg games */ long s_steps2[MAXPARAMS]; /* steps taken in two leg games */ CELL s_board[FULLSIZE*FULLSIZE]; /* board layout */} st;/* * Definitions to make structure references easy. */#define magic st.s_magic#define playing st.s_playing#define size st.s_size#define mines st.s_mines#define legs st.s_legs#define steps st.s_steps#define index st.s_index#define sizeparam st.s_sizeparam#define mineparam st.s_mineparam#define games0 st.s_games0#define games1 st.s_games1#define games2 st.s_games2#define steps0 st.s_steps0#define steps1 st.s_steps1#define steps2 st.s_steps2#define board st.s_board#define boardpos(row, col) (((row) * FULLSIZE) + (col))#define ismine(cell) ((cell) & F_MINE)#define isedge(cell) ((cell) & F_EDGE)#define isold(cell) ((cell) & F_OLD)#define isseen(cell) (((cell) & F_DISPLAY) == F_REMEMBER)#define isknown(cell) (((cell) & F_DISPLAY) != F_EMPTY)#define displaychar(cell) ((cell) & F_DISPLAY)#define badsquare(n) (((n) <= 0) || ((n) > size))/* * Offsets for accessing adjacent cells. */static POS steptable[8] = { FULLSIZE, -FULLSIZE, 1, -1, FULLSIZE-1, FULLSIZE+1, -FULLSIZE-1, -FULLSIZE+1};static GR_WINDOW_ID mainwid; /* main window id */static GR_WINDOW_ID boardwid; /* board window id */static GR_WINDOW_ID statwid; /* status display window id */static GR_WINDOW_ID quitwid; /* window id for quit button */static GR_WINDOW_ID savewid; /* window id for save button */static GR_WINDOW_ID newgamewid; /* window id for new game button */static GR_GC_ID boardgc; /* graphics context for board */static GR_GC_ID cleargc; /* GC for clearing cell of board */static GR_GC_ID redgc; /* GC for drawing red */static GR_GC_ID greengc; /* GC for drawing green */static GR_GC_ID blackgc; /* GC for drawing black */static GR_GC_ID delaygc; /* GC for delaying */static GR_GC_ID statgc; /* GC for status window */static GR_GC_ID buttongc; /* GC for drawing buttons */static GR_GC_ID xorgc; /* GC for inverting things */static GR_SIZE xp; /* pixels for x direction per square */static GR_SIZE yp; /* pixels for y direction per square */static GR_SIZE statwidth; /* width of window drawing text in */static GR_SIZE statheight; /* height of window drawing text in */static GR_SIZE charheight; /* height of characters */static GR_COORD charxpos; /* current X position for characters */static GR_COORD charypos; /* current Y position for characters */static GR_SCREEN_INFO si; /* window information */static GR_FONT_INFO fi; /* font information */static char *savefile; /* filename for saving game *//* * Procedures. */static void printline(GR_COORD, char *, ...);static void newline();static void delay();static void dokey();static void readevent();static void doexposure();static void dobutton();static void drawbomb();static void drawstatus();static void drawbutton();static void drawboard();static void drawcell();static void cellcenter();static void clearcell();static void newgame();static void moveto();static void setcursor();static void togglecell();static void gameover();static void readgame();static void findindex();static POS findcell();static GR_BOOL checkpath();static GR_BOOL writegame();intmain(argc,argv) int argc; char **argv;{ GR_COORD x; GR_COORD y; GR_SIZE width; GR_SIZE height; GR_COORD rightx; /* x coordinate for right half stuff */ GR_BOOL setsize; /* TRUE if size of board is set */ GR_BOOL setmines; /* TRUE if number of mines is set */ GR_SIZE newsize = 0; /* desired size of board */ GR_COUNT newmines = 0; /* desired number of mines */ setmines = GR_FALSE; setsize = GR_FALSE; argc--; argv++; while ((argc > 0) && (**argv == '-')) { switch (argv[0][1]) { case 'm': if (argc <= 0) { fprintf(stderr, "Missing mine count\n"); exit(1); } argc--; argv++; newmines = atoi(*argv); setmines = GR_TRUE; break; case 's': if (argc <= 0) { fprintf(stderr, "Missing size\n"); exit(1); } argc--; argv++; newsize = atoi(*argv); setsize = GR_TRUE; break; default: fprintf(stderr, "Unknown option \"-%c\"\n", argv[0][1]); exit(1); } argc--; argv++; } if (argc > 0) savefile = *argv; srand(time(0)); readgame(savefile); if (setsize) { if ((newsize < MINSIZE) || (newsize > MAXSIZE)) { fprintf(stderr, "Illegal board size\n"); exit(1); } if (newsize != size) { if (steps && playing) { fprintf(stderr, "Cannot change size while game is in progress\n"); exit(1); } playing = GR_FALSE; size = newsize; if (!playing) mines = (size * size * MINEPERCENT) / 100; } } if (setmines) { if ((newmines <= 0) || ((newmines > (size * size) / 2))) { fprintf(stderr, "Illegal number of mines\n"); exit(1); } if (newmines != mines) { if (steps && playing) { fprintf(stderr, "Cannot change mines while game is in progress\n"); exit(1); } playing = GR_FALSE; mines = newmines; } } findindex(); /* * Parameters of the game have been verified. * Now open the graphics and play the game. */ if (GrOpen() < 0) { fprintf(stderr, "Cannot open graphics\n"); exit(1); } GrGetScreenInfo(&si); GrGetFontInfo(0, &fi); charheight = fi.height; /* * Create the main window which will contain all the others. */ mainwid = GrNewWindow(GR_ROOT_WINDOW_ID, 0, 0, si.cols, si.rows, 0, BLACK, WHITE); /* * Create the board window which lies at the left side. * Make the board square, and as large as possible while still * leaving room to the right side for statistics and buttons. */ width = si.cols - RIGHTSIDE - (si.xdpcm * RIGHTGAP / 10) - BOARDBORDER * 2; height = (((long) width) * si.ydpcm) / si.xdpcm; if (height > si.rows /* - y * 2*/) { height = si.rows - BOARDBORDER * 2; width = (((long) height) * si.xdpcm) / si.ydpcm; } xp = width / size; yp = height / size; width = xp * size - 1; height = yp * size - 1; x = BOARDBORDER; y = (si.rows - height) / 2; rightx = x + width + (si.xdpcm * RIGHTGAP / 10); boardwid = GrNewWindow(mainwid, x, y, width, height, BOARDBORDER, BLUE, WHITE); /* * Create the buttons. */ x = rightx; y = (si.ydpcm * BOARDGAP / 10); quitwid = GrNewWindow(mainwid, x, y, BUTTONWIDTH, BUTTONHEIGHT, 1, RED, WHITE); y += (si.ydpcm * BUTTONGAP / 10); savewid = GrNewWindow(mainwid, x, y, BUTTONWIDTH, BUTTONHEIGHT, 1, GREEN, WHITE); y += (si.ydpcm * BUTTONGAP / 10); newgamewid = GrNewWindow(mainwid, x, y, BUTTONWIDTH, BUTTONHEIGHT, 1, GREEN, WHITE); /* * Create the statistics window. */ x = rightx; y += (si.ydpcm * STATUSGAP / 10); width = si.cols - x; height = si.rows - y; statwid = GrNewWindow(mainwid, x, y, width, height, 0, 0, 0); statwidth = width; statheight = height; /* * Create the GC for drawing the board. */ boardgc = GrNewGC(); cleargc = GrNewGC(); delaygc = GrNewGC(); redgc = GrNewGC(); greengc = GrNewGC(); statgc = GrNewGC(); blackgc = GrNewGC(); buttongc = GrNewGC(); xorgc = GrNewGC(); GrSetGCBackground(boardgc, BLUE); GrSetGCForeground(cleargc, BLUE); GrSetGCForeground(redgc, RED); GrSetGCForeground(greengc, GREEN); GrSetGCForeground(statgc, GRAY); GrSetGCForeground(delaygc, BLACK); GrSetGCForeground(blackgc, BLACK); GrSetGCMode(delaygc, GR_MODE_XOR); GrSetGCMode(xorgc, GR_MODE_XOR); GrSetGCUseBackground(boardgc, GR_FALSE); GrSetGCUseBackground(buttongc, GR_FALSE); GrSelectEvents(boardwid, GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_BUTTON_DOWN | GR_EVENT_MASK_KEY_DOWN); GrSelectEvents(statwid, GR_EVENT_MASK_EXPOSURE); GrSelectEvents(quitwid, GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_BUTTON_DOWN); GrSelectEvents(newgamewid, GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_BUTTON_DOWN); GrSelectEvents(savewid, GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_BUTTON_DOWN); setcursor(); GrMapWindow(mainwid); GrMapWindow(boardwid); GrMapWindow(statwid); GrMapWindow(quitwid); GrMapWindow(savewid); GrMapWindow(newgamewid); if (!playing) newgame(); while (GR_TRUE) readevent();}/* * Read the next event and handle it. */static voidreadevent(){ GR_EVENT event; GrGetNextEvent(&event); switch (event.type) { case GR_EVENT_TYPE_BUTTON_DOWN: dobutton(&event.button); break; case GR_EVENT_TYPE_EXPOSURE: doexposure(&event.exposure); break; case GR_EVENT_TYPE_KEY_DOWN: dokey(&event.keystroke); break; }}/* * Handle exposure events. */static voiddoexposure(ep) GR_EVENT_EXPOSURE *ep;{ if (ep->wid == boardwid) { drawboard(); return; } if (ep->wid == statwid) { drawstatus(); return; } if (ep->wid == quitwid) { drawbutton(quitwid, "QUIT"); return; } if (ep->wid == savewid) { drawbutton(savewid, "SAVE GAME"); return; } if (ep->wid == newgamewid) { drawbutton(newgamewid, "NEW GAME"); return; }}/* * Here when we get a button down event. */static voiddobutton(bp) GR_EVENT_BUTTON *bp;{ if (bp->wid == boardwid) { moveto(findcell(bp->x, bp->y)); return; } if (bp->wid == quitwid) { GrFillRect(quitwid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT); GrFlush(); if (savefile) writegame(savefile); GrClose(); exit(0); } if (bp->wid == savewid) { GrFillRect(savewid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT); GrFlush(); if (savefile == NULL) savefile = SAVEFILE; if (writegame(savefile)) write(1, "\007", 1); else delay(); GrFillRect(savewid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT); } if (bp->wid == newgamewid) { GrFillRect(newgamewid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT); GrFlush(); /*if (playing) write(1, "\007", 1); else {*/ newgame(); delay(); /*}*/ GrFillRect(newgamewid, xorgc, 0, 0, BUTTONWIDTH, BUTTONHEIGHT); }}/* * Here when we get a keypress in a window. */static voiddokey(kp) GR_EVENT_KEYSTROKE *kp;{ if ((kp->wid != boardwid) || !playing) return; switch (kp->ch) { case ' ': /* remember or forget mine */ togglecell(findcell(kp->x, kp->y)); break; }}/* * Redraw the board. */static voiddrawboard(){ GR_COORD row; GR_COORD col; for (row = 1; row < size; row++) { GrLine(boardwid, boardgc, 0, row * yp - 1, size * xp - 1, row * yp - 1); GrLine(boardwid, boardgc, row * xp - 1, 0,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -