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++实现俄罗斯方块源码 的全部内容, 来源链接: utcz.com/p/246289.html

回到顶部