主要是优化了代码,信息栏在做了,别催了
#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;
}