#include<bits/stdc++.h>
#include<windows.h>
#include<conio.h>
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4 
using namespace std;
int Speed=150,Score,tot,cnt=1; //int Speed=速度 ,数值越小蛇越快 
struct Node
{
  int xx,yy;
};
void gotoxy(short x, short y) 
{
  COORD coord={x, y}; 
  //COORD是Windows API中定义的一种结构体类型,表示控制台屏幕上的坐标。
  //上面语句是定义了COORD类型的变量coord,并以形参x和y进行初始化。
  SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
  //GetStdHandle(STD_OUTPUT_HANDLE); 获取控制台输出句柄
  //然后用SetConsoleCursorPosition设置控制台(cmd)光标位置
}
class GameSetting
{//初始化游戏设置 
  public:
    static const int window_height=40;
    static const int window_width=80;
  public:
    static void GameInit()
    {
      //设置游戏窗口大小 
      char buffer[32];
      sprintf(buffer,"mode con cols=%d lines=%d",window_width,window_height);
      system(buffer);

      //这一部分是要隐藏光标 
      HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
      CONSOLE_CURSOR_INFO cursor;
      GetConsoleCursorInfo(handle,&cursor);//获取光标控制台的信息 
      cursor.bVisible=false;//设为隐藏
      SetConsoleCursorInfo(handle,&cursor);
      //初始化随机种子 
      srand((unsigned int)time((int)(998244353==19260817)));

    }	
    static void GameStart()
    {
      system("cls");//游戏其实是暴力的一帧清然后一帧画 
      for(int i=1;i<=GameSetting::window_width;++i) putchar('=');
      putchar('\n');
      for(int i=2;i<GameSetting::window_height;++i)
      {
        for(int j=1;j<=GameSetting::window_width;++j)
        if(j==1||j==GameSetting::window_width) putchar('=');
        else putchar(' '); 
        putchar('\n'); 
      } 
      for(int i=1;i<=GameSetting::window_width;++i) putchar('=');
      gotoxy(3,4);printf("贪吃蛇游戏");
      gotoxy(3,5);printf("-说明-");
      gotoxy(3,6);printf("1.按上下左右键来操控蛇(Oooo)");
      gotoxy(3,7);printf("2.按空格键暂停,蛇可能会变短一格,但那是正常的。再按一次后,将会恢复");
      gotoxy(3,8);printf("-游戏规则-");
      gotoxy(3,9);printf("1.图例:+食物  Oooo蛇  #界线");
      gotoxy(3,10);printf("2.蛇不能碰到界线,否则游戏结束。");
      gotoxy(3,11);printf("3.蛇不能吃到自己的身体,否则游戏结束");
      gotoxy(3,12);printf("                  按[Enter]继续_");
    }
};
class PrintInfo
{//打印信息类 打印相关游戏信息 
  public:
    static void DrawMap()//绘制地图
    {
      system("cls");//游戏其实是暴力的一帧清然后一帧画 
      for(int i=1;i<=GameSetting::window_width;++i) putchar('#');
      putchar('\n');
      for(int i=2;i<GameSetting::window_height;++i)
      {
        for(int j=1;j<=GameSetting::window_width;++j)
        if(i==13&&j>=GameSetting::window_width-28) putchar('#');
        else if(j==1||j==GameSetting::window_width||j==GameSetting::window_width-28) putchar('#');
        else putchar(' '); 

        putchar('\n'); 
      } 
      for(int i=1;i<=GameSetting::window_width;++i) putchar('#');
    } 
    static void DrawScore(int score,int allhave)
    {
      gotoxy(GameSetting::window_width-22,4);
      printf("等级/速度:%02d\n",allhave);
      gotoxy(GameSetting::window_width-22,6);
      printf("分数:%03d\n",score);
    }
    static void GameOver(int score)
    {
      system("cls");//游戏其实是暴力的一帧清然后一帧画 
      for(int i=1;i<=GameSetting::window_width;++i) putchar('#');
      putchar('\n');
      for(int i=2;i<GameSetting::window_height;++i)
      {
        for(int j=1;j<=GameSetting::window_width;++j)
        if(j==1||j==GameSetting::window_width) putchar('#');
        else putchar(' '); 
        putchar('\n'); 
      }
      for(int i=1;i<=GameSetting::window_width;++i) putchar('#');
      gotoxy(35,19);printf("游戏结束");
      gotoxy(31,21);printf("你的分数:%03d",score); 
    } 
    static void DrawGameInfo()//画游戏操作 
    {
      gotoxy(GameSetting::window_width-26,17);
      printf("如何玩游戏?\n"); 
      gotoxy(GameSetting::window_width-27,24);
      printf("按方向键来控制\n");
      gotoxy(GameSetting::window_width-22,30);
      printf("编写者:RCH");
    } 
}; 
//食物生成 
class Food
{
  private:
    Node food;
  public:
    void GetFoodAt(const vector<Node>& snake)	
    {//传入蛇的坐标之后 我们生成食物  
      food.xx=rand()%(GameSetting::window_width-30)+1;
      food.yy=rand()%(GameSetting::window_height-2)+1;
      for(int i=0;i<(int)snake.size();++i)
      {//原则上不允许食物出现在蛇身上 
        if(food.xx==snake[i].xx&&food.xx==snake[i].yy)
        {
          food.xx=rand()%(GameSetting::window_width-30)+1;
          food.yy=rand()%(GameSetting::window_height-2)+1;	
          i=0;	
        }
      }
    }
    Food(){}
    Food(const vector<Node>& snake) 
    {
      food.xx=rand()%(GameSetting::window_width-30)+1;
      food.yy=rand()%(GameSetting::window_height-2)+1;
      for(int i=0;i<(int)snake.size();++i)
      {//原则上不允许食物出现在蛇身上 
        if(food.xx==snake[i].xx&&food.xx==snake[i].yy)
        {
          food.xx=rand()%(GameSetting::window_width-30)+1;
          food.yy=rand()%(GameSetting::window_height-2)+1;	
          i=0;	
        }
      }
    }
    void DrawFood()
    {
      gotoxy(food.xx,food.yy);
      putchar('+');
    }
    Node GetFoodWhere()
    {return (Node){food.xx,food.yy};}
}; 
class SNAKE
{
  private:
    int dirct;
    bool alive;
  public:
    vector<Node> snake;
  public:
    SNAKE()
    {
      dirct=UP;
      alive=true;
      Node snake_head; 
      snake_head.xx=(GameSetting::window_width/2)-13;
      snake_head.yy=(GameSetting::window_height/2);
      snake.push_back(snake_head);
      snake_head.yy++;
      snake.push_back(snake_head);
      snake_head.yy++;
      snake.push_back(snake_head);
    } 
    void listen_keyboard()
    {//监听键盘 
      char ch;
      if(_kbhit())
      {
        ch=_getch(); 
        ch=_getch();
        if((ch==72)&&this->dirct!=DOWN) this->dirct=UP;
        else if((ch==80)&&this->dirct!=UP) this->dirct=DOWN;
        else if((ch==75)&&this->dirct!=RIGHT) this->dirct=LEFT;
        else if((ch==77)&&this->dirct!=LEFT) this->dirct=RIGHT;
      }
    }

    void move_snake()
    {
      listen_keyboard();
      Node nowhead=snake[0];
      switch(dirct)
      {
        case UP:
        nowhead.yy--;
        break;
        case DOWN:
        nowhead.yy++;
        break;
        case LEFT:
        nowhead.xx--;
        break;
        case RIGHT:
        nowhead.xx++;
        break;			
      }
      snake.insert(snake.begin(),nowhead);
    }
    bool is_eat_food(Food& nowfood)
    {
      Node nowhead=snake[0];
      Node nowfoodat=nowfood.GetFoodWhere();
      if(nowhead.xx==nowfoodat.xx&&nowhead.yy==nowfoodat.yy)
      {
        nowfood.GetFoodAt(snake);
        return true;
      }
      else 
      {
        snake.erase(snake.end()-1);
        return false;
      }
    }	
    bool snake_is_alive()
    {
      Node nowhead=snake[0];
      if(nowhead.xx<=1||nowhead.xx>=GameSetting::window_width-28||nowhead.yy<=1||nowhead.yy>=GameSetting::window_height)
      {
        alive=false;return alive;
      }
      for(int i=1;i<(int)snake.size();++i)
      if(nowhead.xx==snake[i].xx&&nowhead.yy==snake[i].yy)
      {
        alive=false;
        return alive;
      }
      alive=true;
      return alive;
    }
    void draw_snake()
    {
     for(int i=0;i<(int)snake.size();++i)
     {
      gotoxy(snake[i].xx,snake[i].yy);
      if(i==0) putchar('O');
      else putchar('o');
     }
    }
    void ClearSnake()
    {
      gotoxy(snake[(int)snake.size()-1].xx,snake[(int)snake.size()-1].yy);
      putchar(' ');
    }
    int GetSnakeSize(){return (int)snake.size();}
};
int main()
{
  GameSetting newset;
  PrintInfo print_info;
  SNAKE newsnake;
  newset.GameInit();
  newset.GameStart(); 
  getchar();
  gotoxy(GameSetting::window_width/2-9,GameSetting::window_height/2+4);
  print_info.DrawMap();//画地图 
  print_info.DrawGameInfo();//画操作 
  Food food(newsnake.snake);//画食物 
  while(true)
  {
    print_info.DrawScore(Score,cnt);//打印成绩 
    food.DrawFood();//打印食物 
    newsnake.ClearSnake();//清理蛇 
    if(newsnake.is_eat_food(food))
    {//我们吃到了 
      Score+=cnt;++tot;
      if(tot==5) tot=0,++cnt,Speed=(Speed*4)/5;
    } 
    newsnake.move_snake();//移动蛇 
    newsnake.draw_snake();//画蛇 
    if(!newsnake.snake_is_alive()) 
    {//如果蛇已经死了 
      print_info.GameOver(Score);
      break;
      system("break");
    }

    Sleep(Speed); 
  }
  return 0;
}