Logo Search packages:      
Sourcecode: vgagamespack version File versions

mines.c

/*
 * Mines
 *
 * Copyright (C) Evan Harris, 1994-1998.
 *
 * Permission is granted to freely redistribute and modify this code,
 * providing the author(s) get credit for having written it.
 */

#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <time.h>
#include <unistd.h>

#include "mines.h"


/*
 * The board height is bound by the screen height: 30 * 16 = 480.
 * The board width shouldn't be increased without checking BOARDWIDTH in
 * milinux.c.
 */
#define MINWIDTH 8
#define MAXWIDTH 32
#define MINHEIGHT 8
#define MAXHEIGHT 30
#define MINDENSITY 0.05
#define MAXDENSITY 0.5

#define MINERATIO 4

#define MINE 9
#define PROTECTFLAG (1 << 4)
#define PROTECTQFLAG (1 << 5)
#define CLEARFLAG (1 << 6)
#define QUERYFLAG (1 << 7)
#define NOFLAGS 0x0f


struct minesdata {
    char *b;
    int w, h;
    double d;
    int minesleft;
    int protected;
    int cleared;
};


void init_board(struct minesdata *md);
void free_board(struct minesdata *md);
void new_board(struct minesdata *md);
void protect(struct minesdata *md, int x, int y);
void display_bad_flags(struct minesdata *md, int no_x, int no_y);
int detonate(struct minesdata *md, int x, int y);
void clear_around(struct minesdata *md, int x, int y);
int check_around(struct minesdata *md, int x, int y, int flag);
void redraw_board(struct minesdata *md);


static struct option long_options[] = {
    { "density", 1, NULL, 'd' },
    { "height", 1, NULL, 'h' },
    { "width", 1, NULL, 'w' },
    { "beginner", 0, NULL, 'B' },
    { "intermediate", 0, NULL, 'I' },
    { "expert", 0, NULL, 'E' },
    { "xmines", 0, NULL, 'X' },
    { "help", 0, NULL, 'H' }
};


int
main(int argc, char **argv)
{
    struct minesdata md;
    int c, err = 0;
    int width = 20, height = 20;
    double density = 0.25;
    int packedcmd, cmd, x, y, end = 0;
    int gameover;
    int option_index;
    
    while ((c = getopt_long(argc, argv, "d:h:w:BIEXH",
                      long_options, &option_index)) != -1) {
      switch (c) {
        case 'd':
          density = atof(optarg);
          if (density < MINDENSITY || density > MAXDENSITY) {
            err++;
          }
          break;
        case 'h':
          height = atoi(optarg);
          if (height < MINHEIGHT || height > MAXHEIGHT) {
            err++;
          }
          break;
        case 'w':
          width = atoi(optarg);
          if (width < MINWIDTH || width > MAXWIDTH) {
            err++;
          }
          break;
        case 'B':
          density = 0.16;
          height = 8;
          width = 8;
          break;
        case 'I':
          density = 0.16;
          height = 16;
          width = 16;
          break;
        case 'E':
        case 'X':
          density = 0.207;
          height = 16;
          width = 30;
          break;
        case 'H':
        case '?':
          err++;
          break;
      }
    }
    if (err) {
      fprintf(stderr, "Usage: %s [-d F] [-h N] [-w N] [-B] [-I] [-E] [-X] [-H]\n",
            argv[0]);
      fprintf(stderr, "  -d F   --density=F      set mine density to F (%.2f-%.2f), default: 0.25\n",
            MINDENSITY, MAXDENSITY);
      fprintf(stderr, "  -h N   --height=N       set board height to N (%d-%d), default: 20\n",
            MINHEIGHT, MAXHEIGHT);
      fprintf(stderr, "  -w N   --width=N        set board width to N (%d-%d), default: 20\n",
            MINWIDTH, MAXWIDTH);
      fprintf(stderr, "  -B     --beginner       minesweeper beginner board: 8x8, density 0.16\n");
      fprintf(stderr, "  -I     --intermediate   minesweeper intermediate board: 16x16, density 0.16\n");
      fprintf(stderr, "  -E     --expert         minesweeper expert board: 30x16, density 0.207\n");
      fprintf(stderr, "  -X     --xmines         xmines board: 30x16, density 0.207\n");
      fprintf(stderr, "  -H     --help           print this help message\n");
      exit(EXIT_FAILURE);
    }

    md.w = width;
    md.h = height;
    md.d = density;

    init_board(&md);
    if (md.b == NULL) {
      fprintf(stderr, "Cannot get memory for board\n");
      exit(1);
    }
    InitDisplay(width, height);

    new_board(&md);
    gameover = CONT;
    NewGame(width, height);
    MinesLeft(md.minesleft);

    while (!end) {
      packedcmd = GetMove();
      cmd = packedcmd_cmd(packedcmd);
      x = packedcmd_x(packedcmd);
      y = packedcmd_y(packedcmd);
      switch (cmd) {
        case NEWGAME:
          new_board(&md);
          gameover = CONT;
          NewGame(width, height);
          MinesLeft(md.minesleft);
          break;
        case QUIT:
          end = 1;
          break;
        case PROTECT:
          if (!gameover) {
            protect(&md, x, y);
            MinesLeft(md.minesleft);
            if (md.protected + md.cleared == md.w * md.h) {
                gameover = WIN;
            }
            if (gameover) {
                GameOver(gameover);
            }
          }
          break;
        case DETONATE:
          if (!gameover) {
            gameover = detonate(&md, x, y);
            if (md.protected + md.cleared == md.w * md.h) {
                gameover = WIN;
            }
            if (gameover) {
                GameOver(gameover);
            }
          }
          break;
        case CHECK_AROUND:
          if (!gameover) {
            gameover = check_around(&md, x, y, 1);
            if (md.protected + md.cleared == md.w * md.h) {
                gameover = WIN;
            }
            if (gameover) {
                GameOver(gameover);
            }
          }
          break;
        case UNCHECK_AROUND:
          if (!gameover) {
            gameover = check_around(&md, x, y, 0);
            if (md.protected + md.cleared == md.w * md.h) {
                gameover = WIN;
            }
            if (gameover) {
                GameOver(gameover);
            }
          }
          break;
        default:
          fprintf(stderr, "Unknown command\n");
          end = 1;
          break;
      }
    }
    
    free_board(&md);
    EndDisplay();

    return EXIT_SUCCESS;
}


void
init_board(struct minesdata *md)
{
    srand48(time(NULL));
    
    md->b = malloc(md->w * md->h * sizeof(char));
}


void
free_board(struct minesdata *md)
{
    free(md->b);
}


void
new_board(struct minesdata *md)
{
    int i, j;
    int mines;

    mines = (int)(md->w * md->h * md->d);
    
    for (i = 0; i < md->w * md->h; i++) {
      md->b[i] = 0;
    }
    for (i = 0; i < mines; i++) {
      do {
          j = lrand48() % (md->w * md->h);
      } while (md->b[j] != 0);
      md->b[j] = MINE;
    }
    for (i = 0; i < md->w * md->h; i++) {
      if (md->b[i] != MINE) {
          j = 0;
          if (i / md->w > 0 && i % md->w > 0 && md->b[i - md->w - 1] == MINE)
            j++;
          if (i / md->w > 0 && md->b[i - md->w] == MINE)
            j++;
          if (i / md->w > 0 && i % md->w < md->w - 1
            && md->b[i - md->w + 1] == MINE)
            j++;
          if (i % md->w > 0 && md->b[i - 1] == MINE)
            j++;
          if (i % md->w < md->w - 1 && md->b[i + 1] == MINE)
            j++;
          if (i / md->w < md->h - 1 && i % md->w > 0
            && md->b[i + md->w - 1] == MINE)
            j++;
          if (i / md->w < md->h - 1 && md->b[i + md->w] == MINE)
            j++;
          if (i / md->w < md->h - 1 && i % md->w < md->w - 1
            && md->b[i + md->w + 1] == MINE)
            j++;
          md->b[i] = j;
      }
    }

    md->minesleft = mines;
    md->protected = 0;
    md->cleared = 0;
}


void
protect(struct minesdata *md, int x, int y)
{
    int i;

    i = y * md->w + x;
    if (md->b[i] & CLEARFLAG) {
      return;
    }
    if (md->b[i] & PROTECTFLAG) {
      md->b[i] = (md->b[i] & NOFLAGS) | PROTECTQFLAG;
      ShowSquare(x, y, SQ_Q);
      md->minesleft++;
      md->protected--;
      return;
    }
    if (md->b[i] & PROTECTQFLAG) {
      md->b[i] &= NOFLAGS;
      ShowSquare(x, y, SQ_COVERED);
      return;
    }
    md->b[i] = (md->b[i] & NOFLAGS) | PROTECTFLAG;
    ShowSquare(x, y, SQ_X);
    md->minesleft--;
    md->protected++;
}


void
clear_it(struct minesdata *md, int x, int y)
{
    int i = y * md->w + x;

    if (!(md->b[i] & (PROTECTFLAG | PROTECTQFLAG | CLEARFLAG))) {
      md->b[i] = (md->b[i] & NOFLAGS) | CLEARFLAG;
      md->cleared++;
      ShowSquare(x, y, md->b[i] & NOFLAGS);
      if ((md->b[i] & NOFLAGS) == 0) {
          clear_around(md, x, y);
      }
    }
}


void
clear_around(struct minesdata *md, int x, int y)
{
    if (y > 0 && x > 0) {
      clear_it(md, x - 1, y - 1);
    }
    if (y > 0) {
      clear_it(md, x, y - 1);
    }
    if (y > 0 && x < md->w - 1) {
      clear_it(md, x + 1, y - 1);
    }
    if (x > 0) {
      clear_it(md, x - 1, y);
    }
    if (x < md->w - 1) {
      clear_it(md, x + 1, y);
    }
    if (y < md->h - 1 && x > 0) {
      clear_it(md, x - 1, y + 1);
    }
    if (y < md->h - 1) {
      clear_it(md, x, y + 1);
    }
    if (y < md->h - 1 && x < md->w - 1) {
      clear_it(md, x + 1, y + 1);
    }
}


int
protected_around(struct minesdata *md, int x, int y)
{
    int i = 0;

    if (y > 0 && x > 0 && (md->b[(y - 1) * md->w + (x - 1)] & PROTECTFLAG))
      i++;
    if (y > 0 && (md->b[(y - 1) * md->w + x] & PROTECTFLAG))
      i++;
    if (y > 0 && x < md->w - 1
      && (md->b[(y - 1) * md->w + (x + 1)] & PROTECTFLAG))
      i++;
    if (x > 0 && (md->b[y * md->w + (x - 1)] & PROTECTFLAG))
      i++;
    if (x < md->w - 1 && (md->b[y * md->w + (x + 1)] & PROTECTFLAG))
      i++;
    if (y < md->h - 1 && x > 0
      && (md->b[(y + 1) * md->w + (x - 1)] & PROTECTFLAG))
      i++;
    if (y < md->h - 1 && (md->b[(y + 1) * md->w + x] & PROTECTFLAG))
      i++;
    if (y < md->h - 1 && x < md->w - 1
      && (md->b[(y + 1) * md->w + (x + 1)] & PROTECTFLAG))
      i++;
    return i;
}


int
detonate_it(struct minesdata *md, int x, int y)
{
    int i = y * md->w + x;

    if (md->b[i] & PROTECTFLAG &&
      (md->b[i] & NOFLAGS) != MINE) {
      ShowSquare(x, y, SQ_XFLAG);
      md->cleared++;
      return LOSE;
    }
    if (md->b[i] & (CLEARFLAG | PROTECTFLAG)) {
      return CONT;
    }
    md->b[i] = (md->b[i] & NOFLAGS) | CLEARFLAG;
    ShowSquare(x, y, md->b[i] & NOFLAGS);

    if ((md->b[i] & NOFLAGS) == 0) {
      clear_around(md, x, y);
    }

    if ((md->b[i] & NOFLAGS) == MINE) {
      return LOSE;
    } else {
      md->cleared++;
      return CONT;
    }
}


int
detonate_around(struct minesdata *md, int x, int y)
{
    int i = 0;

    if (y > 0 && x > 0) {
      i += detonate_it(md, x - 1, y - 1);
    }
    if (y > 0) {
      i += detonate_it(md, x, y - 1);
    }
    if (y > 0 && x < md->w - 1) {
      i += detonate_it(md, x + 1, y - 1);
    }
    if (x > 0) {
      i += detonate_it(md, x - 1, y);
    }
    if (x < md->w - 1) {
      i += detonate_it(md, x + 1, y);
    }
    if (y < md->h - 1 && x > 0) {
      i += detonate_it(md, x - 1, y + 1);
    }
    if (y < md->h - 1) {
      i += detonate_it(md, x, y + 1);
    }
    if (y < md->h - 1 && x < md->w - 1) {
      i += detonate_it(md, x + 1, y + 1);
    }
    return (i > 0) ? LOSE : CONT;
}


void
query_it(struct minesdata *md, int x, int y)
{
    int i = y * md->w + x;

    if (md->b[i] & (CLEARFLAG | PROTECTFLAG)) {
      return;
    }
    if (md->b[i] & QUERYFLAG) {
      md->b[i] = md->b[i] & (~QUERYFLAG);
      if (md->b[i] & PROTECTQFLAG) {
          ShowSquare(x, y, SQ_Q);
      } else {
          ShowSquare(x, y, SQ_COVERED);
      }
    } else {
      md->b[i] = md->b[i] | QUERYFLAG;
      ShowSquare(x, y, SQ_QUERY);
    }
}


void
query_around(struct minesdata *md, int x, int y)
{
    if (y > 0 && x > 0) {
      query_it(md, x - 1, y - 1);
    }
    if (y > 0) {
      query_it(md, x, y - 1);
    }
    if (y > 0 && x < md->w - 1) {
      query_it(md, x + 1, y - 1);
    }
    if (x > 0) {
      query_it(md, x - 1, y);
    }
    if (x < md->w - 1) {
      query_it(md, x + 1, y);
    }
    if (y < md->h - 1 && x > 0) {
      query_it(md, x - 1, y + 1);
    }
    if (y < md->h - 1) {
      query_it(md, x, y + 1);
    }
    if (y < md->h - 1 && x < md->w - 1) {
      query_it(md, x + 1, y + 1);
    }
}


void
display_bad_flags(struct minesdata *md, int no_x, int no_y)
{
    int x, y, i;
    
    for (y = 0; y < md->h; y++) {
      for (x = 0; x < md->w; x++) {
          if (x != no_x || y != no_y) {
            i = y * md->w + x;
            if ((md->b[i] & PROTECTFLAG) &&
                ((md->b[i] & NOFLAGS) != MINE)) {
                ShowSquare(x, y, SQ_XFLAG);
            } else if (!(md->b[i] & PROTECTFLAG) &&
                     ((md->b[i] & NOFLAGS) == MINE)) {
                ShowSquare(x, y, SQ_GMINE);
            }
          }
      }
    }
}


int
detonate(struct minesdata *md, int x, int y)
{
    int i, ret;

    i = y * md->w + x;
    if (md->b[i] & PROTECTFLAG) {
      return CONT;
    }

    ret = detonate_it(md, x, y);
    if (ret == LOSE) {
      ShowSquare(x, y, SQ_XMINE);
      display_bad_flags(md, x, y);
    }

    return ret;
}


int
check_around(struct minesdata *md, int x, int y, int flag)
{
    int i, ret;

    i = y * md->w + x;
    if (md->b[i] & CLEARFLAG) {
      if (protected_around(md, x, y) >= (md->b[i] & NOFLAGS)) {
          if (flag == 1) {
            ret = detonate_around(md, x, y);
            if (ret == LOSE) {
                display_bad_flags(md, -1, -1);
            }
            return ret;
          } 
      } else {
          query_around(md, x, y);
      }
    }
    return CONT;
}

Generated by  Doxygen 1.6.0   Back to index