C++命令行扫雷(移动光标+存档+货币+道具)

包含但不仅限于:

  标记地点和取消

  可随时返回的主界面

  起手生成地图自动避雷

  难度选择和自定义地图

  检测键盘移动光标(毕竟不能直接检测鼠标)

  雷数和标记数显示

  颜色的设置等等 (虽然就只是扫雷全部该有的hhhhh)

 PS:现在还加入了商店和复活币,还有存档

源码如下,可以直接编译运行

本人大一学生,基础差,好多地方写的烂,尽管骂

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <windows.h>
#include <fstream>
#include <iomanip>

using namespace std;

//版本号
double version_num = 3.0;

//全局变量cdt
int cdt = 0;
//金币数
int goldcoin = 0;
//道具数
int tool_num = 0;

//标记数
int allsign = 0;


//主菜单
void mainmenu(int &cdt);
void colorshow(unsigned int num);
void backlok();


class Position {
	public:
		//构造函数
		Position(unsigned int x = 0, unsigned int y = 0) {
			this->x = x;
			this->y = y;
			ifclicked = 0;
			ifedge = 0;
			sign_cdt = 0;
			ifbomb = 0;
			num = 0;
		};

		//点击这个点,返回是否点击成功,已经点过的就不成功
		bool click() {
			if (ifclicked) {
				return false;
			} else {
				ifclicked = 1;
				return true;
			}
		}

		//设置为地雷,返回是否成功,已经过的就不成功
		bool setbomb() {
			if (ifbomb) {
				return false;
			} else {
				ifbomb = 1;
				return true;
			}
		}

		//标记为边界
		void setedge() {
			ifedge = 1;
		}

		//转换标记,0,1,2
		void sign() {
			sign_cdt = (sign_cdt + 1) % 3;
		}

		//显示点
		void showposition() {
			if (ifedge) {
				cout << "- ";
			} else if ((sign_cdt == 1 || sign_cdt == 2) && !ifclicked) {
				if (sign_cdt == 1) {
					cout << "\033[91mF\033[0m ";
					allsign++;
				} else if (sign_cdt == 2) {
					cout << "\033[91m?\033[0m ";
				}
			} else if (!ifclicked) {
				cout << "#\033[0m ";
			} else if (ifbomb) {
				cout << "\033[91m!\033[0m ";
			} else {
				colorshow(num);
			}
		};

		//结束时的展示
		void showposition_clean() {
			if (ifedge) {
				cout << "- ";
			} else if (ifbomb) {
				cout << "\033[91m!\033[0m ";
			} else {
				colorshow(num);
			}
		}

		friend class Ground;

	private:
		unsigned int x, y, num;
		bool ifclicked;
		bool ifedge;
		int sign_cdt;
		bool ifbomb;

};



class Ground {
	public:

		//构造函数
		Ground(unsigned int _Long = 0, unsigned int _Wide = 0) {
			Long = _Long + 2;
			Wide = _Wide + 2;
			tab_line = 1;
			tab_list = 1;
			Num = 0;
			reward = 0;
			//创建二维数组
			for (unsigned int i = 0; i < Long; i++) {
				vector<Position> tmp(Wide);
				positions.push_back(tmp);
			}
			for (unsigned int i = 0; i < Long; i++) {
				for (unsigned int j = 0; j < Wide; j++) {
					if (i == 0 || j == 0 || i == Long - 1 || j == Wide - 1) {
						positions[i][j].setedge();
					}
				}
			}

		};

		//显示地图
		void showground() {
			allsign = 0;
			cout << "0 ";
			for (int t = 0; t < positions[0].size(); t++) {
				cout << t % 10 << ' ';
			}
			cout << '\n';
			for (int i = 0; i < positions.size(); i++) {
				cout << i % 10 << ' ';
				for (int j = 0; j < positions[i].size(); j++) {
					if (i == tab_line && j == tab_list) {
						//显示光标背景
						cout << "\033[5m\033[103m";
						positions[i][j].showposition();
						cout << "\033[0m";
					} else {
						positions[i][j].showposition();
					}
				}
				cout << '\n';
			}
			cout << "总雷数:\033[91m" << Num << "\033[0m   当前总标记:\033[92m" << allsign << " \033[0m   复活币:\033[93m" <<
			     tool_num << "\033[0m" << '\n';
			allsign = 0;
		}

		//点击某点,包括连锁反应
		void click(unsigned int x, unsigned int y) {
			//坐标在范围内
			if (1 <= x && x <= Long - 2 && 1 <= y && y <= Wide - 2) {
				positions[x][y].click();
				//检查并点击一大块
				bool needclick = 0;
				do {
					needclick = 0;
					//遍历每一个地点
					for (int i = 1; i < positions.size() - 1; i++) {
						for (int j = 1; j < positions[i].size() - 1; j++) {
							bool click_around0 = 0;
							//遍历这个点周围的九宫格区域
							for (int m = i - 1; m <= i + 1; m++) {
								for (int n = j - 1; n <= j + 1; n++) {
									//如果点击成功就说明还需要点击
									if (positions[m][n].ifclicked && positions[m][n].num == 0) {
										click_around0 = 1;
									}
								}
							}
							if (click_around0 && !positions[i][j].ifclicked) {
								positions[i][j].click();
								needclick = 1;
							}
						}
					}
				} while (needclick);
			}
			//输入坐标错误,do nothing
		}

		//设置一个地雷
		bool setbomb_1(unsigned int x, unsigned int y) {
			if (1 <= x && x <= Long - 2 && 1 <= y && y <= Wide - 2) {
				return positions[x][y].setbomb();
			} else {
				return false;
			}
		}

		//设置全部地雷;同时设置奖励
		void setbomb(unsigned int x, unsigned int y, unsigned int num) {
			Num = num;
			reward = 10 * Num * Num / ((Long - 2) * (Wide - 2));
			//输入可用的数字
			if (1 <= x && x <= Long - 2 && 1 <= y && y <= Wide - 2 && num <= (Long - 2) * (Wide - 2)) {
				srand((unsigned)time(NULL));
				for (unsigned int i = 0; i < num;) {
					int rx = 1 + rand() % (Long - 2);
					int ry = 1 + rand() % (Wide - 2);
					if (rx != x || ry != y) {
						if (positions[rx][ry].setbomb()) {
							i++;
						}
					}
				}
			}
		}

		//设置数字
		void setnum() {
			//遍历每一个地点
			for (int i = 1; i < positions.size() - 1; i++) {
				for (int j = 1; j < positions[i].size() - 1; j++) {
					positions[i][j].num = 0;
					//遍历这个点周围的九宫格区域
					for (int m = i - 1; m <= i + 1; m++) {
						for (int n = j - 1; n <= j + 1; n++) {
							//周围有雷就加雷数
							if (positions[m][n].ifbomb) {
								positions[i][j].num++;
							}
						}
					}
					//减去自己的雷
					if (positions[i][j].ifbomb) {
						positions[i][j].num--;
					}
				}
			}
		}

		//标记或取消雷
		void setsign(unsigned int x, unsigned int y) {
			if (1 <= x && x <= Long - 2 && 1 <= y && y <= Wide - 2) {
				positions[x][y].sign();
			}
		}

		//移动一次光标
		void move_tab_1(char ch) {
			if (ch == 'w' && tab_line > 1) {
				tab_line--;
			} else if (ch == 's' && tab_line < Long - 2) {
				tab_line++;
			} else if (ch == 'a' && tab_list > 1) {
				tab_list--;
			} else if (ch == 'd' && tab_list < Wide - 2) {
				tab_list++;
			}
		}

		//移动光标
		bool move_tab() {
			while (true) {
				if (GetAsyncKeyState('W') & 0x8000) {
					move_tab_1('w');
					backlok();
					Sleep(150);
					return 1;
				} else if (GetAsyncKeyState('S') & 0x8000) {
					move_tab_1('s');
					backlok();
					Sleep(150);
					return 1;
				} else if (GetAsyncKeyState('A') & 0x8000) {
					move_tab_1('a');
					backlok();
					Sleep(150);
					return 1;
				} else if (GetAsyncKeyState('D') & 0x8000) {
					move_tab_1('d');
					backlok();
					Sleep(150);
					return 1;
				} else if (GetAsyncKeyState('J') & 0x8000) {
					click(tab_line, tab_list);
					backlok();
					Sleep(150);
					return 1;
				} else if (GetAsyncKeyState('K') & 0x8000) {
					setsign(tab_line, tab_list);
					backlok();
					Sleep(150);
					return 1;
				} else if (GetAsyncKeyState('0') & 0x8000) {
					backlok();
					return 0;
				}
			}
		}

		//初始化移动光标得到第一个坐标
		char onlymove_tab() {
			while (true) {
				if (GetAsyncKeyState('W') & 0x8000) {
					move_tab_1('w');
					backlok();
					Sleep(150);
					return 'w';
				} else if (GetAsyncKeyState('S') & 0x8000) {
					move_tab_1('s');
					backlok();
					Sleep(150);
					return 's';
				} else if (GetAsyncKeyState('A') & 0x8000) {
					move_tab_1('a');
					backlok();
					Sleep(150);
					return 'a';
				} else if (GetAsyncKeyState('D') & 0x8000) {
					move_tab_1('d');
					backlok();
					Sleep(150);
					return 'd';
				} else if (GetAsyncKeyState('J') & 0x8000) {
					Sleep(150);
					backlok();
					return 'j';
				} else if (GetAsyncKeyState('K') & 0x8000) {
					backlok();
					Sleep(150);
					return 'k';
				} else if (GetAsyncKeyState('0') & 0x8000) {
					backlok();
					return '0';
				}
			}
			//用退格键抵消多余的输入
			INPUT input;
			input.type = INPUT_KEYBOARD;
			input.ki.wVk = VK_BACK; // 退格键的虚拟键码
			input.ki.dwFlags = 0; // 指定按键按下事件
			SendInput(1, &input, sizeof(input)); // 模拟按下退格键
		}

		//在光标位置初始化
		void origin_set(int num) {
			setbomb(tab_line, tab_list, num);
			setnum();
			click(tab_line, tab_list);
		}

		//检查局势,0为正常,1为赢,-1为输
		int condition() {
			for (int i = 1; i < positions.size() - 1; i++) {
				for (int j = 1; j < positions[i].size() - 1; j++) {
					if (positions[i][j].ifbomb && positions[i][j].ifclicked) {
						return -1;
					}
				}
			}
			for (int i = 1; i < positions.size() - 1; i++) {
				for (int j = 1; j < positions[i].size() - 1; j++) {
					if (!positions[i][j].ifbomb && !positions[i][j].ifclicked) {
						return 0;
					}
				}
			}
			return 1;
		}
		//复活
		void relife() {
			for (int i = 1; i < positions.size() - 1; i++) {
				for (int j = 1; j < positions[i].size() - 1; j++) {
					if (positions[i][j].ifbomb && positions[i][j].ifclicked) {
						positions[i][j].ifclicked = 0;
						return;
					}
				}
			}
		}
		//返回奖励值
		unsigned int get_reward() {
			return reward;
		}
		//结束时的展示
		void showground_clean() {
			cout << "0 ";
			for (int t = 0; t < positions[0].size(); t++) {
				cout << t % 10 << ' ';
			}
			cout << '\n';
			for (int i = 0; i < positions.size(); i++) {
				cout << i % 10 << ' ';
				for (int j = 0; j < positions[i].size(); j++) {
					//判断输出金钩
					if (positions[i][j].ifbomb && positions[i][j].sign_cdt && !positions[i][j].ifclicked) {
						cout << "\033[93m√\033[0m ";
					} else if (positions[i][j].ifbomb && !positions[i][j].ifclicked) {
						cout << "\033[91m*\033[0m ";
					} else {
						positions[i][j].showposition_clean();
					}

				}
				cout << '\n';
			}
		}

		friend void startgame(Ground g);
	private:
		unsigned int Long, Wide, tab_line, tab_list, Num, reward;
		vector <vector<Position >> positions;
};


void colorshow(unsigned int num) {
	if (num == 0)
		cout << " \033[0m ";
	if (num == 1)
		cout << "\033[34m1\033[0m ";
	if (num == 2)
		cout << "\033[33m2\033[0m ";
	if (num == 3)
		cout << "\033[31m3\033[0m ";
	if (num == 4)
		cout << "\033[94m4\033[0m ";
	if (num == 5)
		cout << "\033[36m5\033[0m ";
	if (num == 6)
		cout << "\033[95m6\033[0m ";
	if (num == 7)
		cout << "\033[32m7\033[0m ";
	if (num == 8)
		cout << "\033[35m8\033[0m ";
	if (num == 9)
		cout << "\033[91m9\033[0m ";
}




//读取存档更新数据
void readgame() {
	ifstream ifs;
	ifs.open("savegame.txt", ios::in);
	ifs >> goldcoin;
	ifs >> tool_num;
	ifs.close();
}

//更新存档
void savegame(int re_goldcoin, unsigned int re_tool_num) {
	ofstream ofs;
	ofs.open("savegame.txt", ios::out);
	ofs << re_goldcoin << '\n';
	ofs << re_tool_num;
	ofs.close();
}

//改金币
bool change_goldcoin(int change) {
	readgame();
	if (goldcoin + change >= 0) {
		savegame(goldcoin + change, tool_num);
		return 1;
	} else {
		return 0;
	}
}

//改复活币
bool change_tool_num(int change) {
	readgame();
	if (tool_num + change >= 0) {
		savegame(goldcoin, tool_num + change);
		return 1;
	} else {
		return 0;
	}
}



//用退格键抵消多余的输入
void backlok() {
	//用退格键抵消多余的输入
	INPUT input;
	input.type = INPUT_KEYBOARD;
	input.ki.wVk = VK_BACK; // 退格键的虚拟键码
	input.ki.dwFlags = 0; // 指定按键按下事件
	SendInput(1, &input, sizeof(input)); // 模拟按下退格键
}

// 模拟按下回车键
void returnback() {
	// 模拟按下回车键
	INPUT input;
	input.type = INPUT_KEYBOARD;
	input.ki.wVk = VK_RETURN; // 回车键的虚拟键码
	input.ki.dwFlags = 0; // 指定按键按下事件
	SendInput(1, &input, sizeof(input)); // 模拟按下回车键

}

//主要游戏
void menu() {
	cout << "*****************************\n"
	     << "*   按键控制光标位置:WSAD   *\n"
	     << "*   按J:当前位置探雷       *\n"
	     << "*   按K:当前位置标记/解除  *\n"
	     << "*   按0:  返回主菜单       *\n"
	     << "*****************************\n\n\n";
}

void show_game(Ground g) {
	system("cls");
	menu();
	g.showground();
}

void endmenu() {
	cout << "*****************************\n"
	     << "*                           *\n"
	     << "*            游戏           *\n"
	     << "*            结束           *\n"
	     << "*                           *\n"
	     << "*****************************\n\n\n";
}

void startgame(Ground g, unsigned int Long, unsigned int Wide, unsigned int num) {
	while (true) {
		system("cls");
		cout << "*****************************\n"
		     << "*   按键控制光标位置:WSAD   *\n"
		     << "*   按J:当前位置探雷       *\n"
		     << "*按K:当前位置标记(首次除外)*\n"
		     << "*   按0:  返回主菜单       *\n"
		     << "*****************************\n\n\n";
		g.showground();
		char ch = g.onlymove_tab();
		if (ch == 'j' || ch == 'k')
			break;
		else if (ch == '0') {
			returnback();
			return;
		}
		backlok();
	}
	g.origin_set(num);

	int result = 0;

	//初始化完成,正式开始
	while (result == 0) {
		show_game(g);
		if (g.move_tab()) {
			result = g.condition();
		}
		//返回主菜单
		else {
			returnback();
			result = 3;
		}
		backlok();
		//选择复活
		if (result == -1) {
			readgame();
			if (tool_num > 0) {
				show_game(g);
				cout << "\n       \033[91m\033[5mDuang!!!\033[0m\n";
				cout << "\033[93m你炸了,但是你有" << tool_num <<
				     "个复活币哦,是否选择复活呢?\033[0m\n输入1:\033[93m我复活啦!\033[0m\n输入2:哎~我有就是不用\n\n你的选择是:";
				int ifrelife;
				cin >> ifrelife;
				while (cin.fail() || (ifrelife != 1 && ifrelife != 2)) {
					cin.clear();
					cin.ignore();
					show_game(g);
					cout << "\n       \033[91m\033[5mDuang!!!\033[0m\n";
					cout << "是否选择复活呢?\n输入1:复活\n输入2:不复活\n\n你的选择是:";
					cin >> ifrelife;
				}
				if (ifrelife == 1) {
					g.relife();
					change_tool_num(-1);
					result = 0;
				} else {
					result = 2;
				}

			} else {
				result = 2;
			}
		}
	}
	//赢
	if (result == 1) {
		system("cls");
		endmenu();
		g.showground_clean();
		cout << "\n       \033[93m\033[5m恭喜!你赢啦!!!\033[0m\n\n";
		cout << "\033[93m你获得了金币:" << g.get_reward() << "\033[0m";
		cout << "\n\n输入0回到主菜单:";
		char ch(0);
		change_goldcoin(g.get_reward());
		cin >> ch;
		while (ch != '0') {
			cin.clear();
			cin.ignore();
			system("cls");
			menu();
			g.showground_clean();
			cout << "\n       \033[93m\033[5m恭喜!你赢啦!!!\033[0m\n\n";
			cout << "\033[93m你获得了金币:" << g.get_reward() << "\033[0m";
			cout << "\n\n输入0回到主菜单:";
			cin >> ch;
		}
		cin.sync();
		return;
	}
	//输
	else if (result == 2) {
		system("cls");
		endmenu();
		g.showground_clean();
		cout << "\n       \033[91m\033[5mDuang!!!\033[0m";
		cout << "\n\n输入0回到主菜单:";
		char ch(0);
		cin >> ch;
		while (ch != '0') {
			cin.clear();
			cin.ignore();
			system("cls");
			menu();
			g.showground_clean();
			cout << "\n       \033[91m\033[5mDuang!!!\033[0m";
			cout << "\n\n输入0回到主菜单:";
			cin >> ch;
		}
		cin.sync();
		return;
	}

	while (getchar() != '\n');
	return;
}



//主菜单
void mainmenu_show() {
	readgame();
	cout << "\033[93m金币数:" << setw(5) << goldcoin << "                复活币:" << setw(4) << tool_num << "\033[0m\n";
	cout << "---------------主菜单--------------------\n"
	     << "|     难度                  | 选择序号  |\n"
	     << "-----------------------------------------\n"
	     << "|     \033[32m简单(  9*9, 5雷)\033[0m      |        1  |\n"
	     << "-----------------------------------------\n"
	     << "|     \033[33m中等(10*15,20雷)\033[0m      |        2  |\n"
	     << "-----------------------------------------\n"
	     << "|     \033[31m困难(16*30,99雷)\033[0m      |        3  |\n"
	     << "-----------------------------------------\n"
	     << "|   \033[35m自定义(  max60*60)\033[0m      |        4  |\n"
	     << "-----------------------------------------\n"
	     << "|           \033[93m商店\033[0m            |        5  |\n"
	     << "-----------------------------------------\n"
	     << "|           介绍            |        6  |\n"
	     << "-----------------------------------------\n";
	cout << "你的选择为:";
}

void mainmenu(int &cdt) {
	system("cls");
	mainmenu_show();
	cin >> cdt;
	while (cin.fail() || (cdt != 1 && cdt != 2 && cdt != 3 && cdt != 4 && cdt != 5 && cdt != 6)) {
		cin.clear();
		cin.ignore(1024, '\n');
		mainmenu(cdt);
	}

}



//自定义
void user_defined() {
	unsigned int Long, Wide, num;
	system("cls");
	mainmenu_show();
	cout << "自定义\n";
	cout << "行数:";

	cin >> Long;
	while (cin.fail() || Long >= 60) {
		system("cls");
		mainmenu_show();
		cout << "自定义\n";
		cout << "行数:";
		cin >> Long;
	}

	system("cls");
	mainmenu_show();
	cout << "自定义\n";
	cout << "行数:" << Long << endl;
	cout << "列数:";
	cin >> Wide;
	while (cin.fail() || Wide >= 60) {
		system("cls");
		mainmenu_show();
		cout << "自定义\n";
		cout << "行数:" << Long << endl;
		cout << "列数:";
		cin >> Wide;
	}

	system("cls");
	mainmenu_show();
	cout << "自定义\n";
	cout << "行数:" << Long << endl;
	cout << "列数:" << Wide << endl;
	cout << "地雷数:";
	cin >> num;
	while (cin.fail() || num > Long * Wide - 1) {
		system("cls");
		mainmenu_show();
		cout << "自定义\n";
		cout << "行数:" << Long << endl;
		cout << "列数:" << Wide << endl;
		cout << "地雷数:";
		cin >> num;
	}
	startgame(Ground(Long, Wide), Long, Wide, num);
	return;
}



//介绍
void introduce() {
	system("cls");
	cout << "版本号:3.0\n";

	cout << "前言:本扫雷游戏是我第一个练手的游戏,做工粗糙,以后可能会优化awa(就比如现在有乱码的bug啊啊啊)(已修复)\n"
	     << "      还有,如果在玩的时候敲了回车会在结束时直接跳过结算回主界面,就当做特性吧(不是)(已修复)\n\n"
	     << "规则:基本的扫雷,不用多说\n"
	     << "操作:在闪烁的就是你的光标,用wsad来操作\n"
	     << "      创建后,先随便探一个地点来初始化游戏(主要是防止第一就次踩雷)\n"
	     << "      按下J:探雷\n"
	     << "      按下K:标记雷(或解除标记)\n"
	     << "      按下0:返回主菜单\n"
	     << "获胜条件:吧没有雷的地方全部探完(不是全部标记)\n\n"
	     << "如果你觉得做的很烂,赶快去鞭策作者hhhhhh\n"
	     << "如果你也有做游戏的梦想就来找也可以来找我聊(企鹅:2431526739)\n虽然我还什么都不会(卑微)\n"
	     << "有C++方面的问题也可以来找我啦(虽然我也大概率答不上来)\n\n";
	cout << "\n输入任意内容回到主菜单:";
	char ch(0);
	cin >> ch;
	cin.clear();
	cin.ignore(10000, '\n');
	return;

};



//商店
int store_cdt = 0;

void show_store() {
	system("cls");
	readgame();
	cout << "\033[93m金币数:" << setw(5) << goldcoin << "                复活币:" << setw(4) << tool_num << "\n";
	cout << "                    奸商:\n"
	     << "为了助力你在雷区所向披靡,本店隆重推新:复活币\n"
	     << "10金币一枚,价格实惠,童嫂无期,还在等什么?\033[0m\n\n\n";
}

void store() {
	show_store();
	cout << "输入1:你说得对,但是把我放回主菜单\n"
	     << "输入2:好好好,买一个\n";
	store_cdt = 0;
	cin >> store_cdt;
	while (cin.fail() || (store_cdt != 1 && store_cdt != 2)) {
		cin.clear();
		cin.ignore(1024, '\n');
		store();
	}
	switch (store_cdt) {
		case 1:
			return;
			break;
		case 2:
			if (change_goldcoin(-10)) {
				change_tool_num(1);
				cout << "\033[93m老板大气!\033[0m";
				Sleep(200);
			} else {
				cout << "\033[93m你的金币呢?\033[0m";
				Sleep(500);
			}
			store();
			break;
	}

}



int main() {

	//取消输入输出流独立性,在DEV环境会格式混乱,在vs就不会
//	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	while (1) {
		cdt = 0;
		mainmenu(cdt);
		switch (cdt) {
			case 1:
				startgame(Ground(9, 9), 9, 9, 5);
				break;
			case 2:
				startgame(Ground(10, 15), 10, 15, 20);
				break;
			case 3:
				startgame(Ground(16, 30), 16, 30, 99);
				break;
			case 4:
				user_defined();
				break;
			case 5:
				store();
				break;
			case 6:
				introduce();
				break;
		}
	}
}

  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值