C++实现俄罗斯方块源码

作者:网络 时间:2023-06-27 23:49:14 

本文实例为大家分享了C++实现俄罗斯方块的具体代码,供大家参考,具体内容如下

先是效果图:

主菜单:

C++实现俄罗斯方块源码

游戏:

C++实现俄罗斯方块源码

设置:

C++实现俄罗斯方块源码

错误处理:

C++实现俄罗斯方块源码

代码:


#include <iostream.h>
#include <conio.h>
#include <stdio.h>
#include <windows.h>
#include <fstream.h>
#include <time.h>
#include <cstring>
#pragma comment( lib,"winmm.lib" )
//定义
   //方块
#define NO 0
#define SQR 1
   //碰撞检测
#define OK 0
#define CANTMOVE 1
   //方向
#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3
   //错误码
#define no_enough_memory    0
#define set_no_found    1
#define dat_no_found    2
#define error_argument  3
//函数声明
   //模块
void play();//开始游戏
void sets();//设置
void highscores();//排行榜
void copyright();//作者
   //功能
void mapsetup();//准备地图
bool newsqr();//放置方块,返回是否游戏结束
int move(int direction);//移动方块,返回定义表
void movetomap();//把当前方块移动到地图上
int wholeline();//检查是否组成了一层,返回层数,-1表示没有
void deleteline(int which);//删除一行
void endup();//结束游戏,清理内存
   //显示
void show();//刷新画面
void showmenu(char* menu);//显示菜单
   //文件
void loadset();//加载设置
void saveset();//保存设置
void loadhs();//加载排行榜
bool addscores(int score,char name[50]);//增加一个分数,返回是否是高分
void savehs();//保存排行榜
   //坐标变换
int get(int x,int y);
void set(int x,int y,int date);
//结构
   //设置
struct{
   int xs,ys;//屏幕大小
   int speed;//速度
   char sqr[3],no[3],frame[3];//方块、空白处、边框的样式
}gameset;
   //排行榜
struct{
   char name[50];
   int score;
}rating[10];
//全局变量
   //变量
int* map=NULL;//地图
bool now[4][4];//当前方块
int xnow,ynow;//当前位置
int guide;//分数
   //常量
const bool shap[7][4][4]={//形状
{\
0,0,0,0,\
0,0,0,0,\
1,1,1,1,\
0,0,0,0,\
},\
{\
0,0,0,0,\
0,1,1,0,\
0,1,1,0,\
0,0,0,0,\
},\
{\
0,0,0,0,\
0,1,1,1,\
0,0,1,0,\
0,0,0,0,\
},\
{\
0,0,0,0,\
1,0,0,0,\
1,1,1,0,\
0,0,0,0,\
},\
{\
0,0,0,0,\
0,0,0,1,\
0,1,1,1,\
0,0,0,0,\
},\
{\
0,1,0,0,\
0,1,1,0,\
0,0,1,0,\
0,0,0,0,\
},\
{\
0,0,1,0,\
0,1,1,0,\
0,1,0,0,\
0,0,0,0,\
}\
};
const char errword[4][50]={"程序没能取得足够的内存","无法打开或找不到设置文件set.ini","无法打开或找不到排行榜数据highscore.dat","您设置的参数太大或者太小"};
   //控制台
HANDLE    hout;//控制台句柄
COORD curpos={0,0};//光标坐标
//主函数
int main()
{
start1:
   try
   {
   hout = GetStdHandle(STD_OUTPUT_HANDLE);//获取控制台句柄,以便移动光标
   srand(time(0));//用当前时间初始化随机数生成器
   loadset();//加载
   loadhs();
start2:
   while(1)
   {
       showmenu("俄罗斯方块\n请选择菜单:\n1.开始游戏\n2.设置\n3.排行榜\n4.帮助\n5.保存并退出\n");
       switch(getch())
       {
       case '1':
           system("cls");//play函数覆盖界面而不是清屏,所以需要先清屏
           play();
           break;
       case '2':
           sets();
           break;
       case '3':
           highscores();
           break;
       case '4':
           copyright();
           break;
       case '5':
           savehs();//保存数据
           saveset();
           return 0;
       }
   }
   }
   catch(int errnum)//错误处理
   {
       system("cls");
       printf("o(>﹏<)o 出错啦!\n程序收到了一条错误信息,错误码是:%d(%s)\n您可以联系我们解决这个问题。\n",errnum,errword[errnum]);
       printf("\n你可以选择以下操作:\n1.重启程序\n2.以默认设置重启程序\n3.向设置和数据文件写入默认设置然后重启\n4.退出\n");
       switch(getch())
       {
       case '1':
           goto start1;
       case '2':
           gameset.xs=20;
           gameset.ys=20;
           gameset.speed=100;
           strcpy(gameset.sqr,"[]");//无法直接给数组复制数据
           strcpy(gameset.no,"  ");
           strcpy(gameset.frame,"::");
           int i;
           for(i=0;i<10;i++)
               strcpy(rating[i].name,"未命名"),rating[i].score=0;
           goto start2;
       case '3':
           {
           ofstream fout;
           fout.open("set.ini");
           fout<<"20\n20\n100[]\n  \n::\n";
           fout.close();
           fout.clear();
           fout.open("highscore.dat");
           int j;
           for(j=0;j<10;j++)
               fout<<"未命名\n0\n";
           goto start1;
           }
       default:
           return -1;//返回异常退出
       }
   }
   return 0;
}
void play()
{
   mapsetup();//初始化
   /*for(int i=0;i<20;i++)
       set(i,19,SQR);*/
   while(newsqr())//不断新建方块,直到返回NO
   {
       while(move(DOWN)!=CANTMOVE)//每次向下移动方块,直到不能移动
       {
           guide+=1;//向下移动一次加1分
           show();//显示
           while(kbhit())//不断处理键盘,直到没有按键
           {
               switch(getch())//获取按键
               {
               case 'w':
                   move(UP);
                   break;
               case 's':
                   move(DOWN);
                   break;
               case 'a':
                   move(LEFT);
                   break;
               case 'd':
                   move(RIGHT);
                   break;
               }
           }
           Sleep(gameset.speed);//延时
       }
       movetomap();//退出循环时无法向下移动,把当前方块移动到地图上
       int line;
       while((line=wholeline())!=-1);//不断检查是否出现整行,直到没有
           deleteline(line);//删除整行
   }
   endup();//无法新建方块,游戏结束
   return;//结束
}
//函数定义
void mapsetup()
{
   map=new int[gameset.xs*gameset.ys];//申请内存
   if(!map)//如果申请到0
       throw no_enough_memory;//抛出异常
   //初始化地图
   int i,j;
   for(i=0;i<gameset.xs;i++)
   {
       for(j=0;j<gameset.ys;j++)
       {
           set(i,j,NO);
       }
   }
   guide=0;//分数清零
   return;
}
int get(int x,int y)
{
   if(y<0)//上方虚拟为空
       return NO;
   if(x>=0&&x<gameset.xs&&y>=0&&y<gameset.ys)//是否在地图范围内
       return *(map+y*gameset.xs+x);//提取数据
   else
       return SQR;//虚拟地图侧面和底部有方块
}
void set(int x,int y,int date)
{
   if(x>=0&&x<gameset.xs&&y>=0&&y<gameset.ys)//if(x>0&&x<gameset.xs&&y>0&&y<gameset.ys)//是否在地图范围内
       *(map+y*gameset.xs+x)=date;//写入
   return;
}
bool newsqr(){
   int i,j;
   for(i=0;i<4;i++)//检查下一个方块要出现的地方是否有方块
       if(get(gameset.xs/2+i,0)==SQR)
           return false;//有方块,创建失败
   int which=rand()%7;//随机选择形状
   for(i=0;i<4;i++)
   {
       for(j=0;j<4;j++)
       {
           now[i][j]=shap[which][i][j];//复制形状
       }
   }
   for(i=rand()%4;i>0;i--)//旋转随机0-3次
       move(UP);
   xnow=gameset.xs/2;//设置坐标
   ynow=-4;
   return true;
}
int move(int direction){
   int x,y;//储存坐标偏移量
   int i,j;
   switch(direction)
   {
   case UP://上键是旋转
       bool newshap[4][4];//储存旋转后的图形
       for(i=0;i<4;i++)
       {
           for(j=0;j<4;j++)
           {
               newshap[i][j]=now[j][3-i];//坐标变换
           }
       }
       for(i=0;i<4;i++)
       {
           for(j=0;j<4;j++)
           {
               if(newshap[i][j]==true&&get(xnow+i,ynow+j)==SQR)//对新图形碰撞检测
                   return CANTMOVE;//不能旋转
           }
       }
       for(i=0;i<4;i++)
       {
           for(j=0;j<4;j++)
           {
               now[i][j]=newshap[i][j];//检测完毕,复制形状
           }
       }
       return OK;
   case DOWN://先记录坐标的偏移量,确定没有碰撞以后移动
       x=0,y=1;
       break;
   case LEFT:
       x=-1;y=0;
       break;
   case RIGHT:
       x=1,y=0;
       break;
   }
   for(i=0;i<4;i++)
   {
       for(j=0;j<4;j++)
       {
           if(now[i][j]==true&&get(i+x+xnow,j+y+ynow)==SQR)//如果和地图上的方块重合(边缘以外get函数也返回SQR,不必单独处理)//if(get(i+x,j+y)==SQR)//if(now[i+x][j+y]==SQR)
           {
               return CANTMOVE;//无法移动
           }
       }
   }
   xnow+=x;//检测完毕,更改坐标
   ynow+=y;
   return OK;
}
void movetomap(){
   guide+=10;//成功放置方块,加10分
   int i,j;
   for(i=0;i<4;i++)
   {
       for(j=0;j<4;j++)
       {
           if(now[i][j]==true)
               set(xnow+i,ynow+j,SQR);//复制方块到地图
       }
   }
   return;
}
int wholeline(){
   int i,j;
   bool whole;//储存是否是整行
   for(j=0;j<gameset.ys;j++)//for(i=0;i<gameset.ys;i++)
   {
       whole=true;//假设是整行
       for(i=0;i<gameset.xs;i++)//for(j=0;j<gameset.xs;j++)
       {
           if(get(i,j)==NO)
               whole=false;//有空,不是整行
       }
       if(whole)
           return j;//是整行,返回
   }
   return -1;//没找到整行,返回
}
void deleteline(int which){
   int i,j;
   guide+=1000;//消方块,奖励分数
   for(i=which;i>=0;i--)
   {
       for(j=0;j<gameset.xs;j++)
       {
           set(j,i,get(j,i-1));//移动上面的所有方块,覆盖这一行。最上面虚拟成了空,不必特殊处理
       }
   }
   return;
}
void endup(){
   delete map;//清理内存
   system("cls");
   while(kbhit())//清除所有未处理的按键
       getchar();
   showmenu("游戏结束,请输入您的姓名:");
   char name[50]="noname";
   scanf("%s",&name[0]);//输入
   char word[1000];//储存格式化以后的字符串
   sprintf(&word[0],"游戏结束!\n\n您(%s)的积分是:%d\n\n%s\n\n请按任意键继续···\n",name,guide,((addscores(guide,name))?"你进入了排行榜":"你没有进入排行榜"));
   showmenu(&word[0]);
   getch();
   highscores();//显示排行榜
   savehs();//保存排行榜
   return;
}
void show(){
   int i,j;
   SetConsoleCursorPosition(hout,curpos);//system("cls");//光标移至左上角,覆盖之前的图案
   printf("当前积分:%d\n",guide);
   for(i=0;i<gameset.xs+2;i++)//输出上边框
       printf(gameset.frame);
   printf("\n");
   for(j=0;j<gameset.ys;j++)
   {
       printf(gameset.frame);//左边框
       for(i=0;i<gameset.xs;i++)
       {
           if(i>=xnow&&i<(xnow+4)&&j>=ynow&&j<(ynow+4))//if(i>=xnow&&i<(xnow+1)&&j>=ynow&&j<(ynow+1))//在当前方块范围内
           {
               if(now[i-xnow][j-ynow]==true)//如果有方块
                   printf(gameset.sqr);
               else if(get(i,j)==SQR)//如果地图有方块
                   printf(gameset.sqr);
               else//否则,空白
                   printf(gameset.no);
           }
           else//不在当前方块范围内,输出地图
           {
               if(get(i,j)==SQR)//有方块
                   printf(gameset.sqr);
               else//否则,没方块
                   printf(gameset.no);
           }
       }
       printf("::\n");//右边框和换行
   }
   for(i=0;i<gameset.xs+2;i++)//下边框
       printf(gameset.frame);
   printf("\n");
   return;
}
void showmenu(char* menu)
{
   int i,j;
   char output[100];//储存本行的文字
   system("cls");
   for(i=0;i<gameset.xs;i++)
       printf(gameset.frame);//输出上边框
   printf("\n");
   i=0,j=0;
   while(*menu!='\0')
   {
       printf(gameset.frame);//左边框
       for(i=0,j=0;*(menu+i)!='\n'&&*(menu+i)!='\0';i++,j++)//复制本行
       {
           if(*(menu+i)=='\t')//如果是制表符,输出空格直到列数是6的倍数
           {
               for(;j%6!=5;j++)
                   output[j]=gameset.no[0];
               j--;
           }
           else
               output[j]=*(menu+i);//直接复制
       }
       menu=menu+i+1;//移动指针到下一行
       for(;j<gameset.xs*2-6;j++)//用空格填充本行的后面
           output[j]=gameset.no[0];
       output[j]='\0';//结束标记
       printf(gameset.no);//行首空格,让界面更好看
       printf(output);//输出内容
       printf(gameset.frame);//右边框
       printf("\n");//换行
       Sleep(100);//延时,显示渐渐出现的效果
   }
   for(i=0;i<gameset.xs;i++)
       printf(gameset.frame);//输出下边框
   printf("\n");
   return;
}
void sets()
{
   char word[1000];//要显示的文字
while(1)
{//使用符号'\'告诉编译器下一行应该和本行连起来再编译
   sprintf(&word[0],"\
设置菜单\n\
请选择你要更改的选项:\n\
屏幕大小:\n\
\t1.宽度:%d\n\
\t2.高度:%d\n\
速度:\n\
\t3.方块下落速度:%d\n\
显示:\n\
\t4.方块形状:\"%s\"\n\
\t5.空白区域形状:\"%s\"\n\
\t6.边框形状:\"%s\"\n\
7.返回\n\
",gameset.xs,gameset.ys,gameset.speed,gameset.sqr,gameset.no,gameset.frame);
   showmenu(&word[0]);
   char choice=getch();
   showmenu("请输入改变后的参数:");//显示提示
   switch(choice)//分情况输入
   {
   case '1':
       scanf("%d",&gameset.xs);
       if(gameset.xs<15||gameset.xs>70)
           throw error_argument;
       break;
   case '2':
       scanf("%d",&gameset.ys);
       if(gameset.ys<15||gameset.ys>70)
           throw error_argument;
       break;
   case '3':
       scanf("%d",&gameset.speed);
       if(gameset.speed<0)
           throw error_argument;
       break;
   case '4':
       cin.getline(&gameset.sqr[0],3);//scanf("%s",&gameset.sqr[0]);
       cout<<endl;
       //gameset.sqr[3]='\0';
       break;
   case '5':
       cin.getline(&gameset.no[0],3);//scanf("%s",&gameset.no[0]);
       cout<<endl;
       //gameset.no[3]='\0';
       break;
   case '6':
       cin.getline(&gameset.frame[0],3);//scanf("%s",&gameset.frame[0]);
       cout<<endl;
       //gameset.frame[3]='\0';
       break;
   case '7':
       saveset();//保存设置并返回
       return;
   }
}
}
void highscores()
{
   int i;
   char word[1000]="排行榜\n排名\t姓名\t积分\n\0";//抬头
   for(i=0;i<10;i++)
   {
       sprintf(&word[0],"%s%d\t%s\t%d\n",&word[0],i+1,rating[i].name,rating[i].score);//追加名单
   }
   sprintf(&word[0],"%s请按任意键继续···\n",&word[0]);//追加提示
   showmenu(&word[0]);
   getch();//等待按键
   return;
}
void copyright()
{
   showmenu("\
请使用a,s,d,w键,\n\
a,s,d分别为\n\
向左下右移动,\n\
w为旋转\n\
\n\
移动速度是越小越快\n\
\n\
请按任意键继续···\n\
");
       getch();
   return;
}
void loadset()
{
   ifstream fin;
   fin.open("set.ini",ios::in|ios::nocreate);//输入文件流
   if(!fin)
       throw set_no_found;
   fin>>gameset.xs>>gameset.ys>>gameset.speed;
   fin.getline(gameset.sqr,4);//获取整行,因为可能有空格
   fin.getline(gameset.no,4);
   fin.getline(gameset.frame,4);
   return;
}
void saveset()
{
   ofstream fout;//输出文件流
   fout.open("set.ini",ifstream::out|ios::nocreate);
   if(!fout)
       throw set_no_found;
   fout<<gameset.xs<<'\n'<<gameset.ys<<'\n'<<gameset.speed<<gameset.sqr<<'\n'<<gameset.no<<'\n'<<gameset.frame<<'\n';
   return;
}
void loadhs()
{
   int i;
   ifstream fin;
   fin.open("highscore.dat",ifstream::in|ios::nocreate);//打开文件,不存在则错误
   if(!fin)//如果错误
       throw dat_no_found;
   for(i=0;i<10;i++)//读取文件
       fin>>rating[i].name>>rating[i].score;
   return;
}
bool addscores(int score,char name[50])
{
   int i,j;
   for(i=0;i<10;i++)//枚举
   {
       if(rating[i].score<score)//如果排行榜的积分比新积分小
       {
           for(j=9;j>i;j--)//移动数据空出位置
           {
               for(int k=0;k<50;k++)
                   rating[j].name[k]=rating[j-1].name[k];
               rating[j].score=rating[j-1].score;
           }
           rating[i].score=score;//插入数据
           strcpy(rating[i].name,name);
           return true;//返回进入排行
       }
   }
   return false;//返回没有进入
}
void savehs()
{
   int i;
   ofstream fout;
   fout.open("highscore.dat",ifstream::out|ios::nocreate);
   if(!fout)
       throw dat_no_found;
   for(i=0;i<10;i++)
       fout<<rating[i].name<<'\n'<<rating[i].score<<'\n';
   return;
}
标签:C++,俄罗斯方块
0
投稿

猜你喜欢

  • c#调用vc写的ocx控件示例

    2021-09-26 19:34:00
  • C语言文件操作之fread函数详解

    2023-07-06 18:24:15
  • C#数据结构与算法揭秘三 链表

    2022-04-17 03:22:38
  • Android 用Time和Calendar获取系统当前时间源码分享(年月日时分秒周几)

    2022-10-17 16:45:51
  • Android Intent实现页面跳转的方法示例

    2021-11-08 05:27:22
  • Android如何通过组合的方式自定义View

    2022-11-01 04:33:46
  • springboot快速整合Mybatis组件的方法(推荐)

    2022-03-23 11:59:15
  • 使用jpa之动态插入与修改(重写save)

    2021-07-04 21:02:26
  • Android如何利用RecyclerView实现列表倒计时效果实例代码

    2023-01-24 08:08:53
  • Java毕业设计实战之教室预订管理系统的实现

    2023-03-03 20:38:11
  • java 读取本地文件实例详解

    2023-08-12 20:41:32
  • C# WebApi CORS跨域问题解决方案

    2022-12-21 05:01:33
  • Android 登录处理简单实例(源码下载)

    2023-06-22 11:44:43
  • Android中实现下载URL地址的网络资源的实例分享

    2023-07-30 03:56:58
  • SpringBoot使用JWT实现登录验证的方法示例

    2023-09-19 19:12:10
  • java实现将结果集封装到List中的方法

    2021-10-27 22:29:45
  • 30条Java代码编写经验分享

    2022-01-09 06:41:42
  • java简单手写版本实现时间轮算法

    2023-01-05 14:50:38
  • Android实现波浪球效果

    2021-09-02 16:07:53
  • 一篇文章带你搞定JAVA反射

    2023-09-18 02:27:51
  • asp之家 软件编程 m.aspxhome.com