没错,我今天又无聊写了个扫雷。
但无system(“cls”)!不废眼睛!渣机也能玩!
你们可能觉得是我跟system(“cls”)有仇,还是说不会用?其实都不是,只是适用的场景不同罢了:
system(“cls”)适用于要重新输出的时候,比如游戏结束时清屏输出“game over”;
覆盖输出适用于要更新状态的时候,比如对象的位置发生了改变,覆盖输出移动前的位置和移动后的位置。
一般(我)写游戏用覆盖输出更好;
不闪屏,效率高,只需要抓住哪变了、变哪了,接着覆盖输出更新就行,还可以锻炼思维。
下面就是源码,想试玩、学习的可以看看或丢编译器里:
#include<bits/stdc++.h>
#include<windows.h>
#include<conio.h>
using namespace std;
/*
我写的游戏规则:
wasd或方向键控制光标进行操作,
空格翻格子,q插/拆旗,
只有非雷的格子全点了才胜利,你们是逃不掉2选1的,
剩下的跟正常的扫雷没区别(第一个翻开的位置不会有雷)。
*/
//定义结构体存坐标
struct axis{
int x,y;
};
//自己做的非小数数字转字符串
string string_num(long long num){
string str;
bool pd=num<0;
num=abs(num);
do{
str=char(num%10+'0')+str;
num/=10;
}while(num);
if(pd)str='-'+str;
return str;
}
//设置控制台长和宽
void systemcl(long long length,long long width){
string str="mode con:cols="+string((length<15)?"15":string_num(length))+" lines="+string_num(width);//设置控制台长和宽
const int strlen=str.size()+1;//定义一个str长度+1的整型常量
char chars[strlen];//定义一个长度为str长度+1的char类型的数组
for(int i=0;i<str.size();i++)chars[i]=str[i];//因为system只能接受char数组,所以要转换
chars[str.size()]='\0';//必须加,不然会无效指令
system(chars);//有些电脑可能设置不了(我也不知道为什么)
}
//覆盖输出核心功能函数
//让光标移动到控制台指定位置(从0,0开始)
void setCursorPosition(int x, int y){
COORD coord;
coord.X=x,coord.Y=y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
}
//让输出变色
void color_print(int color,int color2){
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),color+color2*16);
}
//隐藏、显示光标
void hidden(bool pd){
CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci);
cci.bVisible=pd;//赋1为显示,赋0为隐藏
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci);
}
//修复了随机数产生了比max(a,b)大的数,而且修复了每个数产生的比例不等的情况
long long random(long long a,long long b){
return rand()*1.0/(RAND_MAX+1)*(max(a,b)-min(a,b)+1)+min(a,b);
}
//撒随机数种子
void srs(){
SYSTEMTIME c;
GetSystemTime(&c);
srand(time(0)*1000+c.wMilliseconds);//c.wMilliseconds是当前时间的毫秒,可以有效避免同一秒内种子相同(但在GetSystemTime(&c)后值是固定的,所以不能反复用),快把你那破srand(time(0))扔了吧!
}
const int length=10,width=10;//地图长宽
const int lnum=10;//雷的数量(要小于length*width)
int mapnum[width][length];//地图,-1雷,0空,1~8周围雷的数量
int mapbool[width][length];//地图状态,-1插旗,0没翻,1翻了
int cmnp[11]={0,9,2,4,1,5,11,0,8,0,4};//字符对应颜色
string mnp[11]={" ","①","②","③","④","⑤","⑥","⑦","⑧","●","<|"};//字符
int tx=0,ty=0;//光标位置
//游戏核心功能,递归空格与更新
void mapabb(int x,int y){
if(mapbool[y][x]<1){//状态是-1和0的执行(-1能进来是因为你插错旗了)
mapbool[y][x]=1;//自动帮你翻了
setCursorPosition((x+1)*2,y+1);
if(x==tx&&y==ty)color_print((mapnum[y][x]!=-1)*7,12);
else color_print(cmnp[mapnum[y][x]+(mapnum[y][x]==-1)*10],7+(mapnum[y][x]==0));
cout<<mnp[mapnum[y][x]+(mapnum[y][x]==-1)*10];//打印(你看的懂就怪了)
if(mapnum[y][x]==0)for(int fy=-1;fy<=1;fy++)for(int fx=-1;fx<=1;fx++)if((fx!=0||fy!=0)&&y+fy>=0&&y+fy<width&&x+fx>=0&&x+fx<length)mapabb(x+fx,y+fy);//当前位置是空,就继续打开周围其他格子
}
}
//获取键盘输入(含方向键)
char game_getch(){
int inttlttk=getch();//存按下的按键的ASCLL码
if(!inttlttk||inttlttk==224){
inttlttk=getch();//方向键第一被侦测会返回0或224,所以要再次侦测(我测试了很多遍,都没返回0,可我找到资料写的是会返回0或224,我也不知道)
switch(inttlttk){//72是↑,80是↓,75是←,77是→
case 72:return 'w';
case 80:return 's';
case 75:return 'a';
case 77:return 'd';
}
}
return inttlttk+(inttlttk>='A'&&inttlttk<='Z')*' ';//有返回大写的情况,所以加了个判断
}
//判断是否只有非雷的格子全点了
bool mapfo(){
for(int i=0;i<width;i++)for(int j=0;j<length;j++)if(mapnum[i][j]>=0&&mapbool[i][j]!=1)return false;
return true;
}
int main(){
srs();//撒种子
systemcl(24,12);//设置控制台长和宽
hidden(false);//隐藏光标
for(int i=-1;i<=width;i++){
for(int j=-1;j<=length;j++){
if(i==-1||j==-1||i==width||j==length)color_print(0,3);
else if(!i&&!j)color_print(7,12);
else color_print(0,0);
cout<<" ";//mnp[(mapnum[i][j]+(mapnum[i][j]<0)*10)];
}
if(i<width)cout<<"\n";
}
//打印地图
char lttk;//定义获取的输入
while(1){//第一次翻开
lttk=game_getch();//获取的输入
if(lttk==' ')break;//空格就是翻开了
setCursorPosition((tx+1)*2,ty+1);
color_print(cmnp[(mapbool[ty][tx]==1)*mapnum[ty][tx]+(mapbool[ty][tx]==-1)*10],(mapbool[ty][tx]!=0)*(7+(mapbool[ty][tx]==1)*(mapnum[ty][tx]==0)+(mapbool[ty][tx]==-1)*8));
cout<<mnp[(mapbool[ty][tx]==1)*mapnum[ty][tx]+(mapbool[ty][tx]==-1)*10];//移动前更新
tx+=(lttk=='d')*(tx<length-1)-(lttk=='a')*(tx>0);
ty+=(lttk=='s')*(ty<width-1)-(lttk=='w')*(ty>0);//移动和限制
setCursorPosition((tx+1)*2,ty+1);
color_print(7,12);
cout<<mnp[(mapbool[ty][tx]==1)*mapnum[ty][tx]+(mapbool[ty][tx]==-1)*10];//移动后更新
}
axis tmn[width*length-1];//存坐标,不含第一次翻开的位置
int lentmn=0;//存长度
for(int i=0;i<width;i++){
for(int j=0;j<length;j++){
if(i==ty&&j==tx)continue;
tmn[lentmn].x=j;
tmn[lentmn].y=i;
lentmn++;
}
}//存坐标
for(int i=0;i<lnum;i++){
int idx=random(0,lentmn-1-i);
mapnum[tmn[idx].y][tmn[idx].x]=-1;
swap(tmn[idx],tmn[lentmn-1-i]);
}//将随机到的几个位置放在地图对应的位置(细节:swap将idx项的值依次丢到了后面,记住,后面要考的)
for(int i=0;i<width;i++){
for(int j=0;j<length;j++){
if(!mapnum[i][j]){
int shu=0;
for(int fy=-1;fy<=1;fy++)for(int fx=-1;fx<=1;fx++)shu+=(i+fy>=0&&i+fy<width&&j+fx>=0&&j+fx<length&&mapnum[i+fy][j+fx]==-1);
mapnum[i][j]=shu;
}
}
}//更新地图
mapabb(tx,ty);//打印地图
while(1){
while(1){//重复的地方就不说了
lttk=game_getch();
if(lttk==' '&&mapbool[ty][tx]!=-1||lttk=='q'&&mapbool[ty][tx]!=1)break;//可以插/拆旗了
setCursorPosition((tx+1)*2,ty+1);
color_print(cmnp[(mapbool[ty][tx]==1)*mapnum[ty][tx]+(mapbool[ty][tx]==-1)*10],(mapbool[ty][tx]!=0)*(7+(mapbool[ty][tx]==1)*(mapnum[ty][tx]==0)+(mapbool[ty][tx]==-1)*8));
cout<<mnp[(mapbool[ty][tx]==1)*mapnum[ty][tx]+(mapbool[ty][tx]==-1)*10];
tx+=(lttk=='d')*(tx<length-1)-(lttk=='a')*(tx>0);
ty+=(lttk=='s')*(ty<width-1)-(lttk=='w')*(ty>0);
setCursorPosition((tx+1)*2,ty+1);
color_print(7,12);
cout<<mnp[(mapbool[ty][tx]==1)*mapnum[ty][tx]+(mapbool[ty][tx]==-1)*10];
}
if(lttk=='q'){
mapbool[ty][tx]=0-(!mapbool[ty][tx]);//更新状态
setCursorPosition((tx+1)*2,ty+1);
color_print(7,12);
cout<<mnp[(mapbool[ty][tx]==1)*mapnum[ty][tx]+(mapbool[ty][tx]==-1)*10];//更新
continue;//进行下一次循环
}
//翻格子
mapabb(tx,ty);//更新状态,打印地图
if(mapnum[ty][tx]==-1){//还是自信大蛇
for(int i=0;i<lnum;i++){
if(tmn[width*length-2-i].x==tx&&tmn[width*length-2-i].y==ty)continue;//光标上的不打印
setCursorPosition((tmn[width*length-2-i].x+1)*2,tmn[width*length-2-i].y+1);
color_print(0,7);
cout<<"●";//打印其他雷的位置
/*
《扫雷,但无system("cls")!》帖子扫雷源码真题:
简答题:tmn[width*length-2-i]的下标意义是什么?为什么可以这样用?
*/
}
break;//没救了
}
if(mapfo())break;//可恶,你赢了
}
game_getch();//等待返回
setCursorPosition(0,2+length);
return 0;
}