minesweeper.c

来自「开发linux应用-用gtk+和gdk开发linux图形用户界面应用--的实例」· C语言 代码 · 共 849 行 · 第 1/2 页

C
849
字号
/* * File: Minesweeper.c * Auth: Eric Harlow * * Similar to the windows minesweeper game. */#include <gtk/gtk.h>#include <stdlib.h>#include <time.h>#include "misc.h"#include "bitmaps.h"#include "digits.h"void SetStartButtonIcon (gchar **xpm_data);/* * --- Want the buttons to be a particular size. */#define BUTTON_WIDTH 18#define BUTTON_HEIGHT 19/*  * --- Each of the digits showing the number of bombs *     has a colorful bitmap showing the count.  This *     array make the looking up of the digits happen *     very quickly. */gpointer digits[] = {     NULL,     xpm_one,     xpm_two,    xpm_three,    xpm_four,    xpm_five,    xpm_six,    xpm_seven,    xpm_eight};/* * --- These are the available button states.   * * Unknown - Empty.  Don't know what it could be. * Flagged - User has put a flag on it, suspecting *           there's a bomb underneath. * Question - not implemented. * Down - Button has been pressed. */enum {    BUTTON_UNKNOWN,    BUTTON_FLAGGED,    BUTTON_QUESTION,    BUTTON_DOWN};/* * --- data structure to keep track of the minesweeper buttons. */typedef struct {    int       buttonState;  /* --- Column to place the button --- */    GtkWidget *widget;      /* --- Handle to the button --- */    int       nBombsNearby; /* --- How many are near this cell? --- */    int       bHasBomb;     /* --- Does it have a bomb? --- */    int       nRow;         /* --- Row of button --- */    int       nCol;         /* --- Column of button --- */} typMinesweeperButton;/*  * --- Default values for the size of the grid  *     and number of bombs. */static int nRows = 10;static int nCols = 10;static int nTotalBombs = 10;/* * --- Use a 2-d array for the grid.  This is just *     the maximum size.  */#define MAX_ROWS 35#define MAX_COLS 35/* * Globals. *//* --- Just allocate the MAX for now.  This really should be dynamic,  *     but using a two dimensional array is easy when dealing with a grid. */typMinesweeperButton mines[MAX_COLS][MAX_ROWS];/* --- Flags for the game --- */int bGameOver = FALSE;            /* --- Game is over? --- */int bResetGame = FALSE;           /* --- Game is over? --- */int nBombsLeft;                   /* --- Undiscovered bombs --- */GtkWidget *table = NULL;          /* --- Table with the grid in it. --- */GtkWidget *button_start;          /* --- Start button --- */GtkWidget *label_bombs;           /* --- Label showing # of bombs left --- */GtkWidget *label_time;            /* --- Label showing # of bombs left --- */GtkWidget *vbox;                  /* --- main window's vbox --- *//* * --- Prototypes. */void DisplayHiddenInfo (typMinesweeperButton *mine);void CreateMinesweeperButtons (GtkWidget *table, int c, int r, int flag);void FreeChildCallback (GtkWidget *widget);/* * CheckForWin * * Check to see if the game has been won.  Game has been won when the  * number of unclicked squares equals the number of total bombs.  */void CheckForWin (){    int i, j;    int nMines = 0;    /* --- Run through all the squares --- */    for (i = 0; i <  nCols; i++) {        for (j = 0; j < nRows; j++) {            /* --- If square is unclicked or has a flag on it... --- */            if (mines[i][j].buttonState == BUTTON_UNKNOWN ||                mines[i][j].buttonState == BUTTON_FLAGGED) {                /* --- Could be a bomb. --- */                nMines ++;            }        }    }    /* --- As many bombs as there are squares left? --- */    if (nMines == nTotalBombs) {        /* --- Stop the game.  We have a winner. --- */        StopTimer ();        SetStartButtonIcon ((gchar **) xpm_winner);        bGameOver = TRUE;    }}/* * AddImageToMine * * Change the image on the button to be that of the xpm. * * mine - square on the grid * xpm - image data */void AddImageToMine (typMinesweeperButton *mine, char *xpm[]){    GtkWidget *widget;    /* --- Create a widget from the xpm file --- */    widget = CreateWidgetFromXpm (table, (gchar **) xpm);    /* --- Put the bitmap in the button --- */    gtk_container_add (GTK_CONTAINER (mine->widget), widget);    /* --- deref image so when button is destroyed, image is destroyed. --- */    gdk_pixmap_unref ((GdkPixmap *) widget);}/* * UpdateSeconds * * Refresh the seconds display in the toolbar. * * nSeconds - how many seconds to display. */void UpdateSeconds (int nSeconds){    char buffer[44];    /* --- Change the label to show new time --- */    sprintf (buffer, "Time: %d", nSeconds);    gtk_label_set (GTK_LABEL (label_time), buffer);}/* * DisplayBombCount * * Show number of bombs remaining. */void DisplayBombCount (){    char buffer[33];    /* --- You have XX bombs left --- */    sprintf (buffer, "Bombs: %d", nBombsLeft);    gtk_label_set (GTK_LABEL (label_bombs), buffer);}/* * FreeChild * * Free all the children of the widget * This is called when the button has to display a new image.  * The old image is destroyed here. */void FreeChild (GtkWidget *widget) {    /* --- Free button children --- */    gtk_container_foreach (               GTK_CONTAINER (widget),                (GtkCallback) FreeChildCallback,               NULL);}/* * delete_event * * The window is closing down, end the gtk loop * */void delete_event (GtkWidget *widget, gpointer *data){    gtk_main_quit ();}/* * ShowBombs * * They clicked on a bomb, so now we have to show them were the * bombs really are.  (At least those they didn't find already.) * We display the bombs they didn't find as well as the bombs * they think they found that were not bombs. */ void ShowBombs (typMinesweeperButton *minefound){    int i, j;    typMinesweeperButton *mine;     GtkWidget *widget_x;    /* --- Run through all the squares --- */    for (i = 0; i <  nCols; i++) {        for (j = 0; j < nRows; j++) {            /* --- Get the datastructure --- */            mine = &mines[i][j];            /* --- If there's a button here and there's a bomb --- */            /* --- underneath --- */            if (mine->buttonState == BUTTON_UNKNOWN &&                mine->bHasBomb) {                /* --- Display the bomb. --- */                DisplayHiddenInfo (mine);            /* --- If they marked it as a bomb and there is               *     no bomb here... --- */            } else if (mine->buttonState == BUTTON_FLAGGED &&                       !mine->bHasBomb) {                /* --- Free the flag --- */                FreeChild (mine->widget);                /* --- Show the X at the location --- */                AddImageToMine (mine, xpm_bigx);            }        }    }}/* * OpenNearbySquares * * Open up all squares around this square. * * col, row - position to open up the square  * Open all squares near this one - X represents * the current square. * *      |---|---|---- *      |   |   |   | *      ------------- *      |   | X |   | *      ------------- *      |   |   |   | *      |---|---|---- */void OpenNearbySquares (int col, int row){    int i, j;    /* --- Look one column back and one column ahead --- */    for (i = MAX (col-1, 0); i <= MIN (col+1, nCols-1); i++) {        /* --- Check one row back and one row ahead --- */        for (j = MAX (row-1, 0); j <= MIN (row+1, nRows-1); j++) {            /* --- Display what's underneath --- */            DisplayHiddenInfo (&mines[i][j]);        }    }}/* * DisplayHiddenInfo * * Display what's hidden underneath the button. * Could be a bomb -  * Could be a square with a count of the bombs *  nearby. * Could be an empty square */void DisplayHiddenInfo (typMinesweeperButton *mine){    char      buffer[88];    GtkWidget *widget;    /* --- If it's already down, just return --- */    if (mine->buttonState == BUTTON_DOWN) {        gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (mine->widget), TRUE);        return;    }    /* --- If the button is flagged, don't fix it for them --- */    if (mine->buttonState == BUTTON_FLAGGED) {        /* --- They said there's a bomb here - so don't           *     open it up even if logically, there          *     can't be a bomb.                             */        gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (mine->widget), FALSE);    } else {        /* --- Put the button in the "down" state --- */        mine->buttonState = BUTTON_DOWN;        gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (mine->widget), TRUE);        /* --- If there's a bomb at the location --- */        if (mine->bHasBomb) {            /* --- Show the bomb at the location --- */            AddImageToMine (mine, xpm_bomb);        /* --- No bombs, but there are bombs near this one --- */        } else if (mine->nBombsNearby) {            /* --- Show the count of nearby bombs. --- */            AddImageToMine (mine, digits[mine->nBombsNearby]);        } else {            /* --- Hmm.  Clicked here, but no bombs and no count. --- */            /*     Open up all squares near here - may cascade. --- */            OpenNearbySquares (mine->nCol, mine->nRow);        }    }}/* * ResetGame * * Reset the game so it can be replayed.  Reset bomb * count and create a nice empty field of bombs. */void ResetGame (int nGridColumns, int nGridRows, int nBombs, int bNewButtons){    /* --- Reset the number of bombs in grid  --- */    nTotalBombs = nBombs;    /* --- Reset the number of bombs undiscovered --- */    nBombsLeft = nBombs;    /* --- Create the Minesweeper buttons. --- */    CreateMinesweeperButtons (table, nGridColumns, nGridRows, bNewButtons);    /* --- Stop the timer. --- */    StopTimer ();    UpdateSeconds (0);    SetStartButtonIcon ((gchar **) xpm_smile);}/* * FreeChildCallback * * Free the widget. */void FreeChildCallback (GtkWidget *widget){    gtk_widget_destroy (widget);}/* * SetStartButtonIcon * * Set the start button to have the image based on  * the data passed in.  Usually, this is going to be  * either the happy face or the frown. */void SetStartButtonIcon (gchar **xpm_data){    GtkWidget *widget;    /* --- Create a widget from the xpm --- */    widget = CreateWidgetFromXpm (button_start, xpm_data);    /* --- Free any children the button has --- */    FreeChild (button_start);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?