使用dev c++
直接上代码:
程序分两部份,其中各类函数声明在.h文件
.h 文件
#ifndef DAY2_H_INCLUDED
#define DAY2_H_INCLUDED
//#include <string.h>
#include <windows.h>
#include <time.h>
#include <stdbool.h>
#include <conio.h>
//定义游戏区域大小
#define col_begin 2
#define col_end 14
#define row_begin 4
#define row_end 26
//定义句柄变量
HANDLE consoleoutpt;
//定义一个结构体Data,存储游戏数据
typedef struct DATA{
unsigned int pool[28];//游戏池深度28 宽度16 包含边界
int x;//当前正在下落方块的左上角坐标
int y;//当前同上
int type[3];//当前,下一个,下下一个方块类型
int revolve[3];//当前,下一个,下下一个方块旋转状态
unsigned int score;//得分
unsigned int erasetotal;//消行总数
unsigned int erasestatistics[4];//不同数量行数一并消去次数统计
unsigned int tetristotal;//方块总数统计
unsigned int tetristatistics[7];//不同形状方块统计
bool dead;//游戏失败,结束
}Data;
//control结构体存储游戏相关控制数据
typedef struct tetriscontrol{
int direction;//移动方向,值为0左移值为1右移
bool revolvedirection;//旋转方向,值为TURE顺时针,FALSE逆时针
bool pause;//暂停
int color[28][16];//游戏池内28行16列每格的颜色
}Control;
//7种方块的每种的旋转状态(4个状态)
static const int tetrisstate[7][4] =
{
{ 0X00F0, 0X2222, 0X00F0, 0X2222 }, // I型
{ 0X0072, 0X0262, 0X0270, 0X0232 }, // T型
{ 0X0223, 0X0074, 0X0622, 0X0170}, // L型
{ 0X0644, 0X0710, 0X0226, 0X0470 }, // J型
{ 0X0360, 0X0462, 0X0360, 0X0462 }, // Z型
{ 0X0630, 0X0264, 0X0630, 0X0264 }, // S型
{ 0X0660, 0X0660, 0X0660, 0X0660 } // O型
};
//游戏池初始状态 游戏池边界每一格设为1,游戏池内全设为0,转化为16进制数
static const unsigned gamepooltetris[28] =
{
0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,
0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,
0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,0XC003,
0XC003,0XC003,0XFFFF,0XFFFF
};
//全角定位函数
void fullposition( short x, short y );
//按键信息提示函数
void prekeyinfo();
//显示游戏池边界
void gamepoolboundary();
//得分信息显示
void scoreinfo( const Data *s_data );
//下一、下下一方块
void nexttetris();
//初始化游戏
void initgame( Data *s_data,Control *control );
//开始游戏
void startgame( Data *s_data,Control *control );
//显示游戏池
void printgamepool( Data *s_data,Control *control );
//初始化方块
void inittetris( Data *s_data );
//插入方块
void inserttetris( Data *s_data );
//设置游戏池方块颜色
void setpoolcolor(const Data *s_data,Control *control );
//显示当前方块
void printcurrenttetris( const Data *s_data,Control *control );
//碰撞检测
bool crashtetris(const Data *s_data );
//向下移动方块
void movedowntetris( Data *s_data,Control *control );
//运行游戏
void rungame( Data *s_data,Control *control );
//水平移动方块
void horizontalmove( Data *s_data,Control *control );
//按键控制方块
void keydowncontrol( Data *s_data,Control *control,int key );
//消行检测
bool checkerase( Data *s_data,Control *control );
//旋转方块
void revolvetetris( Data *s_data,Control *control );
//方块直接落地
void dropdowntetris( Data *s_data,Control *control );
//再来一次
bool playagain();
#endif // DAY2_H_INCLUDED
.c文件
#include <stdio.h>
#include <stdlib.h>
#include "day.h"
int main()
{
Data s_data;
Control control;
//初始化游戏
initgame( &s_data,&control );
//函数调用
//nexttetris( &s_data );
//printgamepool( &s_data,&control );
do
{
prekeyinfo();
gamepoolboundary();
//scoreinfo( &s_data );
//运行游戏
rungame( &s_data,&control );
if( playagain() )//是否再来一次
{
//定位,清屏
SetConsoleTextAttribute( consoleoutpt,0x7 );
system( "cls" );//清屏
//重新开始游戏
startgame( &s_data,&control );
}else
{
break;
}
// Sleep( 40 );
}while(1);
fullposition( 0,0 );
//关闭句柄
CloseHandle( consoleoutpt );
return 0;
}
//初始化游戏
void initgame( Data *s_data,Control *control )
{
//设置控制台标题
SetConsoleTitle( "俄罗斯块块" );
//获取标准输出句柄
consoleoutpt = GetStdHandle( STD_OUTPUT_HANDLE );
//定义光标隐藏变量,并初始化
CONSOLE_CURSOR_INFO info;//定义变量info
//初始化
info.bVisible = FALSE;//光标隐藏
info.dwSize = 1;//光标厚度为1
//设置光标隐藏
SetConsoleCursorInfo( consoleoutpt , &info );
//开始游戏
startgame( s_data,control );
}
//全角定位函数
void fullposition( short x, short y )
{
static COORD pos;
pos.X = 2*x;//之所以左、右移1,像素点总距离变2,是在windows命令提示窗口中,需要两个单位x和
//一个单位y才能组成一个小方块形状,即是两个单位x变化1
pos.Y = y; //下移一,像素点变1
SetConsoleCursorPosition( consoleoutpt,pos );
}
//按键信息提示函数
void prekeyinfo()
{
//设置输出字体 颜色
SetConsoleTextAttribute( consoleoutpt,0xc );
//设置位置信息
fullposition( 33,10 );
printf( "?控制:");
fullposition( 34, 12 );
printf( "□向左移动:← A " );
fullposition( 34, 13 );
printf( "□向右移动:→ D " );
fullposition( 34, 14 );
printf( "□向下移动:↓ S " );
fullposition( 34, 15 );
printf( "□顺时针转:↑ W " );
fullposition( 34, 16 );
printf( "□逆时针转:0" );
fullposition( 34, 17 );
printf( "□直接落地:空格" );
fullposition( 34, 18 );
printf( "□暂停游戏:回车" );
}
//显示游戏池边界
void gamepoolboundary()
{
int y;//打印纵线需要纵坐标y
//确定颜色 0xf0
SetConsoleTextAttribute( consoleoutpt,0XF0 );
for( y=row_begin;y<row_end;y++ )//打印纵线
{
//确定初始打印位置
fullposition( 10,y-3);/*//注意此处/*/
printf("%2s","");
fullposition( 23,y-3);
printf("%2s","");
}
//确定初始打印位置
fullposition( 10,y-3 );
printf("%28s","");
}
//得分信息显示
void scoreinfo( const Data *s_data )
{
//int i;//控制循环,输出数组
static const char Tetris[8] = "ITLJZSO";//定义静态字符数组保存方块类型
//空间全部置零
//memset( s_data,0,sizeof(Data) );
fullposition( 2,2 );
//设置颜色
SetConsoleTextAttribute( consoleoutpt,0XE );
printf( "■得分:%u",s_data->score );
fullposition( 2,6 );
printf( "消行总数:%u",s_data->erasetotal );
fullposition( 2,8 );
printf( "|消去1:%u",s_data->erasestatistics[0] );
fullposition( 2,9 );
printf( "|消去2:%u",s_data->erasestatistics[1] );
fullposition( 2,10 );
printf( "|消去3:%u",s_data->erasestatistics[2] );
fullposition( 2,11 );
printf( "|消去4:%u",s_data->erasestatistics[3] );
fullposition( 2,15 );
printf( "■方块总数:%u",s_data->tetristotal );
fullposition( 2,17 );
printf( "□I形:%u" ,s_data->tetristatistics[0] );
fullposition( 2,18 );
printf( "□T形:%u" ,s_data->tetristatistics[1] );
fullposition( 2,19 );
printf( "□L形:%u" ,s_data->tetristatistics[2] );
fullposition( 2,20 );
printf( "□J形:%u" ,s_data->tetristatistics[3] );
fullposition( 2,21 );
printf( "□Z形:%u" ,s_data->tetristatistics[4] );
fullposition( 2,22 );
printf( "□S形:%u" ,s_data->tetristatistics[5] );
fullposition( 2,23 );
printf( "□O形:%u" ,s_data->tetristatistics[6] );
}
//下一、下下一方块
void nexttetris( Data *s_data )
{
int i;//循环输出
unsigned int tetris;//用于储存下一,下下一方块状态
//提示边框
int y;//输出纵向条
SetConsoleTextAttribute( consoleoutpt,0X0B );
fullposition( 26,1 );
printf( "¥¥¥¥¥¥¥¥¥¥¥" );
//for( y=3;y<7;y++ )
//{
fullposition( 26,2 );
printf( "¥%8s¥%8s¥","","");
fullposition( 26,3 );
printf( "¥%8s¥%8s¥","","");
fullposition( 26,4 );
printf( "¥%8s¥%8s¥","","");
fullposition( 26,5 );
printf( "¥%8s¥%8s¥","","");
fullposition( 26,6 );
printf( "¥¥¥¥¥¥¥¥¥¥¥" );
//}
//输出提示图形
//srand( (unsigned)time(NULL) );
//打印下一个方块
//s_data->type[1] = rand() % 7;//产生0 1 2 3 4 5 6共七个随机数
//s_data->revolve[1] = rand() % 4;//0 1 2 3
//调取方块种类和旋转状态
tetris = tetrisstate[ s_data->type[1] ][ s_data->revolve[1] ];
//设置颜色 高亮
SetConsoleTextAttribute( consoleoutpt,s_data->type[1]|8 );
//打印提示图形
//(34,3)(35,3)(36,3)(37,3)
//(34,4)(35,4)(36,4)(37,4)
//(34,5)(35,5)(36,5)(37,5)
//(34,6)(35,6)(36,6)(37,6)
for( i=0;i<16;i++ )
{
fullposition( 27+i%4,2+i/4 );//定位 注意方法
if( (tetris>>i)&1)
printf( "■" );
else
printf( "%2s","" );
}
//打印下下一个方块 不设置颜色 仅高亮
//s_data->type[2] = rand() % 7;//产生0 1 2 3 4 5 6共七个随机数
//s_data->revolve[2] = rand() % 4;//0 1 2 3
//调取方块种类和旋转状态
tetris = tetrisstate[ s_data->type[2] ][ s_data->revolve[2] ];
SetConsoleTextAttribute( consoleoutpt,0x8 );
for( i=0;i<16;i++ )
{
fullposition( 32+i%4,2+i/4 );
if( (tetris>>i)&1)
printf( "■" );
else
printf( "%2s","" );
}
}
//开始游戏
void startgame( Data *s_data,Control *control )
{
//初始化相关数据:存储游戏数据,游戏操作控制数据
//初始化存储游戏数据
//空间全部置零
memset( s_data,0,sizeof(Data) );
//初始化游戏池
memcpy( s_data->pool,gamepooltetris,sizeof(unsigned int[28]) );
//初始化游戏操作控制数据 内存置0
memset( control,0,sizeof(Control) );
//初始化下一方块
srand( (unsigned)time(NULL) );//生成随机数种子
s_data->type[1] = rand() % 7;//随机下一生成方块形状
s_data->revolve[1] = rand() % 4;//随机生成下下一旋转状态
//初始化下下一方块
s_data->type[2] = rand() % 7;//随机下下一生成方块形状
s_data->revolve[2] = rand() % 4;//随机生成下下一旋转状态
//初始化方块
inittetris( s_data );
//设置游戏池方格颜色
setpoolcolor( s_data,control );
}
#define infullposition(x,y) fullposition(x+9,y-3)
//显示游戏池
void printgamepool( Data *s_data,Control *control )
{
int x,y;//横纵坐标
for( y=row_begin;y<row_end;y++ )
{
//方块入池位置
infullposition(2,y);//11,1
for( x=col_begin;x<col_end;x++ )
{
//设置方块颜色
if( (s_data->pool[y]>>x)&1 )
{ SetConsoleTextAttribute( consoleoutpt,control->color[y][x] );
printf( "■" );
}
else
{
//没有方块显示空白
SetConsoleTextAttribute( consoleoutpt,0 );
printf( "%2s","" );
}
}
}
}
//初始化方块
void inittetris( Data *s_data )
{ //下一方块变为当前方块
s_data->type[0] = s_data->type[1];
s_data->revolve[0] = s_data->revolve[1];
//下下一方块变为下一方块
s_data->type[1] = s_data->type[2];
s_data->revolve[1] = s_data->revolve[2];
//随机生成下下一个方块
s_data->type[2] = rand()%7;
s_data->revolve[2] = rand()%4;
s_data->x = 6; //相对位置
//s_data->y = 4; //绝对位置
s_data->y = 0;
//检测是否有碰撞
if(crashtetris( s_data ))
{
//有碰撞
s_data->dead = true;
}
else
//没有碰撞
//插入方块
inserttetris( s_data );
s_data->tetristotal++ ; //方块总数
s_data->tetristatistics[s_data->type[0]]++ ;//方块种类统计
//显示下一方块
nexttetris( s_data );
//显示得分信息
scoreinfo( s_data );
//插入方块
// inserttetris( s_data );
//设置游戏池方块颜色
//setpoolcolor( s_data,control );
}
//插入方块
void inserttetris( Data *s_data )
{
//当前方块tetrisstate[ s_data->type[0] ][ s_data->revovle[0]]
unsigned int tetris = tetrisstate[ s_data->type[0] ][ s_data->revolve[0]];
s_data->pool[s_data->y+0] |= (((tetris>>0x0)&0x000f)<<s_data->x);
s_data->pool[s_data->y+1] |= (((tetris>>0x4)&0x000f)<<s_data->x);
s_data->pool[s_data->y+2] |= (((tetris>>0x8)&0x000f)<<s_data->x);
s_data->pool[s_data->y+3] |= (((tetris>>0xc)&0x000f)<<s_data->x);
}
//设置游戏池方块颜色
void setpoolcolor( const Data *s_data,Control *control )
{
//显示颜色前需要先判断是否有方块,有再显示颜色
//此函数仅设置颜色,无需清楚方块
//当初始化方块时或移动方块时调用
int i;//控制循环,便于定位
int x,y;//待设置的行列
//当前的方块
unsigned int tetris;
tetris = tetrisstate[ s_data->type[0] ][ s_data->revolve[0]];
for( i=0;i<16;i++ )
{
y = s_data->y+i/4;
if( y>row_end )
break;
x = s_data->x+i%4;
if( (tetris>>i)&1 )
control->color[y][x] = s_data->type[0]|8;
}
}
//显示当前方块
void printcurrenttetris( const Data *s_data,Control *control )
{
//方块不会向上移动,故不需要向下扩展
int x,y;//用于扩展方块移动后的行数//记录方块移动后的坐标
//若方块向左/右移动一格,则要向右或左扩展一格,即横向扩展两格
if( s_data->y>row_begin )
y = s_data->y-1;
else
y = row_begin;
for( ;y<row_end && y<s_data->y+4;y++ )
{
if( s_data->x>col_begin )
x = s_data->x-1;
else
x = col_begin;
for( ;x<col_end && x<s_data->x+5;x++ )
{
//定位到游戏池中
infullposition( x,y );
//游戏池中的方块的定点确定
if( (s_data->pool[y]>>x)&1)
{
//设置颜色
SetConsoleTextAttribute( consoleoutpt,control->color[y][x] );
printf( "■" );
}
else
{
SetConsoleTextAttribute( consoleoutpt,0 );
printf( "%2s","" );
}
}
}
}
//移除方块
void movetetris( Data *s_data )
{
//当前方块
unsigned int tetris = tetrisstate[s_data->type[0]][s_data->revolve[0]];
//移除当前方块即将当前方块的16位二进制数取反,再与原游戏池内该位置上
//方块与运算,即可全部置零
s_data->pool[ s_data->y+0 ] &= ~(((tetris>>0x0)&0x000f)<<s_data->x);
s_data->pool[ s_data->y+1 ] &= ~(((tetris>>0x4)&0x000f)<<s_data->x);
s_data->pool[ s_data->y+2 ] &= ~(((tetris>>0x8)&0x000f)<<s_data->x);
s_data->pool[ s_data->y+3 ] &= ~(((tetris>>0xc)&0x000f)<<s_data->x);
}
//碰撞检测
bool crashtetris( const Data *s_data )
{
//当前方块
unsigned int tetris = tetrisstate[s_data->type[0]][s_data->revolve[0]];
//游戏池内的方块
unsigned int dest = 0;//初始化
//获取当前方块在游戏池中的位置
//游戏池中x,y处小方格信息,按低位到高位存放在16位二进制的无符号数中
dest |= (((s_data->pool[ s_data->y+0 ]>>s_data->x)<<0x0)&0x000f );//与第0行做按位或运算(有一为真即为真)
dest |= (((s_data->pool[ s_data->y+1 ]>>s_data->x)<<0x4)&0x00f0 );
dest |= (((s_data->pool[ s_data->y+2 ]>>s_data->x)<<0x8)&0x0f00 );
dest |= (((s_data->pool[ s_data->y+3 ]>>s_data->x)<<0xc)&0xf000 );
//上面的操作是将游戏池中的方格型二进制码先右移到最右端,再左移到横条行二进制码上应有的位置处
//dest 是通过或预算得到的16位二进制码
//每一行游戏池1100 0000 0000 0000 0011
//最后一行1111 1111 1111 1111 1111
//检测碰撞就是将游戏池方块转化成16位二进制数,再与当前方块进行位于运算
//若运算结果存在重叠,即结果只要有一不为0,则产生碰撞,不可移动
return ((dest&tetris) != 0);
}
//向下移动方块
void movedowntetris( Data *s_data,Control *control )
{
int y = s_data->y;//用于记录原行位置
//调用移除函数,移除当前方块
movetetris( s_data );
//向下移动
s_data->y++ ;
//检测是否有碰撞
//若有碰撞,则要恢复到下落前位置,插入当前方块,由于位置没变,无需设置颜色
if( crashtetris( s_data ) )//检测碰撞
{
//恢复到原行位置
s_data->y = y;
//插入当前方块,位置不变,不设置颜色
inserttetris( s_data );
//初始化方块
//inittetris( s_data );
//显示下一个方块
//nexttetris( s_data );
if( checkerase( s_data,control))
//刷新游戏池,显示游戏池
printgamepool( s_data,control );
}
else
{
//没有碰撞,插入方块,设置颜色,显示当前方块
inserttetris( s_data );
setpoolcolor( s_data,control );
printcurrenttetris( s_data,control );
}
}
//运行游戏
void rungame( Data *s_data,Control *control )
{
//显示游戏池
//printgamepool( s_data,control );
//向下移动方块
//movedowntetris( s_data,control );
//clock_t代表长整型,clock_t用来保存时间的数据类型
clock_t clocklast,clocknow;
clocklast = clock();//计时
//显示当前游戏池
printgamepool( s_data,control );
while( !s_data->dead )//游戏是否失败,未失败执行
{
while( kbhit())//有按键按下
{
keydowncontrol( s_data,control,getch() );//getch()接受不回显
}
if(!control->pause)//未暂停
{
clocknow = clock();
if( clocknow-clocklast >= 0.3*1000 )//CLOCK_PER_SEC=1000
{
//向下移动方块
movedowntetris( s_data,control );
clocklast = clocknow;
}
}
}
}
//水平移动方块
void horizontalmove( Data *s_data,Control *control )
{
//记录原列位置
int x = s_data->x;
//移除当前方块
movetetris( s_data );
//左移x--,右移x++
control->direction == 0?s_data->x--:s_data->x++;
//检测碰撞
if( crashtetris( s_data ))
{
//恢复原列位置
s_data->x = x;
//插入方块
inserttetris( s_data );
}
else //未检测到碰撞
{
inserttetris( s_data );
setpoolcolor( s_data,control );
printcurrenttetris( s_data,control );
}
}
//按键控制方块
void keydowncontrol( Data *s_data,Control *control,int key )
{
//回车 暂停/解除暂停
if( key == 13 )
{
control->pause = !control->pause;
}
if( control->pause )
return ;//终止语句作用
switch( key )
{
case 75:
case 'a':
case 'A':
control->direction = 0; //向左移动
horizontalmove( s_data,control );break;
case 77:
case 'd':
case 'D':
control->direction = 1;//右移
horizontalmove( s_data,control );break;
case 'w':
case 'W':
case '8': //顺时针旋转
control->direction = true;
revolvetetris( s_data,control );break;
case '0': //逆时针旋转
control->direction = false;
revolvetetris( s_data,control );break;
case 32: //直接落地
dropdowntetris( s_data,control );break;
case 80:
case 's':
case 'S':
movedowntetris( s_data,control );break;
default:
break;
}
}
//消行检测
bool checkerase( Data *s_data,Control *control )
{
int count = 0;//消行数
static const score[5] = { 0,10,30,90,150 };//最多同时消去四行
//从下往上检测
int y = s_data->y + 3;
while( y>=s_data->y+0 )
{
//有效区域,且已填满
if( y<row_end && s_data->pool[y] == 0xffff )
{
count++;
//消去一行方块
//数据内存要移动 memove
memmove( s_data->pool+1,s_data->pool,sizeof(unsigned int)*y );
//颜色数据地移动
memmove( control->color[1],control->color[0],sizeof(int[16])*y );
}
else
y--;
}
s_data->erasetotal += count;
s_data->score += score[ count ];
if( count>0 )
s_data->erasestatistics[ count-1 ]++;
//初始化方块
inittetris( s_data );
//设置颜色
setpoolcolor( s_data,control );
return( count>0 );
}
//旋转方块
void revolvetetris( Data *s_data,Control *control )
{
//记录原旋转状态
int ori = s_data->revolve[0];
//移除当前方块
movetetris( s_data );
//顺逆时针旋转状态判定
s_data->revolve[0] = control->direction?( ori+1 )%4:( ori+3 )%4;
//检测到碰撞,方块恢复到旋转前状态,插入方块
//若没有,在当前位置放入方块,设置颜色,显示当前方块
if( crashtetris( s_data ) )//检测到碰撞
{
//恢复原旋转状态
s_data->revolve[0] = ori;
//放入当前方块
inserttetris( s_data );
}
else//未检测到碰撞
{
//插入方块
inserttetris( s_data );
//设置颜色
setpoolcolor( s_data,control );
//显示当前方块
printcurrenttetris( s_data,control );
}
}
//方块直接落地
void dropdowntetris( Data *s_data,Control *control )
{
//移走当前方块
movetetris( s_data );
//检测是否有碰撞
for( ;s_data->y<row_end;s_data->y++ )
{
if( crashtetris( s_data ) )
{
break;
}
}
s_data->y--;//上移一格没碰撞
//插入方块,设置颜色
//插入方块
inserttetris( s_data );
//设置颜色
setpoolcolor( s_data,control );
//检测消行
checkerase( s_data,control );
//显示游戏池
printgamepool( s_data,control );
}
//再来一次
bool playagain()
{
int ch;//用于接收按键 y n
//定位设置结束文字
SetConsoleTextAttribute( consoleoutpt,0xf0 );
fullposition( 15,10 );
printf( "游戏结束!" );
fullposition( 13,11 );
printf( "按Y重玩,按N结束" );
//判断是否重来一次
//按Y重新开始
//按N结束游戏
do
{
ch = getch();
if( ch=='y' | ch=='Y' )
return true;
else if( ch=='n' | ch=='N' )
return false;
}while(1);
}
``