首页 > 技术文章 > 贪吃蛇

Vanilla-chan 2020-04-04 15:11 原文

贪吃蛇

本文已被搬运到Github去咯

https://github.com/hsh778205/Greedy-Snake

不完善的地方:电脑走只能死走,没学人工智能

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<windows.h>
#include<stdlib.h>
#include<fstream>
#include<set>
#include<queue>
#include<vector>
#include<ctime>
#include<conio.h> 
#define IL inline
#define re register
#define LL long long
using namespace std;

void init();
int lx,ly;
void hide();
void backxy();
void gotoxy0(int,int);
void gotoxy(int,int);
void getxy(); 
const int A=10,B=11,C=12,D=13,E=14,F=15;
void SetColor(unsigned short,unsigned short);
void SetColor(unsigned short=0x07);
long long step,score;
int speed,size,lenth,way,tme,init_size,start_time,clr=1,have_eaten;
int big_apple_probability,big_apple_save_time=10000,big_apple_time,mode;
long long his_score[10],his_step[10];
bool through_wall,wall_protection,self_protection,full_time,adj_speed;
bool have_big_apple;
int delta[4][2]={
{-1,0},
{0,-1},
{1,0},
{0,1}
};
struct xy{
    int x,y;
    bool operator==(xy t)
    {
        return x==t.x&&y==t.y;
    }
    bool inmap(){
        return x<size&&x>=0&&y<size&&y>=0;
    }
    xy edge(int w){
        return {x+delta[w][0],y+delta[w][1]};
    }
}apple,big_apple;

bool operator<(xy a,xy b)
{
    return a.x*100+a.y<b.x*100+b.y;
}
deque<xy>snake;
set<xy>line;
int map[100][100];
void save();
void setting();
void make_apple();
void move(int);
int judge();
int calc();
void show();

int main()
{
    srand(time(0));
    hide(); 
    setting();
    init();
    while(true)
    {
        show();
        if(mode==5)
        {
            if(_kbhit()){
//                char ch=getch();
                if(getch()==' '){
                    int t=clock();
                    while(getch()!=' ');;
                    start_time+=clock()-t;
                }
            }
            
            way=calc();
        }
        else
        {
            tme=clock();
            while(clock()-tme<=speed)
            {
                gotoxy0(size+2+5,4);
                cout<<(clock()-start_time)/1000<<"s";
                if(mode==1) gotoxy0(size+2,15),cout<<"当前苹果价值="<<log2((clock()-start_time)/1000.0)*(1000.0/speed);
                if(_kbhit()){
                    char ch=getch();
                    if((ch=='a'||ch=='A')&&way!=2){
                        way=0;break;
                    }
                    if((ch=='w'||ch=='W')&&way!=3){
                        way=1;break;
                    }
                    if((ch=='d'||ch=='D')&&way!=0){
                        way=2;break;
                    }
                    if((ch=='s'||ch=='S')&&way!=1){
                        way=3;break;
                    }
                    if(adj_speed)
                    if(ch=='+'||ch=='='||ch=='.'){
                        speed=max(0,speed-10);
                        gotoxy0(size+2,5);
                        cout<<"移动间隔:        \b\b\b\b\b\b\b\b"<<speed<<"ms";
                        if(mode==1) gotoxy0(size+2,15),cout<<"当前苹果价值=            \b\b\b\b\b\b\b\b\b\b\b\b"<<log2((clock()-start_time)/1000.0)*(1000.0/speed);
                    }
                    if(adj_speed)
                    if(ch=='-'||ch==','){
                        speed+=10;
                        gotoxy0(size+2,5);
                        cout<<"移动间隔:        \b\b\b\b\b\b\b\b"<<speed<<"ms";
                        if(mode==1) gotoxy0(size+2,15),cout<<"当前苹果价值=            \b\b\b\b\b\b\b\b\b\b\b\b"<<log2((clock()-start_time)/1000.0)*(1000.0/speed);
                    }
                    if(ch=='x'||ch=='X') save(),exit(0);
                    if(ch=='v'||ch=='V') save();
                    if(ch==' '){//暂停 
                        long ttime=clock();
                        char c;
                        do{
                            c=getch();
                            if(adj_speed)
                            if(c=='+'||c=='='||c=='.'){
                                speed=max(0,speed-10);
                                gotoxy0(size+2,5);
                                cout<<"移动间隔:        \b\b\b\b\b\b\b\b"<<speed<<"ms";
                                if(mode==1) gotoxy0(size+2,15),cout<<"当前苹果价值=            \b\b\b\b\b\b\b\b\b\b\b\b"<<log2((clock()-start_time)/1000.0)*(1000.0/speed);
                            }
                            if(adj_speed)
                            if(c=='-'||c==','){
                                speed+=10;
                                gotoxy0(size+2,5);
                                cout<<"移动间隔:        \b\b\b\b\b\b\b\b"<<speed<<"ms";
                                if(mode==1) gotoxy0(size+2,15),cout<<"当前苹果价值=            \b\b\b\b\b\b\b\b\b\b\b\b"<<log2((clock()-start_time)/1000.0)*(1000.0/speed);
                            }
                            if(c=='x') save(),exit(0);
                            if(c=='v') save();
                        }while(c!=' ');
                        tme+=clock()-ttime;
                        big_apple_time+=clock()-ttime;
                    }
                }
            }
        }
        
        if(full_time&&clock()-tme<=speed) Sleep(speed-clock()+tme);
        
        if(have_big_apple){
            gotoxy0(size+2,14);
            cout<<"                       ";
            if(clock()-big_apple_time<big_apple_save_time){
                gotoxy0(size+2,14);
                cout<<"大苹果剩余时间:"<<big_apple_save_time-clock()+big_apple_time<<"ms";
                gotoxy(big_apple.x,big_apple.y);
                if(rand()&1) cout<<"";
                else cout<<"";
            }
            else{
                gotoxy(big_apple.x,big_apple.y);
                cout<<"  ";
                have_big_apple=0;
            }
        }
        
        snake.push_front({snake.front().x+delta[way][0],snake.front().y+delta[way][1]});
        
        if(judge()==1) break;
        if(judge()==2){
            if(mode==1) score--;
            gotoxy0(2,size+2);
            cout<<"蛇蛇撞墙了!扣分ing";
            cout<<"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b                   ";
            gotoxy0(size+2,2);
            cout<<"得分:            \b\b\b\b\b\b\b\b\b\b\b\b"<<score;
            snake.pop_front();
            continue;
        }
        line.insert(snake.front());
        gotoxy(snake.front().x,snake.front().y);
        SetColor(0x0c);
        cout<<""; 
        
        //将第二格的颜色改回来 
        gotoxy(snake[1].x,snake[1].y);
        SetColor();
        if(mode==1){
            clr++;
            clr%=15;
            clr++;
            if(clr==C||clr==7||clr==0) clr++;
            SetColor(0,clr);
        }
        cout<<"";
        SetColor();
        
        //如果吃到了苹果 
        if(have_big_apple&&snake.front()==big_apple){
            score+=5;
            for(int i=0;i<5&&snake.size()>2;i++){
                line.erase(snake.back());
                gotoxy(snake.back().x,snake.back().y);
                snake.pop_back();lenth--;
                SetColor();
                cout<<"  ";
                
            }
            big_apple_time=-10000;
            show();
        }
        
        if(snake.front()==apple){
            have_eaten++;
            if(have_big_apple) big_apple_time=-10000;
            lenth++;
            
            if(snake.size()==(unsigned)size*size){
                show();
                break;
            }
            
            make_apple();
            
            if(mode==1){
                score+=log2((clock()-start_time)/1000.0)*(1000.0/speed);
                
            }
            else score+=1;
            
            show();
        } 
        else{
            line.erase(snake.back());
            gotoxy(snake.back().x,snake.back().y);
            snake.pop_back();
            SetColor();
            cout<<"  ";
        }
        
        
        gotoxy0(size+2+3,-1);
        cout<<"     \b\b\b\b\b"<<snake.front().x<<","<<snake.front().y;
        gotoxy0(size+2+5+4,-1);
        cout<<"    \b\b\b\b\b"<<apple.x<<","<<apple.y;
        gotoxy0(size+2+3,9);
        cout<<++step;
    }
    show();
    save();
    cin.get();cin.get();
    if(snake.size()>=(unsigned int)size*size){
        gotoxy0(size/2-2,size/2); 
        SetColor(4,7);
        cout<<"You have won!!!";
        cin.get();
        return 0;
    }
    
    for(int i=1;i<size;i++)
    {
        gotoxy0(1,i);
        for(int j=1;j*7<size;j++)cout<<"You are dead  ";
        cout<<"\n"; 
    }
    cout<<"\n\n\n\n\n";
    cin.get();
    main();
    return 0;
}

void save()
{
    if(his_score[mode]<score)
    {
        his_score[mode]=score;
        his_step[mode]=step;
    }
    ofstream fout("history.txt",ios::out);
    for(int i=0;i<10;i++) fout<<his_score[i]<<" "<<his_step[i]<<endl;
}

void setting()
{
    memset(his_step,sizeof(his_score),0);
    memset(his_step,sizeof(his_step),0);
    step=0;
    if(system("@echo off\ndir /a history.txt")==1){
        save();
    }
    ifstream fin("history.txt",ios::in);
    if(fin.eof()||fin.peek()==EOF){
        save();
    }
    else{
        for(int i=0;i<10;i++) fin>>his_score[i]>>his_step[i];
    }
    
    SetColor();
    system("cls");
    system("mode con cols=80 lines=40");//改变宽高
    cout<<"\t序号 名称\t地图\t速度\t大苹果\t其他属性\n";
    cout<<"\t【0】作弊模式\t20*20\t5m/s\t15%\t穿墙 吃自己不死亡 可调节速度\n";
    cout<<"\t【1】娱乐模式\t25*25\t2m/s\t30%\t撞墙保护 特殊计分方式 彩色!\n";
    cout<<"\t【2】经典模式\t20*20\t5m/s\t15%\t穿墙\n";
    cout<<"\t【3】普通模式\t15*15\t10m/s\t0%\t无\n";
    cout<<"\t【4】快速模式\t20*20\t1m/s\t20%\t穿墙 可调节速度 可手动移动\n";
    cout<<"\t【5】电脑模式\n";
    cout<<"\t【6】自定义\n";
    cout<<"请输入数字选择  或按w(上)/s(下)移动 回车确定\n";
    
    vector<string>sentence;
    sentence.push_back("请认真阅读下面的信息!!!!!!!!!!!!!!!!!!!!");
    sentence.push_back("请把控制台窗口的字号改成14或以下!!!");
    sentence.push_back("请将输入法调成英文模式!!");
    sentence.push_back("按空格键暂停游戏");
    sentence.push_back("按v键保存(只记录最高) 按x键保存并退出");
    sentence.push_back("在可调节速度的模式下,按+加速,按-减速"); 
    sentence.push_back("不仅+可以加快速度,=也可以");
    sentence.push_back("其实 ,和.也可以调整速度,但是一定要是英文的符号!");
    sentence.push_back("娱乐模式下不会死亡,推荐先玩娱乐模式"); 
    sentence.push_back("娱乐模式下,移速越快,游戏时间越长,加分越多"); 
    sentence.push_back("娱乐模式下,记得先加快一点速度哦"); 
    sentence.push_back("快速模式下,只要你手速够快,蛇想多快有多快"); 
    sentence.push_back("穿墙和撞墙保护不可共存,穿墙优先"); 
    for(unsigned int i=0;i<sentence.size();i++)
    {
        gotoxy0(16-sentence[i].size()/4,10+i*2);
        cout<<sentence[i];
    }
    
    
    
    int ch=0;
    char t;
    gotoxy0(1,0);
    cout<<"->"; 
    do{
        t=getch();
        if(t=='s'||t=='S'||t=='W'||t=='w'){
            gotoxy0(1,ch);
            cout<<"  ";
            if(t=='s'||t=='S') ch++;
            if(t=='w'||t=='W') ch--;
        }
        if(t>='0'&&t<='6'){
            ch=t-'0';
            break;
        }
        ch=(ch+7)%7;
        gotoxy0(1,ch);
        cout<<"->";
    }while(t!='\n'&&t!='\r');
    
    system("cls");
    mode=ch;
    switch(ch+'0'){
        case '0':{
            size=20;
            speed=200;
            big_apple_probability=10;
            through_wall=1;
            wall_protection=0;
            self_protection=1; 
            adj_speed=1;
            full_time=1;
            gotoxy0(size+2,6);cout<<"【0】作弊模式";
            break;
        }
        case '1':{
            size=25;
            speed=500;
            through_wall=0; 
            big_apple_probability=30;
            wall_protection=1;
            self_protection=1;
            adj_speed=1;
            full_time=1;
            gotoxy0(size+2,6);cout<<"【1】娱乐模式";
            break;
        }
        case '2':{
            size=20;
            speed=200;
            big_apple_probability=20;
            wall_protection=0;
            self_protection=0;
            adj_speed=0;
            through_wall=1; 
            full_time=1;
            gotoxy0(size+2,6);cout<<"【2】经典模式";
            break;
        }
        case '3':{
            size=15;
            speed=100;
            big_apple_probability=10;
            through_wall=0; 
            wall_protection=0;
            self_protection=0;
            adj_speed=0;
            full_time=1;
            gotoxy0(size+2,6);cout<<"【3】普通模式";
            break;
        }
        case '4':{
            size=20;
            speed=1000;
            big_apple_probability=20;
            through_wall=1;
            wall_protection=1;
            self_protection=0;
            adj_speed=1;
            full_time=0;
            gotoxy0(size+2,6);cout<<"【4】快速模式";
            break;
        }
        case '5':{
            system("mode con cols=130 lines=53");//改变宽高
            cout<<"输入地图大小(10-50且只能是偶数):";
            cin>>size;
            size=min(max(size,10),50);
            if(size&1){
                size--; 
                cout<<"已自动转化为"<<size;
            } 
            system("cls");
            speed=0;
            big_apple_probability=0;
            wall_protection=0;
            self_protection=0;
            through_wall=0; 
            adj_speed=0;
            full_time=0;
            gotoxy0(size+2,6);cout<<"【5】电脑模式";
            break;
        }
        case '6':{
            system("cls");
            cout<<"地图大小:";cin>>size;size=max(min(size,50),5);
            init_size=2;
            cout<<"移动间隔时间(ms):";cin>>speed;speed=max(min(speed,2000),10);
            cout<<"大苹果掉落概率(%):";cin>>big_apple_probability;big_apple_probability=max(min(big_apple_probability,100),0);
            cout<<"穿墙(1/0):\n";through_wall=getch()-'0';
            if(!through_wall) cout<<"撞墙不死亡(1/0):\n",wall_protection=getch()-'0';
            cout<<"吃自己不死亡(1/0):\n";self_protection=getch()-'0';
            cout<<"允许调整速度(1/0):\n";adj_speed=getch()-'0';
            cout<<"允许手动移动(1/0):\n";full_time=!(getch()-'0');
            system("cls");
//            system("mode con cols=100 lines=45");//改变宽高
            gotoxy0(size+2,6);cout<<"【6】自定义模式"; 
            break;
        }
    }
    system("mode con cols=130 lines=53");//改变宽高
}

void init()
{
    SetColor();
    memset(map,sizeof(map),0);
    gotoxy0(-1,-1);
    cout<<"  x\ny";
    for(int i=0;i<size;i++){
        gotoxy0(i+1,-1);cout<<i+1;
        gotoxy0(-1,i+1);cout<<i+1;
    }
    
    for(int i=0;i<=size+1;i++)
    {
        gotoxy0(i,0);cout<<"";
        gotoxy0(i,size+1);cout<<"";
        gotoxy0(0,i);cout<<"";
        gotoxy0(size+1,i);cout<<"";
    }
    gotoxy0(0,0);cout<<"";
    gotoxy0(0,size+1);cout<<"";
    gotoxy0(size+1,size+1);cout<<"";
    gotoxy0(size+1,0);cout<<"";
    
    snake.clear();
    line.clear();
    snake.push_back({size/2-1,size/2-1});
    line.insert(snake.back());
    gotoxy(size/2-1,size/2-1);SetColor(0x0c);cout<<"";
    
    SetColor();
    show(); 
    while(true){
        char ch=getch();
        if(ch=='a'||ch=='A'){
            way=0;break;
        }
        if(ch=='w'||ch=='W'){
            way=1;break;
        }
        if(ch=='d'||ch=='D'){
            way=2;break;
        }
        if(ch=='s'||ch=='S'){
            way=3;break;
        }
    }
    for(int i=0;i<init_size-1;i++){
        snake.push_back(snake.back().edge((way+2)%4));
        line.insert(snake.back());
        gotoxy(snake.back().x,snake.back().y);cout<<"";
    }
    start_time=clock();
    make_apple();
    
    lenth=snake.size();
    
    gotoxy0(size+2,-1);
    cout<<"蛇头:";
    gotoxy0(size+2+6,-1);
    cout<<"Apple:";
    gotoxy0(size+2,9);
    cout<<"step:"; 
    
    if(mode==5) return;
    
    gotoxy0(size+2,7); cout<<"计分方式:";
    if(mode==1) cout<<"小苹果加log2(time)*(1000/speed)分并长长1m";
    else cout<<"小苹果加1分并长长1m";
    
    gotoxy0(size+2,8);
    cout<<"大苹果加5分并缩短5m";
    
    if(mode==1) gotoxy0(size+2,12),cout<<"碰到墙会持续扣分哦",gotoxy0(size+2,13),cout<<"吃到自己会扣得分的十分之一分(不足5分记作5分)";
    
    gotoxy0(size+2,5);
    cout<<"移动间隔:          \b\b\b\b\b\b\b\b\b\b"<<speed<<"ms";
    
    
}

void hide()
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
    CursorInfo.bVisible = false; //隐藏控制台光标
    SetConsoleCursorInfo(handle, &CursorInfo);//设置控制台光标状态
}

void backxy()
{
    COORD pos;
    pos.X = lx;
    pos.Y = ly;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
} 

void gotoxy0(int x,int y)
{
    COORD pos;
    pos.X =(x<<1)+2;
    pos.Y =y+1;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
}

void gotoxy(int x, int y)
{
    COORD pos;
    pos.X =(x<<1)+4;
    pos.Y =y+2;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
}

void getxy(){
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hConsole, &csbi);
    lx=csbi.dwCursorPosition.X,ly=csbi.dwCursorPosition.Y;
} 

void SetColor(unsigned short BackGroundColor,unsigned short ForeColor)
{
    HANDLE hCon=GetStdHandle(STD_OUTPUT_HANDLE);  
    SetConsoleTextAttribute(hCon,(ForeColor%16)|(BackGroundColor%16*16));  
}

void SetColor(unsigned short x)
{
    HANDLE hCon=GetStdHandle(STD_OUTPUT_HANDLE);  
    SetConsoleTextAttribute(hCon,x);  
}

void make_apple()
{
    if(have_big_apple){
        have_big_apple=0;
        gotoxy(big_apple.x,big_apple.y);
        cout<<"  "; 
    }
    
    
    vector<xy>t;
    for(int i=0;i<size;i++)
    for(int j=0;j<size;j++)
    if(line.find({i,j})==line.end()) t.push_back({i,j});
    random_shuffle(t.begin(),t.end());
    gotoxy(t[0].x,t[0].y);
    cout<<"";
    if(snake.size()>10&&rand()%100+1<=big_apple_probability&&t.size()>=2){
        gotoxy(t[1].x,t[1].y);
        if(rand()&1) cout<<"";
        else cout<<"";
        have_big_apple=1;
        big_apple=t[1];
        big_apple_time=clock();
    }
    apple=t.front();
}

int judge()
{
    if(snake.front().inmap()&&line.find(snake.front())==line.end()) return 0;
    
    if(!snake.front().inmap()&&through_wall){
        xy h=snake.front();
        h.x=(h.x+size)%size;
        h.y=(h.y+size)%size;
        snake[0]=h;
        return 0;
    }
    
    if(!snake.front().inmap()&&wall_protection) return 2;
    
    if(line.find(snake.front())!=line.end()&&self_protection){
        if(mode==1){
            score-=max((long long)5,score/10); 
        } 
        return 0;
    }
    return 1;
}



/*



*/ 
void show() 
{
    gotoxy0(size+2,1);
    cout<<"长度:     \b\b\b\b\b"<<snake.size();
    gotoxy0(size+2+3,9);
    cout<<step;
    gotoxy0(size+2,4);cout<<"游戏时间:";
    gotoxy0(size+2+5,4);
    cout<<(clock()-start_time)/1000<<"s";
    if(mode==5) return;
    
    
    gotoxy0(size+2,0);
    cout<<"已经吃了"<<have_eaten<<""; 
    gotoxy0(size+2,2);
    cout<<"得分:                    \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"<<score;
    gotoxy0(size+2,3);
    cout<<"方向:     \b\b\b\b\b";
    if(way==0) cout<<"left";
    if(way==1) cout<<"up";
    if(way==2) cout<<"right";
    if(way==3) cout<<"down";
    
    
    if(mode==1) gotoxy0(size+2,15),cout<<"当前苹果价值=            \b\b\b\b\b\b\b\b\b\b\b\b"<<log2((clock()-start_time)/1000.0)*(1000.0/speed);
}

int calc()
{
    xy h=snake.front();
    if(snake.size()/size<(size*0.5))
    {
         
        if(h.x<apple.x/*go_front*/&&h.y==size-1&&(snake.back().x<h.x||snake.size()<(unsigned int)size-1||snake.back().x>apple.x))
        {
            if(h.x==size-1){
                return 1;
            }
            if(!(apple.x&1)&&h.x+1==apple.x){
                return 1;
            }
            if((apple.x&1)&&h.x==apple.x){
                return 1;
            }
            return 2;
        }
        if(h.x>apple.x/*go_back*/&&h.y==1&&(snake.back().x<h.x||snake.size()<(unsigned int)size-1)){
            return 1;
        }
        if(!h.y&&snake.back().x>h.x){
            if((apple.x&1)==0&&apple.x==h.x) return 3;
            if((apple.x&1)&&apple.x==h.x+1) return 3;
            //否则就用普通规则 
            if(!h.x) return 3;
            return 0; 
        }
    }
    
    if(!h.y){
        if(!h.x) return 3;
        return 0;
    }
    if(!(h.x&1)){
        if(h.y==size-1) return 2;
        else return 3;
    }
    if(h.x&1){
        if(h.y==1&&h.x!=size-1) return 2;
        //else return 1;
    }
    
    return 1;
    
}
//若要转载,搬运,复制,请不要丢了这行注释!made by 折柳

 

推荐阅读