📄 landmine.c
字号:
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(void){ 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, row * xp - 1, size * yp - 1); } for (row = 0; row < FULLSIZE; row++) { for (col = 0; col < FULLSIZE; col++) { drawcell(boardpos(row, col)); } }}/* * Draw a cell on the board. */static voiddrawcell(POS pos){ GR_COORD x; GR_COORD y; GR_SIZE chwidth; GR_SIZE chheight; GR_SIZE chbase; CELL cell; GR_CHAR ch; cell = board[pos]; if (!isknown(cell)) return; ch = displaychar(cell); if (ch == F_WRONG) { drawbomb(pos, greengc, GR_FALSE); return; } if (isold(cell)) { clearcell(pos); cellcenter(pos, &x, &y); GrGetGCTextSize(boardgc, &ch, 1, GR_TFASCII, &chwidth, &chheight, &chbase); GrText(boardwid, boardgc, x - chwidth / 2 + 1, y + chheight / 2, &ch, 1, GR_TFBOTTOM); return; } drawbomb(pos, redgc, GR_FALSE);}/* * Clear a particular cell. */static voidclearcell(POS pos){ GR_COORD row; GR_COORD col; row = pos / FULLSIZE; col = pos % FULLSIZE; GrFillRect(boardwid, cleargc, col * xp - xp, row * yp - yp, xp - 1, yp - 1);}/* * Draw a bomb in a window using the specified GC. * The bomb is animated and the terminal is beeped if necessary. */static voiddrawbomb(POS pos, GR_GC_ID gc, GR_BOOL animate) /*POS pos; position to draw bomb at */ /*GR_GC_ID gc; GC for drawing (red or green) */ /*GR_BOOL animate; TRUE to animate the bomb */{ GR_COORD x; GR_COORD y; GR_COUNT count; if (animate) write(1, "\007", 1); cellcenter(pos, &x, &y); count = (animate ? 8 : 1); for (;;) { GrFillEllipse(boardwid, gc, x, y, xp / 2 - 3, yp / 2 - 3); if (--count == 0) return; delay(); clearcell(pos); delay(); }}/* * Draw a button which has a specified label string centered in it. */static voiddrawbutton(GR_WINDOW_ID window, char *label){ GR_SIZE width; GR_SIZE height; GR_SIZE base; GrGetGCTextSize(buttongc, label, strlen(label), GR_TFASCII, &width, &height, &base); GrText(window, buttongc, (BUTTONWIDTH - width) / 2, (BUTTONHEIGHT - height) / 2 + height - 1, label, -1, GR_TFBOTTOM);}/* * Set the cursor as appropriate. * The cursor changes depending on the number of legs left. */static voidsetcursor(void){ GR_BITMAP *fgbits; /* bitmap for foreground */ GR_BITMAP *bgbits; /* bitmap for background */ switch (legs) { case 0: fgbits = noleg_fg; bgbits = noleg_bg; break; case 1: fgbits = oneleg_fg; bgbits = oneleg_bg; break; default: fgbits = twolegs_fg; bgbits = twolegs_bg; break; } GrSetCursor(boardwid, 9, 12, 4, 6, WHITE, BLACK, fgbits, bgbits);}/* * Delay for a while so that something can be seen. * This is done by drawing a large rectangle over the window using a mode * of XOR with the value of 0, (which does nothing except waste time). */static voiddelay(void){ GR_COUNT i; for (i = 0; i < 1; i++) { GrFillRect(boardwid, delaygc, 0, 0, xp * size - 1, yp * size - 1); GrFlush(); }}/* * Calculate the coordinates of the center of a cell on the board. * The coordinates are relative to the origin of the board window. */static voidcellcenter(POS pos, GR_COORD *retx, GR_COORD *rety){ *retx = (pos % FULLSIZE) * xp - 1 - xp / 2; *rety = (pos / FULLSIZE) * yp - 1 - yp / 2;}/* * Draw the status information in the status window. */static voiddrawstatus(void){ long score; long allsteps; long games; score = 0; games = games0[index]; allsteps = steps0[index]; score += games1[index]; games += games1[index]; allsteps += steps1[index]; score += games2[index] * 2; games += games2[index]; allsteps += steps2[index]; printline(0, "Size: %2d\n", size); printline(1, "Mines: %3d\n", mines); PRINTSTEPS; printline(3, "Legs: %d\n", legs); printline(5, "Won games: %3d\n", games2[index]); printline(6, "1-leg games:%3d\n", games1[index]); printline(7, "Lost games: %3d\n", games0[index]); if (games) { printline(9, "Legs/game: %3d.%03d\n", score / games, ((score * 1000) / games) % 1000); printline(10, "Steps/game:%3d.%03d\n", allsteps / games, ((allsteps * 1000) / games) % 1000); }}/* * Printf routine for windows, which can print at particular lines. * A negative line number means continue printing at the previous location. * Assumes the status window for output. */static void printline(GR_COORD row, char * fmt, ...){ va_list ap; GR_COUNT cc; GR_SIZE width; char *cp; char buf[256]; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); if (row >= 0) { charxpos = 0; charypos = charheight * row + charheight - 1; } cp = buf; for (;;) { cc = 0; width = 0; while (*cp >= ' ') { width += fi.widths[(int)*cp++]; cc++; } if (width) { GrText(statwid, statgc, charxpos, charypos, cp - cc, cc, GR_TFBOTTOM); charxpos += width; } switch (*cp++) { case '\0': return; case '\n': newline(); break; case '\r': charxpos = 0; break; } }}/* * Clear the remainder of the line and move to the next line. * This assumes output is in the status window. */static voidnewline(void){ GrFillRect(statwid, blackgc, charxpos, charypos - charheight + 1, statwidth - charxpos, charheight); charxpos = 0; charypos += charheight;}/* * Translate a board window coordinate into a cell position. * If the coordinate is outside of the window, or exactly on one * of the interior lines, then a coordinate of 0 is returned. */static POSfindcell(GR_COORD x, GR_COORD y){ GR_COORD row; GR_COORD col; if (((x % xp) == 0) || ((y % yp) == 0)) return 0; row = (y / yp) + 1; col = (x / xp) + 1; if ((row <= 0) || (row > size) || (col <= 0) || (col > size)) return 0; return boardpos(row, col);}/* * Initialize the board for playing */static voidnewgame(void){ GR_COORD row; GR_COORD col; GR_COUNT count; CELL cell; POS pos; for (row = 0; row < FULLSIZE; row++) { for (col = 0; col < FULLSIZE; col++) { cell = F_EMPTY; if (badsquare(row) || badsquare(col)) cell |= F_EDGE; board[boardpos(row, col)] = cell; } } playing = GR_TRUE; count = 0; legs = 2; steps = 0; drawstatus(); setcursor(); while (count < mines) { do { row = (rand() / 16) % (size * size + 1); } while (row == (size * size)); col = (row % size) + 1; row = (row / size) + 1; pos = boardpos(row, col); if ((pos == boardpos(1,1)) || (pos == boardpos(1,2)) || (pos == boardpos(2,1)) || (pos == boardpos(2,2)) || (pos == boardpos(size,size))) continue; if (!ismine(board[pos]) && checkpath(pos)) count++; } board[boardpos(1,1)] = (F_OLD | '0'); GrClearWindow(boardwid, GR_TRUE);}/* * Check to see if there is still a path from the top left corner to the * bottom right corner, if a new mine is placed at the indicated position. * Returns GR_TRUE if mine was successfully placed. */static GR_BOOLcheckpath(POS pos){ CELL *bp; /* current board position */ CELL *endbp; /* ending position */ POS endpos; /* ending position */ GR_COUNT count; /* number of neighbors */ GR_COUNT i; /* loop counter */ GR_BOOL more; /* GR_TRUE if new square reached */ /* * Begin by assuming there is a mine at the specified location, * and then count neighbors. If there are less than two other * mines or edge squares, then there must still be a path. */ board[pos] |= F_MINE; count = 0; for (i = 7; i >= 0; i--) { if (board[pos + steptable[i]] & (F_MINE | F_EDGE)) count++; } if (count < 2) return GR_TRUE; /* * Two or more neighbors, so we must do the full check. * First clear the reach flag, except for the top left corner. */ endpos = boardpos(size, size); bp = &board[endpos]; endbp = bp; while (bp != board) *bp-- &= ~F_REACH; board[boardpos(1,1)] |= F_REACH; /* * Now loop looking for new squares next to already reached squares. * Stop when no more changes are found, or when the lower right * corner is reached. */ do { more = GR_FALSE; for (bp = &board[boardpos(1,1)]; bp != endbp; bp++) { if (*bp & F_REACH) { for (i = 7; i >= 0; i--) { if ((bp[steptable[i]] & (F_MINE | F_REACH | F_EDGE)) == 0) { bp[steptable[i]] |= F_REACH; more = GR_TRUE; } } } } if (board[endpos] & F_REACH) return GR_TRUE; } while (more); /* * Cannot reach the lower right corner, so remove the mine and fail. */ board[pos] &= ~F_MINE; return GR_FALSE;}/* * Move to a particular position and see if we hit a mine. * If not, then count the number of mines adjacent to us so it can be seen. * If we are stepping onto a location where we remembered a mine is at, * then don't do it. Moving is only allowed to old locations, or to * locations adjacent to old ones. */static voidmovetopos(POS newpos){ POS fixpos; /* position to fix up */ CELL cell; /* current cell */ GR_COUNT count; /* count of cells */ GR_COUNT i; /* index for neighbors */ if ((newpos < 0) || (newpos >= (FULLSIZE * FULLSIZE)) || !playing) return; cell = board[newpos]; if (isedge(cell) || (isseen(cell)) || isold(cell)) return; count = isold(cell); for (i = 0; i < 8; i++) if (isold(board[newpos + steptable[i]])) count++; if (count <= 0) return; cell = (cell & F_FLAGS) | F_OLD; steps++; PRINTSTEPS; if (ismine(cell)) { /* we hit a mine */ legs--; board[newpos] = (F_REMEMBER | F_MINE); cell = (F_EMPTY | F_OLD); board[newpos] = cell; drawbomb(newpos, redgc, GR_TRUE); clearcell(newpos); setcursor(); for (i = 0; i < 8; i++) { fixpos = newpos + steptable[i]; if (isold(board[fixpos])) { board[fixpos]--; drawcell(fixpos); } } drawstatus(); } count = 0; for (i = 0; i < 8; i++) if (ismine(board[newpos + steptable[i]])) count++; board[newpos] = cell | (count + '0'); drawcell(newpos); if ((legs <= 0) || (newpos == boardpos(size,size))) gameover();}/* * Remember or forget the location of a mine. * This is for informational purposes only and does not affect anything. */static voidtogglecell(POS pos){ CELL cell; if ((pos <= 0) || !playing) return; cell = board[pos]; if (isknown(cell)) { if (!isseen(cell)) return; board[pos] = (board[pos] & F_FLAGS) | F_EMPTY; clearcell(pos); return; } board[pos] = (board[pos] & F_FLAGS) | F_REMEMBER; drawcell(pos);}/* * Here when the game is over. * Show where the mines are, and give the results. */static voidgameover(void){ POS pos; CELL cell; playing = GR_FALSE; switch (legs) { case 0: games0[index]++; steps0[index] += steps; break; case 1: games1[index]++; steps1[index] += steps; break; case 2: games2[index]++; steps2[index] += steps; break; } for (pos = 0; pos < (FULLSIZE * FULLSIZE); pos++) { cell = board[pos]; if (isseen(cell)) cell = (cell & F_FLAGS) | F_WRONG; if (ismine(cell)) cell = (cell & F_FLAGS) | F_REMEMBER; board[pos] = cell; } drawboard(); drawstatus();}/* * Search the game parameter table for the current board size and * number of mines, and set the index for those parameters so that * the statistics can be accessed. Allocates a new index if necessary. */static voidfindindex(void){ for (index = 0; index < MAXPARAMS; index++) { if ((sizeparam[index] == size) && (mineparam[index] == mines)) return; } for (index = 0; index < MAXPARAMS; index++) { if (sizeparam[index] == 0) { sizeparam[index] = size; mineparam[index] = mines; return; } } fprintf(stderr, "Too many parameters in save file\n"); exit(1);}/* * Read in a saved game if available, otherwise start from scratch. * Exits if an error is encountered. */static voidreadgame(char *name){ int fd; fd = -1; if (name) fd = open(name, 0); if (fd < 0) { magic = MAGIC; size = SIZE; mines = (size * size * MINEPERCENT) / 100; playing = GR_FALSE; return; } if (read(fd, &st, sizeof(st)) != sizeof(st)) magic = 0; close(fd); if ((magic != MAGIC) || (size > MAXSIZE)) { fprintf(stderr, "Save file format is incorrect\n"); exit(1); }}/* * Write the current game to a file. * Returns nonzero on an error. */static GR_BOOLwritegame(char *name){ int fd; if (name == NULL) return GR_TRUE; fd = creat(name, 0666); if (fd < 0) return GR_TRUE; if (write(fd, &st, sizeof(st)) != sizeof(st)) { close(fd); return GR_TRUE; } close(fd); return GR_FALSE;}/* END CODE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -