#include <cstdlib>
#include <cstdio>
#include <ctime> //提供时间函数
#include <conio.h> //提供getch()
#include <windows.h>
#include <iostream>
#include "map.h"
#define GOTO(pos) gotoxy(2 * (pos.x) - 1, (pos.y) - 1) //定义用于移动光标的 宏
//由于一个方块占 2 个格子,所以 pos.x 每加 1,则光标要移动 2 格
using std::cout;
using std::cin;
using std::endl;
void gotoxy(int x, int y) { //移动光标的接口
COORD pos = { short(x), short(y) };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut, pos);
}
void Map::AcceptCond() { //接收游戏模式参数
cout << "Choose Mode" << endl;
cout << "1 : Beginner" << endl;
cout << "2 : Intermediate" << endl;
cout << "3 : Expert" << endl << "Mode: ";
char mode;
cin >> mode;
while (('1' != mode) && ('2' != mode) && ('3' != mode)) { //仅仅接受 1, 2, 3,其他字符跳过
cout << "Wrong Mode, Enter number again\n Mode: ";
cin >> mode;
}
switch (mode) {
case '1' : _lng = 8; _wid = 8; _mines = 10; break;
case '2' : _lng = 16; _wid = 16; _mines = 40; break;
default: _lng = 30; _wid = 16; _mines = 99;
}
_blanks = _lng * _wid - _mines; //计算空格数,用于判断是否赢,_blanks = 0 时判定赢
}
void Map::InitMap() { //初始化地图,显示的地图下标从 1 - wid, 1 - _lng, 边界外面还有空格,用于计算空格对应数字的,边界相当于0
for (int i = 0; i < _wid + 2; i++) {
for (int j = 0; j < _lng + 2; j++) {
data[i][j].n = 0;
data[i][j].flag = false;
}
}
}
void Map::SetMine() {
int i, j;
int m = _mines;
srand(time(NULL));
while (m)
{
i = rand() % _wid + 1;
j = rand() % _lng + 1;
if ((-1 != data[i][j].n) && (j != _pos.x && i != _pos.y)) { //后面的条件用于避免用户第一个打开的空格处布置地雷
data[i][j].n = -1;
m--;
}
}
}
void Map::SetNumber() {
for (int i = 1; i <= _wid; i++) {
for (int j = 1; j <= _lng; j++) { //依次检查周围的 8 个空格的雷数
if (-1 == data[i][j].n) continue;
if (-1 == data[i-1][j-1].n) data[i][j].n++;
if (-1 == data[i][j-1].n) data[i][j].n++;
if (-1 == data[i+1][j-1].n) data[i][j].n++;
if (-1 == data[i-1][j].n) data[i][j].n++;
if (-1 == data[i+1][j].n) data[i][j].n++;
if (-1 == data[i-1][j+1].n) data[i][j].n++;
if (-1 == data[i][j+1].n) data[i][j].n++;
if (-1 == data[i+1][j+1].n) data[i][j].n++;
}
}
}
void Map::SetPosition() {
GOTO(_pos);
}
void Map::ResetPosition() {
_pos.x = _pos.y = 1;
}
void Map::ShowMap() {
system("cls"); //清屏
system("color 03"); //调整控制台显示颜色
SetConsoleOutputCP(437); //使方块能够正常显示
for (int i = 1; i <= _wid; i++) {
cout << '|'; //左边界
for (int j = 1; j <= _lng; j++) {
if (data[i][j].flag) {
switch (data[i][j].n) {
case 0 : cout << " "; break; //由于方块占两个格子,因此其他的输出,如空格、数字等也要占2个格子,对齐
default: cout << data[i][j].n << ' ';
}
}
else printf("%c", 219);
}
cout << '|' << endl; //右边界
}
gotoxy(0, _wid+2); //在地图下方输出坐标信息和空格数
printf("Position : (%d, %d)\n Blanks : %d", _pos.x, _pos.y, _blanks);
GOTO(_pos); //归位到原先地图坐标对应的位置
}
void Map::ShowAll() { //类似上面的ShowMap(),但在游戏失败时调用
system("cls");
system("color 03");
SetConsoleOutputCP(437);
for (int i = 1; i <= _wid; i++) {
cout << '|';
for (int j = 1; j <= _lng; j++) {
switch (data[i][j].n) {
case 0 : printf("%c", 219); break;
case -1:
if (i == _pos.y && j == _pos.x)
cout << "X ";
else
cout << "* ";
break;
default: cout << data[i][j].n << ' ';
}
}
cout << '|' << endl;
}
}
#define SOLVE_IT(t) {stack[++top] = (t); data[(t).y][(t).x].flag = true; _blanks--;}
#define FALSE_FLAG(t) !data[(t).y][(t).x].flag
void Map::OpenBlock() { //用栈来将连着的空格区域遍历一遍,并将其 flag 值置为 true
if (data[_pos.y][_pos.x].flag) return; //如果已经打开过就不需要再次打开,否则 _blanks--; 会多次执行,无法判断赢
Position stack[_lng * _wid << 1];
Position t;
int top = 0;
stack[top] = _pos;
data[_pos.y][_pos.x].flag = true;
_blanks--;
while (top != -1) {
t = stack[top--];
if (0 == data[t.y][t.x].n) { //如果该位置为 0 ,那么它周围的格子都要打开
t.y--; //判断上方三个格子
if (t.y >= UP_EDGE) { //如果上方三个格子 y 不越界
if (FALSE_FLAG(t)) SOLVE_IT(t)
t.x--;
if (t.x >= LEFT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t)
t.x += 2;
if (t.x <= RIGHT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t)
t.x--;
}
t.y++; t.x--;//判断左右两个格子, 此时 t.y 复原
if (t.x >= LEFT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t)
t.x += 2;
if (t.x <= RIGHT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t)
t.y++; //下方三个格子, 此时 t.x 是最右边的格子
if (t.y <= DOWN_EDGE) { //如果下方三个格子 y 不越界, 与上面判断基本相同
if (t.x <= RIGHT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t)
t.x--;
if (FALSE_FLAG(t)) SOLVE_IT(t)
t.x--;
if (t.x >= LEFT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t)
}
}
}
}
void Map::FirstStep() { //函数结束后将改变 _pos,就是我们用的预先处理函数,防止第一步就触雷的
char op;
do {
op = getch();
while ((op != 'a') && (op != 's') && (op != 'd') && (op != 'w') && (op !=' '))
op = getch();
} while (Move(op));
}
bool Map::Move(char op) {
switch (op) { //通过不同的操作,改变坐标,然后再通过 GOTO宏 移动到该位置上
case ' ':
return false;
case 'w':
if (UP_EDGE != _pos.y) _pos.y--;
break;
case 'a':
if (LEFT_EDGE != _pos.x) _pos.x--;
break;
case 's':
if (DOWN_EDGE != _pos.y) _pos.y++;
break;
default:
if (RIGHT_EDGE != _pos.x) _pos.x++;
}
gotoxy(0, _wid + 2);
printf("Position : (%d, %d)\n Blanks : %d", _pos.x, _pos.y, _blanks);
GOTO(_pos);
return true;
}
bool Map::IfLose() {
return -1 == data[_pos.y][_pos.x].n;
}
bool Map::IfWin() {
return 0 == _blanks;
}
bool Map::PlayGame() {
char op;
float start, end;
while (!IfWin()) {
do {
op = getch();
while ((op != 'a') && (op != 's') && (op != 'd') && (op != 'w') && (op !=' '))
op = getch();
} while (Move(op));
if (IfLose()) { //触雷
ShowAll(); gotoxy (0, _wid + 3);
return false;
}
else {
OpenBlock(); //打开方块,实质上时将 flag 的值置为 true,接着 ShowMap()将可以显示该方块信息
ShowMap();
GOTO(_pos);
}
}
gotoxy(0, _wid + 3);
return true;
}
|