用覆盖写的贪吃蛇,比system(“cls”)更快,对眼睛更友好
#include<bits/stdc++.h>
#include<windows.h>
#include<conio.h>
using namespace std;
//用覆盖写的贪吃蛇,比system("cls")更快,对眼睛更友好
//实现方法就是将光标移动到需要覆盖的位置,再输出
//用来存坐标
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,2,3};
int sx=2,sy=0;
int main(){//(width+2+Status_bar+1)*lenstr length+2
string cangshu1="mode con:cols="+string(((width+2)*lenstr<15)?"15":string_num((width+2)*lenstr))+" lines="+string_num(length+2);//设置控制台长和宽
char cangshu2[cangshu1.size()+1];//因为system只能接受char数组,不支持string,所以要转换
for(int i=0;i<cangshu1.size();i++)cangshu2[i]=cangshu1[i];
cangshu2[cangshu1.size()]='\0';//必须加,不然会无效指令
system(cangshu2);//有些系统可能设置不了
srand(time(0)+GetTickCount());//GetTickCount()从系统开机时间到现在的毫秒,可以大量避免同一秒内运行随机结果相同的情况(加time(0)是为了避免用GetTickCount()时系统重启重新计时导致随机结果受影响才加的
hidden(false);
char lttk='d';
int slen=3,foodmaplen=0,frandomi;
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-3)*1.0/10;//3是初始蛇身长度,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')*' ';//Shift+按键会返回大写,所以加了个判断
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;
if(mapnum[sy][sx]==-1){//碰到食物
int shu=1;//增加的长度
foodmaplen=0;
slen+=shu;
for(int i=0;i<length;i++){//添加可以生成食物位置和增加蛇身每个的长度和在一起写了
for(int j=0;j<width;j++){
if(shu-1>0&&mapnum[i][j]>0)mapnum[i][j]+=shu-1;//对shu为1可能有点多此一举,主要是支持拓展
else if(mapnum[i][j]==0&&(i!=sy||j!=sx)){
foodmap[foodmaplen].x=j;
foodmap[foodmaplen].y=i;
foodmaplen++;
}
}
}
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;
}
else{
for(int i=0;i<length;i++){
for(int j=0;j<width;j++){
if(mapnum[i][j]==1&&(i!=sy||j!=sx)){//覆盖蛇尾(当前位置的值是1的就是蛇尾)
setCursorPosition((j+1)*lenstr,(i+1));
color_print(0,0);
cout<<str;
}
if(mapnum[i][j]>0)mapnum[i][j]--;//将每一个蛇身的值减一
}
}
}
mapnum[sy][sx]=slen;
}
setCursorPosition(0,1+length);
color_print(7,0);
return 0;
}