第2代贪吃蛇,渣机也能玩

主要是优化了代码,信息栏在做了,别催了

#include<bits/stdc++.h>
#include<windows.h>
#include<conio.h>
using namespace std;

//用覆盖写的贪吃蛇,比system("cls")更快,对眼睛更友好,渣机也能玩 
//实现方法就是将光标移动到需要覆盖的位置,再输出 
//为了提升效率和缩减代码长度,只用了5个循环:打印地图2个、游戏主循环1个和游戏主逻辑2个 

//用来存坐标的结构体 
struct axis{
    int x,y;
};

//自己做的非小数数字转字符串
string string_num(long long num){
    string str;
    bool pd=true,fu=num<0;
    num=abs(num);
    while(num||pd){
        str=char(num%10+'0')+str;
        num/=10;
        pd=false;
    }
    if(fu)return '-'+str;
    return str;
}

//让光标移动到控制台指定位置(从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);
}

const int length=25,width=25;//地图长宽 
const string str="██";//字符串皮肤 
const int lenstr=2;//不能接str.size()(str.length() 也是一样的),因为有些直接赋值的字符串用size()获取的长度有问题,比如:██size()是4,实际要正确渲染的长度是2,而cin读入的就没有这个问题(我也不知道为什么) 
//中字是字数*2,大部分字符都是字数*2,字母和数字等是字数 
int mapnum[length][width]={1};
int sx=0,sy=0;//蛇头位置,sx是行,sy是列 

int main(){
    string cangshu1="mode con:cols="+string(((width+2)*lenstr<15)?"15":string_num((width+2)*lenstr))+" lines="+string_num(length+2);//设置控制台长和宽
    const int strlen=cangshu1.size()+1;//定义一个cangshu1长度+1的整型常量
	char cangshu2[strlen];//定义一个长度为cangshu1长度+1的char类型的数组 
    for(int i=0;i<cangshu1.size();i++)cangshu2[i]=cangshu1[i];//因为system只能接受char数组,所以要转换
    cangshu2[cangshu1.size()]='\0';//必须加,不然会无效指令
    system(cangshu2);//有些系统可能设置不了(我也不知道为什么) 
    srand(time(0)+GetTickCount());//GetTickCount()从系统开机时间到现在的毫秒,可以大量避免同一秒内运行随机结果相同的情况(加time(0)是为了避免用GetTickCount()时系统重启重新计时导致随机结果受影响才加的 
    hidden(false);
    char lttk='d';
    int slen=1,foodmaplen=0,frandomi;//slen就是蛇的长度 
    axis foodmap[length*width];
    //添加可以生成食物位置和打印地图和在一起写了 
    for(int i=-1;i<=length;i++){
        for(int j=-1;j<=width;j++){
    		setCursorPosition((j+1)*lenstr,i+1);
            if(i>=0&&i<length&&j>=0&&j<width){
	            if(mapnum[i][j]==0){
		            foodmap[foodmaplen].x=j;
		            foodmap[foodmaplen].y=i;
		            foodmaplen++;
		            color_print(0,0);
		        }
		        else{
		            if(sy==i&&sx==j)color_print(4,0);
		            else color_print(12,0);
		        }
			}
            else if(i==-1||i==length||j==-1||j==width)color_print(9,0);
            else color_print(0,0);
			cout<<str;
        }
        if(i!=length)cout<<endl;//不能接'\n',因为'\n'占一个字符,对有限制长度的控制台需多留一个位置输出'\n' 
    }
    if(foodmaplen==0)return 0;
	random_shuffle(foodmap,foodmap+foodmaplen);//随机打乱数组(好像支持结构体) 
    frandomi=random(0,foodmaplen-1);//随机抽取一个坐标(和上一个随机打乱数组连在一起,感觉是不是有点多此一举?但我也不知道为什们,一开始只用了随机抽取一个坐标的写法,总是随机到同一位置,不是随机种子的问题,而再次抽取坐标时就没有这种情况) 
    mapnum[foodmap[frandomi].y][foodmap[frandomi].x]=-1;
    setCursorPosition((foodmap[frandomi].x+1)*lenstr,foodmap[frandomi].y+1);
    color_print(10,0);
    cout<<str;
    while(1){
        double t=(slen-1)*1.0/10;//slen-1的1是初始蛇身长度,10是达到最大速度的长度 
        t=(t>1)?1:t;
        t=(t<0)?0:t;//限制,让t在0~1之间 
        Sleep(200-100*t);//200是最慢的等待时间,100是最大减少的等待时间 
        hidden(false);
        //侦测按下的按键(支持wasd和方向键,我写的是把方向键归wasd判断了) 
        if(kbhit()){//侦测控制台是否按下某一个按键
            char chartlttk;//实际用来判断的按下的按键 
            int inttlttk=getch();//存按下的按键的ASCLL码 
            if(!inttlttk||inttlttk==224)inttlttk=getch();//方向键第一被侦测会返回0或224,所以要再次侦测(我测试了很多遍,都没返回0,可我找到资料写的是会返回0或224,我也不知道) 
            switch(inttlttk){//72是↑,80是↓,75是←,77是→
                case 72:chartlttk='w';break;
                case 80:chartlttk='s';break;
                case 75:chartlttk='a';break;
                case 77:chartlttk='d';break;
                default:
                    chartlttk=inttlttk+(inttlttk>='A'&&inttlttk<='Z')*' ';//有返回大写的情况,所以加了个判断 
                    break;
            }
            if((chartlttk=='a'||chartlttk=='d')&&(lttk=='w'||lttk=='s')||(chartlttk=='w'||chartlttk=='s')&&(lttk=='a'||lttk=='d'))lttk=chartlttk;//左转和右装才会变改方向 
        }
        int tsx=sx,tsy=sy;//设置蛇头原位置 
        sx+=(lttk=='d')-(lttk=='a');
        sy+=(lttk=='s')-(lttk=='w');//移动 
        if(sx<0||sx>=width||sy<0||sy>=length||mapnum[sy][sx]>1)break;//判断是否撞墙或碰到蛇身(我写的是碰到蛇尾不会结束,不喜欢的可以换成mapnum[sy][sx]>0) 
        setCursorPosition((tsx+1)*lenstr,tsy+1);//覆盖蛇头原位置 
        color_print(12,0);
        cout<<str;
        setCursorPosition((sx+1)*lenstr,sy+1);//覆盖蛇头现在位置 
        color_print(4,0);
        cout<<str;
        bool pd=(mapnum[sy][sx]==-1);
        int shu=int((pd)?1:0);//1是增加的长度
    	foodmaplen=0;
        slen+=shu; 
        mapnum[sy][sx]=slen;
        for(int i=0;i<length;i++){
        	for(int j=0;j<width;j++){
        		if(!pd){//碰到食物就一定不会覆盖
        			if(mapnum[i][j]==1&&(j!=sx||i!=sy)){//覆盖蛇尾(当前位置是1与不是蛇头的位置时就是蛇尾) 
	                    setCursorPosition((j+1)*lenstr,(i+1));
	                    color_print(0,0);
	                    cout<<str;
	                }
				}//碰到食物就准备生成新的食物 
	        	else if(mapnum[i][j]==0){//只有当前位置是0的才是可能要生成的位置 
                    foodmap[foodmaplen].x=j;
                    foodmap[foodmaplen].y=i;
                    foodmaplen++;
                }
        		if(mapnum[i][j]>0&&(j!=sx||i!=sy))mapnum[i][j]+=shu-1;//将每一个蛇身的值减一,如果碰到食物了,shu就会和-1抵消 
			}
		}
        if(!pd)continue;//没碰到食物就不执行下面的代码 
        if(foodmaplen==0)break;//没有可以用来生成食物的位置了,胜利
        random_shuffle(foodmap,foodmap+foodmaplen);
        frandomi=random(0,foodmaplen-1);
        mapnum[foodmap[frandomi].y][foodmap[frandomi].x]=-1; 
        setCursorPosition((foodmap[frandomi].x+1)*lenstr,foodmap[frandomi].y+1);
        color_print(10,0);
        cout<<str;
    }
    setCursorPosition(0,1+length);
    color_print(7,0); 
    return 0;
}
1 个赞