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;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • VC++ 6.0 C语言实现俄罗斯方块详细教程

    今天把我之前写的大作业分享一下吧,并教你们如何实现,希望你们看了前面的教程也能自己写一个. 1.要先下载一个 graphics.h 的头文件来绘图. 2.初始化窗口:initgraph(x, y);这是先创建一个窗口的函数,以左上角为(0,0),向右为x轴,向下为y轴,其中x表示长x个单位,y表示宽y个单位. 3.关闭图像窗口:closegraph();结束时用来关闭用的. 4.按任意键继续:getch();这个就和getchar();差不多,为了防止以运行完就关了,这样能停顿一下,他的头文件是

  • C++控制台实现俄罗斯方块游戏

    之前学了些C++的课程,一直想着说编点小游戏,可是MFC又不想学,所以就只能变成控制台的小游戏. 俄罗斯方块一定是很多人小时候玩过的游戏.接下来就说说设计想法. 主要实现,选择游戏的等级,加速下降,不同形状不同颜色,暂停和退出功能. 首先是类的设计. class Box { private: int map[23][12];//画面坐标,记录有方块的点,也是游戏界面 int hotpoint[2];//当前活动的点,所有图形都是以此为基准绘制的 int top;//当前最高位置 int poin

  • linux环境下C++实现俄罗斯方块

    本文实例为大家分享了C++实现俄罗斯方块的具体代码,供大家参考,具体内容如下 本程序的运行环境是linux,用到了多线程.创建了一个用来绘图的线程和一个获取按键的线程.程序中有一些需要改善的地方,比如336-338行定义的全局变量以及声明的对象.本来声明的Block和Table对象应该在main函数里面,然后将这两个对象作为参数传递给线程函数getkey.但是好像只能传递一个对象参数给线程函数.希望高手能够对程序进行改进. ps:由于用到了多线程,而pthread不是linux的默认库,所以编译

  • 使用C++一步步实现俄罗斯方块后续

    一.实验简介 1.1 实验内容 本节实验我们将实现俄罗斯方块主要函数的设计,完成基本功能并运行. 1.2 实验知识点 窗口的绘制 方块类的设计 旋转算法 移动.消除函数 1.3 实验环境 xface 终端 g++ 编译器 ncurses 库 1.4 编译程序 编译命令要加上 -l 选项引入 ncurses 库: g++ main.c -l ncurses 1.5 运行程序 ./a.out 1.6 运行结果 二.实验步骤 2.1 头文件 首先包含头文件以及定义一个交换函数和随机数函数,后面用到(交

  • Linux下用C++实现俄罗斯方块

    本文实例为大家分享了C++实现俄罗斯方块游戏代码,供大家参考,具体内容如下 1.block.c #include <stdio.h> #include <termios.h> #include <unistd.h> #include <stdlib.h> #include <setjmp.h> #include <sys/time.h> #include <string.h> #include "block.h&

  • C++实现俄罗斯方块游戏

    本文实例为大家分享了C++实现俄罗斯方块游戏的具体代码,供大家参考,具体内容如下 使用VS2017编译 思路: 1.打印出游戏地图,也就是设计游戏地图类game_interdace,包括设计游戏开始界面的函数,游戏地图界面的函数,游戏结束界面的函数,以及设计一些辅助函数如(设置光标为目标点,改变颜色,隐藏光标)来完成上述设计. 2.设计方块图形类,包括生成图形,清理图形,图形移动,图形停止的处理,图形的消失(实质是得分)等. #include <iostream> #include<co

  • 使用C++一步步实现俄罗斯方块

    一.实验介绍 1.1 实验内容 本节实验我们进行设计俄罗斯方块前的思路分析,以及介绍ncurses 库的使用方法. 1.2 实验知识点 C++ 编程基础 ncurses 库的使用 俄罗斯方块逻辑设计 1.3 实验环境 xface 终端 g++ 编译器 ncurses 库 1.4 适合人群 本课程难度一般,适合有 C++ 编程基础,对游戏设计.逻辑分析感兴趣的同学. 1.5 代码获取 git clone https://github.com/Gamerchen/game_zero.git 二.开发

  • C++俄罗斯方块游戏 无需图形库的俄罗斯方块

    本文实例为大家分享了C++俄罗斯方块游戏的具体实现代码,供大家参考,具体内容如下. #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<time.h> #include<conio.h> #define MOD 28 #define SIZE_N 19 #define SIZE_M 12 int cur_x,cur_y; int score,mark,next,map

  • C++实现俄罗斯方块

    本文实例为大家分享了C++实现俄罗斯方块的具体代码,供大家参考,具体内容如下 工具:vc++2010,图库:EasyX 先看效果图片 纯手写,没有面向对象思想,看全部源码 #include <stdio.h> #include <graphics.h> #include <time.h> #include <conio.h> #define BLOCK_COUNT 5 #define BLOCK_WIDTH 5 #define BLOCK_HEIGHT 5

  • C++实现俄罗斯方块(windows API)

    本文分享的这些俄罗斯方块代码是我最近放假在家里自己写的,虽然以前有过看别人写的代码,但是那个游戏代码好像不是很全面,因为无法实现全部的方块和实现随机的产生任意方向的方块,现在也基本上是忘光了当时的代码,下面的这些代码是我最近写的,没有参考其他人的代码,真正写俄罗斯方块起来感觉真的是挺难的,关键是在于方块的旋转.当然下面的代码仅仅是一个框架,只能够实现大致上的功能,还不全面,贴出来和大家交流学习. 编译器是code::block  +  MinGW ,感觉CB这个IDE真的是太强大,太棒了,下面的

随机推荐