俄罗斯方块c语言详解
俄罗斯方块最详解
做每一件事前,都会有一个粗略的构想。编程更应该这样,现在先说一些大的、粗略的东西。 ****************************************************************************************
****************************************************************************************
目录:
?屏幕的划分
?图形显示
?三种坐标。 绝对坐标、相对坐标、左上角坐标
?方块的构造
?动画效果
?键盘控制
?判断方块碰撞
?消行
?变形
?关于菜单的制作
?附录(完整的源程序)
****************************************************************************************
****************************************************************************************
1、屏幕的划分
将整个屏幕划分成四部分:a、一个没盖的杯子;b、一个不断下落4*4数组的盒子;c、一个给 预览下一个方块4*4数组的盒子;d、提示信息。由于提示信息比较简单,这里只讨论前三样。
没盖的杯子:
即平时说玩这款游戏时,下落方块不可超出的那个边界,下落的方块从这个“杯口”的上方往下下落,方块只在“杯子”里移动、变形、停止。
游戏空间指的是整个游戏主要的界面(呵呵,其实就是所说的“杯子”)。实际上是一个宽10格子、高20格子的 游戏板。用一个全局数组GameSpace[22][12]表示。表示的时候:GameSpace[x][y]为1时表示游戏板上(x,y)这个位置上已经有方块占着了,GameSpace[x][y]为0表示游戏板上这位置还空着。为了便于判
断形状的移动是否到边、到底,初始的时候在游戏板的两边各加一列,在游戏板的下面加一行,全 部填上
1,表示不能移出界。即GameSpace[x][0],GameSpace[x][11](其中x从0到20)初始都为1,GameSpac
e[20][y](其中y从0到11)初始都为1。
0 1 2 3 4 5 6 7 8 910
0????????????
1????????????
2????????????
3????????????
4????????????
5????????????
6????????????
7????????????
8????????????
9????????????
10????????????
11????????????
12????????????
13????????????
14????????????
15????????????
16????????????
17????????????
18????????????
19????????????
20????????????
下落的4*4盒子:
即7种
的方块形状,如我认为比较经典的测试方块“7字形”
可看作:
0 1 2 3
0????
{{0,0,0,0},
1???? 用数组表示则是 {0,1,1,0},
2???? {0,0,1,0},
3???? {0,0,1,0}}
预览4*4数组盒子:
即玩这款游戏时,给看下一个方块是什么样的窗口。
这三样东西可以这样联系起来:一个不断下落的盒子由杯子的上方下落到杯子底部,之后将预览盒子的东西放到下落的4*4盒子中,如此循环反复……
2、图形显示
Tc2.0中有两种显示模式,一种是我们所熟知的字符模式,另一种是图形模式。在字符模式下只能显式字符,如ASCII字符。一般是显示25
行,每行80个字符。程序缺省的是字符模式。在字符模式下不能显式图形和进行绘图操作。要想进行图形显示和绘图操作,必须切换到图形模
式下。
Tc2.0中用initgraph()函数可以切换到图形模式,用closegraph()可以从图形模式切换回字符模式。initgraph()和closegraph()都是图形
函数,使用图形函数必须包括头文件"graphics.h"。
void far initgraph(int far *graphdriver,int far *graphmode,char far *pathtodriver);graphdriver是上涨指向图形驱动序号变量的指针;graphmode是在graphdriver选定后,指向图形显示模式序号变量的指针。pathtodriver表示存放图形驱动文件的路径。 特别值得一提的是驱动文件路径的写法,由于有转义字符,如bgi文件夹(一般这个文件夹就在Tc文件夹目录下,这个说明文档黙认bgi在c:\,后面举的例子亦然)在c:\,写的时候很多人写成"c:\bgi",少了个\,应写成"c:\\bgi",切记。
此外,我还强烈建议借一本叫《c作图与c汉字技术》的
,这是我见过介绍c 绘图方面最全面的书了,
为了节省时间,可以直接看这几个函数,这些函数均包含在头文件#include
中 void line( int x,int y,int xx,int yy); /*从(x,y)到(xx,yy)处以setcolor()决定的颜色画直线*/
void setlinestyle(int linestyle , unsigned upattern ,int thickness );
/*linestyle 变化范围为 0 ~ 4,也可以是大写的英文*/
/*分别表示实线,点线,中心线,点画线,用户自定义线*/
/*upattern处一般写0*/
/*thickness只有1、3两个值,分别表示一个像素宽,三个像素宽*/
void setcolor (int color ); /*决定前景颜色,color 变化范围为 0 ~ 15也可以是大写的英文*/
void setbkcolor(int color); /*决定背景颜色,color 变化范围为 0 ~ 15也可以是大写的英文*/
void rectangle(int x,int y,int xx,int yy);/*以(x,y)为左上角,以(xx,yy)为右下角画矩形*/
void bar(int x,int y,int xx,int yy); /*以(x,y)为左上角,以(xx,yy)为右下角画条形*/
void bar3d(int x,int y,int xx,int yy,int depth,int topflag);
/*以(x,y)为左上角,以(xx,yy)为右下角画立体条形*/
/*depth决定了三维直方图的长度*/
/*当topflag非0时,画出三维顶,否则不画出三维顶*/
void setfillstyle(int pattern,int color);/*pattern变化范围为 0 ~ 12也可以是大写的英文*/
/*color 变化范围为 0 ~ 15也可以是大写的英文*/
char* itoa(int n,char* str,int radix); /*n是要转的整型变量 */
/*str用来存的字符串*/
/*radix是按什么进制转化*/
/*能将整型数字转成字符型数字,结合outtextxy()*/
/*可将得分、菜单上的数值等简单地显示在屏幕上*/
void settextjustify(int horiz,int vert); /*horiz变化范围为 0 ~ 2,也可以是大写的英文,分别代表左对齐,字符串中心对齐,右对齐*/ /*vert 变化范围为 0 ~ 2,也可以是大写的英文,分别代表底部对齐,中心对齐,顶部对齐*/
void settextstyle(int font,int direction,int charsize);
/*font变化范围为 0 ~ 12也可以是大写的英文*/
/*direction只能是0、1,也可以是大写的英文,0、1分别代表水平输出,垂直输出*/ /*charsize变化范围为 1 ~ 10也可以是大写的英文,数值越大,字体越大*/
void outtextxy(int x,int y,char* str); /*按settextjustify()决定的对齐方式*/
/*以setcolor()决定的颜色,在(x,y)附近输出字符串*/
(在第10点的小程序中有以上的大部分函数的实现)
3、三种坐标。 绝对坐标、相对坐标、左上角坐标
绝对坐标:
图形屏幕其实是个y轴的正方向朝下,原点在屏幕最左上角的直角坐标系,vga模式下的一般分辨率为640*480,这样就把屏幕横向、纵向分别平分640等分、480等分,横、纵坐标变化范围分别为0 ~ 639,0 ~ 479,每个像素点占用一个1的长度。我们说的绝对坐标就是以屏幕原点为相对点的切切实实的坐标。
相对坐标:
即以屏幕上某个绝对坐标为相对点(或把它看成原点),在这个基础上再计算的坐标。
如以(0,0)为相对点,(1,1)的绝对坐标为(1,1),相对坐标为(1,1);以(2,2)相对点,(1,1)的绝对坐标为(1,1),相对坐标为(-1,-1)。
左上角坐标:
特别提出这个,是因为在俄罗斯方块中解决数组与屏幕连接的关健所在。如画个小正方形时用函数rectangle(x,y,x+单位长度,y+单位长度)
(其中(x,y)为这个正方形的左上角坐标,单位长度可以是10,16,我就取了16,这样在640*480中便变成了 (640/16)*(480/16) = 40*30 ),又如前面说的GameSpace[21][12]可以这样化散为整成GameSpace[(x-相对原点的x)/单位长度][(y-相对原点的y)/单位长度]。
4、方块的构造
为了简单处理,我选择了数组构造方块,而不是坐标描点,但这样浪费空间。在要画的地方赋1,不画的地方赋0,如长条可表示为
0 1 2 3
0???? {{0,1,0,0}, 到了这里,我们应 ____
1???? 用数组表示则是 {0,1,0,0}, 已深入的将各种方
??
2???? {0,1,0,0}, 块看成由四个小正 | | 看成 ?
3???? {0,1,0,0}} 形组成,如 |_| ?
为突出立体感,可以在画了一个小正方形后,再用line()函数在这个小正方形的左上角坐标附近画横、纵两条白线,再在小正方形的右侧画一条黒线,如图是放大的小正方形
________
| ______ | (其中,里面的横线为白,里面左侧的纵线为白,里面右侧的纵线为黒) || ||
|| ||
|| ||
|________|
在我的程序里就没搞立体感,只为体现算法而已。
现在,整个屏幕在我们眼里应达到这样理性的看法:
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
??????????????????????????????? 其中每个小正方形放大看为 ???????????????????????????????
??????????????????????????????? (x,y)______ ??????????????????????????????? | | ??????????????????????????????? | | ??????????????????????????????? | | ??????????????????????????????? |_______| ???????????????????????????????
??????????????????????????????? (x,y)为左上角坐标 ???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
???????????????????????????????
5、动画效果
说穿了就是先画一个图形显示出来,延时一小段时间认人看清楚后,再将显示的图形在原来的位置涂黒(或涂成背景颜色)。其中延时函数为delay (数值),包含在头文件#include 中。 括号中的数值越大,延时越长,数值最大为65535,可配合for 循环让delay()超过65535这个数,下面给出的小程序是熟悉这个函数的.功能是一个红色的正方形斜斜地移动
/*animation.c*/
#include
#include /*delay()函数的头文件*/
void main()
{
int gr = DETECT , gm = 0;
int x = 50 , y = 50;
int i , j;
initgraph( &gr , &gm , "c:\\bgi" ) ; for( i = 0 ; i < 8 ; ++ i , x += 50 , y += 50) {
setfillstyle( SOLID_FILL , RED ); bar( x , y , x + 50 , y + 50 ); for (j = 0 ; j < 30 ; ++ j)
delay( 10000 );
/*不同的编译器显示的快慢不同,10000这个数值可根据不同的编译器改*/ setfillstyle( SOLID_FILL , BLACK ); bar( x , y , x + 50 , y + 50 ); }
}
6、键盘控制
键盘上的每个键都有自己的键盘码,为了得到想要的,下面介绍一个函数 在Tc2.0中有一个处理键盘输入的函数bioskey();
int bioskey(int cmd);
当cmd为1时,bioskey()检测是否有键按下。没有键按下时返回0;有键按下时返回键盘码(任何键盘
码都不为0),但此时并不将检测到的按 键码从键盘缓冲队列中清除。
当cmd为0时,bioskey()返回键盘缓冲队列中的键盘码,并将此按键码从键盘缓冲队列中清除。如果
键盘缓冲队列为空,则一直等到有键按下,才将得到的键盘码返回。
Escape键的按键码为0x11b,下面的小程序可以获取按键的键盘码 /*keyboard_code.c*/
#include
#include
void main()
{
int key ;
while ( 1 )
{
key = bi
oskey( 0 ); /* wait for a keystrike */
printf ( "0x%x\n" , key );
if ( key == 0x11b )
break; /* Escape */
}
}
常用按键的按键码如下:
#define LEFT 0x4b00 #define RIGHT 0x4d00 #define DOWN 0x5000 #define UP 0x4800 #define HOME 0x4700 #define END 0x4f00 #define SPACE 0x3920 #define ESC 0x011b #define ENTER 0x1c0d
这样只要事先用#define 这个宏定义,将要用的键盘码定义好,然后用switch语句分支各个键的功能即
可。这里多加说明一个函数kbhit(),其头文件为#include .当没有按键时,返回0值;有按键时,返
回非0值,结合while循环,就可实现键盘控制了。下面的函数功能是按上下左右键,屏幕上的正方形跟
着做出相应地移动,按逃跑键退出
/*control.c*/
#include /*delay()函数的头文件*/ #include #include #include
#define LEFT 0x4b00 #define RIGHT 0x4d00 #define DOWN 0x5000 #define UP 0x4800 #define ESC 0x011b
void main()
{
int gr = DETECT , gm = 0;
int x = 100 , y = 100;
int i , key;
initgraph( &gr , &gm , "c:\\bgi" ) ; while ( 1 )
{
while ( !kbhit() ) /*前面刚说完,不记得了,往上翻翻*/ {
setfillstyle( SOLID_FILL , RED );
bar( x , y , x + 50 , y + 50 );
for (i = 0 ; i < 30 ; ++ i)
delay( 10000 );
setfillstyle( SOLID_FILL , BLACK );
bar( x , y , x + 50 , y + 50 ); }
key = bioskey( 0 );
/*前面刚说完,不记得了,往上翻翻*/
switch ( key )
{
case LEFT:
x -= 50;
break;
case RIGHT:
x += 50;
break;
case UP:
y -= 50;
break;
case DOWN:
y += 50;
break;
case ESC:
exit( 0 );
}
}
}
7、判断方块碰撞(即方块是否还能下落)
用一个带有返回值的函数,若碰撞则说明不能下落,返回1;反则说明没有碰撞,返回0.
具体一点就是整个4*4方块数组下落一个单位长度,与游戏空间数组(即前面说的“无盖的杯子”)有重叠的1,则在当前位置4*4数组是1的地方赋值给游戏空间对应的数组元素,表示停止下落,并画有1 的地方。对于左移、右移一个单位长度有重叠的1 ,则不允许左移、右移,继续自然下落。
8、消行
总的想法是先认为每一行都是满1的,从游戏空间的数组由上到下扫描,一旦测试到某一行中某个列元素为0,则认为这一行没满,跳出这行的扫描循环,进入下一行的扫描。
若扫描完某一行的元素都没有发现0,则以这行以上的每一行完完整整地将上一行的元素赋值给下一行,这个过程以由下到上进行,然后将整个游空间(GameSpace)画黒,再在GameSpace[21][12]中有1的地方画小正方形,如示例
A 0 0 0 1 0 0 从A行到D行扫描, A 0 0 0 0 0 0 B 1 1 0 1 0 1 扫描后发现C行是满的, B 0 0 0 1 0 0 C 1 1 1 1 1 1 则从B行起B行赋给C行,A行赋给B行, C 1 1 0 1 0 1 D 0 1 1 1 1 1 最后在最行A行赋全0 ,变成 D 0 1 1 1 1 1
对应的 ? 对应的 ?
图形 ? 图形 ?
A?????? A??????
B?????? B??????
C?????? C??????
D?????? D??????
读者可能会问这样不是每次都消一行而己,若要一次消两行以上怎么办(如恰好有一长条一次能消4行),这个简单,因为即使一次消多行也是建立在一行一行消的基础上的。把消行函数再细化成两部分,一部分为为扫描哪行满行并返回这个满行的行号的值,另一部分就消这个满行行号的行,用一个while 循环,判断条件为 “扫描函数(在我的程序里是fullrow())!= 0”,形如
while(fullrow() != 0 )
{ 其中flrw是个整型变量,用来哪行满行,而clearrow(flrw)
flrw=fullrow(); 则消指定的行。由于计算速度快,人眼看多次消行,就像一
clearrow( flrw ); 次搞掂的样子
}
9、极度重要的变形函数
分两部分讲 a、怎么变
b、变后插入到已有方块里的处理(即卡位处理),情况如图:
??????? ???????
??????? ???????
??????? ???????
??????? 变形后 ???????
??????? 有可能 ???????
??????? ???????
??????? ???????
a、怎么变。
其实我能想到两种方法,一种是把每个方块各种变形的样子都用结构体定义好(这样有19种),形成一个封闭的链表,变形一次指针指向下一种形状;
另一种是旋转90度,也是我选择的方法。
后一种是一种通法,但执行效率不比前一种方法快。下面针对旋转90度详细讲解。
网上说的旋转90度只是泛泛地说而已,经过毛概课上偷偷画方格,折纸,终于想到了一种主对角线对折,再翻来达到90度旋转的方法。请拿上一张纸,标上正面,反面,|、||,跟着图示做:
正面 反面 正面
________ _______ _______ |\ | |\ | __|__ | /| | \ || | 沿对角线
; | \ —| 再沿中轴 | | | | — / |
| \ | 对折后 | \ | 翻过来 | | | 得 | / |
| \ | | \ | |__|__| | / | | | \ | | 〓 \ | | | / 〓 |
|_____\| |_____\| |/_____|
图(a) 图(b) 图(c)
对比图(a),图(c),则发现原来坚的东西变横了,也实现了顺时针转了90度。概念是这样,接下来是4*4数组怎么对折,只要仔细研究发现主对角线上的元素不必动,只要
0 1 2 3
0???? 0-1号与1-0号 1-2号与2-1号 2-3与3-2号
1???? 0-2号与2-0号 1-3号与3-1号 互相交换值,
2???? 0-3号与3-0号
3????
再沿中轴互相交换值
0 1|2 3
0??|?? 0-0号与0-3号 0-1号与0-2号
1??|?? 1-0号与1-3号 1-1号与1-2号
2??|?? 2-0号与2-3号 2-1号与2-2号
3??|?? 3-0号与3-3号 3-1号与3-2号
|
这样就完成了第一部分。
b、变后插入到已有方块里的处理 (即卡位处理)
整体思路是先执行上面的第一部分,但不显示出来,然后判断是不是插到了已有方块中,
若可以左移或右移之后就可以的,我们就人为地移动它,后显示。
若左移或右移,又插入了右侧或左侧的方块,则逆序地执行第一部分,先执行中轴交换值,再对角线交换值,变回原来的形状,认为不具备变形的条件,后原形显示。
对于第一种情况,为分析情况,我们讨论每行的0、1、2、3号位置(注意这里的0、1、2、3号位置是4*4数组的每一行的,须要用循环一行行扫描),由于1、2号位置情况复杂且出现的机会少,故舍弃。因为(注意这里为了更好地说明,把长条????变成????,实际作图时还是????)
0 1 2 3 0 1 2 3 0 1 2 3
???? ???? ????
???? ? ???? ? ??????
???? ? ???? ? ??????
???? ? 变形后 ???? ? 这时1号卡 ??????
? ? 有可能 ?
? 右移 两位 ? ?
? ? ? ? ? ?
?????? ?????? ??????
又如
0 1 2 3 0 1 2 3 0 1 2 3
???? ???? ????
???? ? ???? ? ? ????
???? ? ???? ? ? ????
???? ? 变形后 ???? ? 这时1号卡 ? ???? 这时2号又卡了
? ? 有可能 ? ? 右移两位 ? ?
? ? ? ? ? ?
?????
????? ?????
0 1 2 3
????
?????
?????
若右移一位 ????? 这时3号又卡了
? ?
? ?
?????
由于1、2 号位的情况只会在长条(因为其它的方块长度最长为3)时出现,虽然存在移两位可行的情况,但实在是少,如要都完善的编写上,显得麻烦,故舍弃。
现在重点讨论0、3号位置。
0号位置由于变形后,有可能因为左侧位置不够而不能变形,只须稍微右移一个单位长度就可以了,如
0 1 2 3 0 1 2 3 0 1 2 3
???? ???? ?????
???? ???? ?????
???? 变形后 ???? 移动后 ?????
???? ???? ?????
? ? ?
? ? ?
而人为地移动后,对应的1,2,3号位置又有可能插入了已有的方块中,如
0 1 2 3 0 1 2 3 0 1 2 3
???? ???? ????
????
; ???? ?????
???? 变形后 ???? 右移一 ????? 1号又卡了
???? ???? 位后 ?????
? ? ? ? ? ?
? ? ? ? ? ?
0 1 2 3 0 1 2 3 0 1 2 3
???? ???? ????
???? ???? ?????
???? 变形后 ???? 右移一 ????? 2号又卡了
???? ???? 位后 ?????
? ? ? ? ? ?
? ? ? ? ? ?
0 1 2 3 0 1 2 3 0 1 2 3
????? ????
????
????? ????? ?????
????? 变形后 ????? 右移一 ????? 3号又卡了
????? ????? 位后 ?????
? ? ? ? ? ?
? ? ? ? ? ?
对于这些情况我们无奈,只能逆序变回原形。
3号位置的情况亦然,只不过是把左移变成右移,移动后讨论的是0,1,2号位置。
10、关于菜单的制作
我认为不会很难。概念上是把要显示的动态数值改变后,涂黒原来的地方,将新的数值在原来 的地方再
显示即可。特别值得一提的是,这之中可能用到的itoa()函数,头文件#include 。也许有人会问,
为什么要用itoa()函数,这是因为在图形模式下,用printf()输出只能是8*8点阵,白色字体。若想搞点比
较艺术的字体,输出的字体大点的,就看下面的函数是怎么实现的。 功能是按一下回车,数值增大100,按ESC退出。
/*itoa.c*/
#include
#include
#include
#include
#define ESC 0x011b
#define ENTER 0x1c0D
main()
{
int gr = DETECT , gm = 0;
int key , value = 100 ; /*value是要转的整型变量*/
char str[10]; /*用来存的字符串*/
initgraph( &gr , &gm , "c:\\bgi" ) ; setbkcolor( BLACK ); /*设置背景颜色*/
/*对应的数值形式是setbkcolor( 0 )*/
setfillstyle( SOLID_FILL,BLACK );/*设置bar函数填充样式,这里为实心、黑色*/
/*对应的数值形式是setfillstyle( 1 , 0 )*/
settextjustify( LEFT_TEXT , CENTER_TEXT );/*设置文本输出的样式,*/
/*这里是左对齐,中心对齐*/
/*对应的数值形式是settextjustify( 0 , 1)*/
setcolor( CYAN ); /*设置输出文本的颜色,这里是青色*/
/*对应的数值形式是setcolor( 3 )
settextstyle ( SANS_SERIF_FONT , HORIZ_DIR , 3 );/*设置文本的字型,*/
/*这里是无衬线笔画字体,水平输出,3是24*24点阵*/
/*对应的数值形式是settextstyle ( 3 , 0 , 3 )
itoa( value , str , 10 ); /*前一个是要转成字符的整型变量名,*/
/*后一个是转成字符后用来存的字符串首址*/
/*itoa中的10是按10进制转化*/
while (1)
{
while ( !kbhit() )
{
outtextxy(320,240,str);
}
key=bioskey( 0 );
switch ( key )
{
case ENTER:
bar(300,200,400,300);
value += 100 ;
itoa( value , str , 10 );
break;
case ESC:
exit( 0 );
}
}
}
好了,看到这相信你对这款游戏的制作已有所把握。
你可以先搞个没有键盘控制垂直下落的单一的测试方块,然后把向左、向右、向下加上,再把无盖的杯子画出来,让其有个范围,顺道搞掂碰撞,之后把消行功能加上,又将单一的测试方块变成7种随机的方块出现且最难搞的变形函数也完成了,补上记分功能,把菜单给做了,最后加上了自己的新功能。记住千里之行始于足下,聚沙成塔。我们有的是朩头、钉子、石头等各种零件,却不能忘记心中那宏伟的城堡!现在就一点点来吧。
*******************************************************************************
*******************************************************************************
实现的源程序(菜单上好像还有小bugs,不影响大体)
/*Russian_Diamond.c*/
#include
#include /*主要应用于作图*/
#include /*主要应用于键盘*/
#include
#include /*主要应用于产生随即数*/
#include /*主要应用于延时*/
/**************************
第一个界面的主要函数
***************************/ void hello(); /*第一个界面的入口函数*/ void interface(); /*输出不许改变的文字信息 */ void activemenu(); /*选择菜单的动态显示函数*/ void menu_up(); /*选择菜单的向上函数*/ void menu_down(); /*选择菜单的向下函数*/ void menu_left(); /*选择菜单的减少速度值,难度值的函数*/ void menu_right(); /*选择菜单的增大速度值,难度值的函数*/ void menu_enter(); /*确定选项的函数*/ void menu_escape(); /*选择退出的函数*/ /************************ 第二个界面的主要函数
************************/ void game(); /*选择开始游戏的入口函数*/ void gamestart(); /*第一次产生随机数*/ void initgamespace(); /*初始化游戏空间*/ void drawwal(); /*画边框与预览框*/
void drawsqa(); /*画下落方块*/
void showtext(); /*显示游戏过程中提示信息*/ void showvalue(); /*显示游戏过程中数值信息*/ void up(); /*游戏中变形函数*/
void down(); /*向下函数*/
void left(); /*向左函数*/
void right(); /*向右函数*/
void pause(); /*暂停函数*/
void escape(); /*显示退出菜单的函数*/ void speed_up(); /*加速函数*/
void speed_down(); /*减速函数*/
void wipe(); /*使用橡皮函数*/
void if_end(); /*判断游戏结束的函数*/
void preview(int ); /*预览框函数*/
void clearrow(int ); /*消行函数*/
void showscore(int ); /*显示分数的函数*/
void id(int ); /*为了能按照预览框的形状开始掉落而写的函数*/
int crash(); /*判断碰撞的函数*/
int fullrow(); /*侦测满行的函数*/
int next();
/*产生随机数的函数*/
/************************ 退出界面
***********************/ void goodbye(); /*退出函数的入口*/
/************************ 第一个界面的主要参数值
************************/ int start_color=14; /*说明当前选项到了确定选择的各个参数准备开始游戏的地方*/
int speed_color=3; /*说明当前选项到了选择速度的speed的地方*/ int difficulty_color=3; /*说明当前选项到了选择难度的difficulty的地方*/ int end_color=3; /*说明当前选项到了选择退出的end的地方*/ int menu_color=0; /*控制当前选项到了哪的重要判断标记*/ /************************ 游戏主体的各个全局变量
************************/
#define LEFT 0x4b00
#define RIGHT 0x4d00
#define DOWN 0x5000
#define UP 0x4800
#define ESC 0x011b
#define ENTER 0x1c0D
#define SPACE 0x3920
#define W 0x1177
#define S 0x1f73
#define D 0x2064
#define UNIT 16 /*单位长度*/
#define spaLeft 188 /*游戏框的左上角*/
#define spaTop 34 /*由于作品上交时间急又由于出现了bug,已改动故现在已不是真正的左上角的数值
了,只能是概念上有个游戏框的左上角*/
#define staLeft 252 /*方块出现的初始地方*/
#define staTop 34 /*如上面说的那样,出现bug,而中间画图判断等用到原始的34,其实左上角y的数
值为staTop-UNIT*/
int a[ 11 ][ 4 ][ 4 ]={
/*************************
7 字形
*************************/
{ {0,0,0,0},
{0,1,0,0},
{0,1,0,0},
{0,1,1,0} },
/*************************反 7 字形
*************************/
{ {0,0,0,0},
{0,1,1,0},
{0,1,0,0},
{0,1,0,0} },
/*************************
t 字形
*************************/
{ {0,0,0,0},
{0,1,0,0},
{1,1,1,0},
{0,0,0,0} },
/*************************
小z 字形
*************************/
{ {0,0,0,0},
{1,1,0,0},
{0,1,1,0},
{0,0,0,0} },
/*************************
小反 z 字形
*************************/
{ {0,0,0,0},
{0,1,1,0},
{1,1,0,0},
{0,0,0,0} },
/*************************
长条
************************/
{ {0,1,0,0},
{0,1,0,0},
{0,1,0,0},
{0,1,0,0} },
/*************************
田 字形
*************************/
{ {0,0,0,0},
{0,1,1,0},
{0,1,1,0},
{0,0,0,0} },
/*******************************
选了难度为困难时才有的图形
********************************/
/********************************
杯子形
*********************************/
{ {1,1,1,0},
{1,0,1,0},
{1,0,1,0},
{0,0,0,0} },
/*************************
大 Z 字形
*************************/
{ {1,1,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,0,0} },
/*************************
大 Z 字形
*************************/
{ {0,1,1,0},
{0,1,0,0},
{1,1,0,0},
{0,0,0,0} },
/*************************
一点
*************************/
{ {0,0,0,0}
{0,1,0,0},
{0,0,0,0},
{0,0,0,0} },
} ;
int speed=0; /*速度变量*/
int difficulty=0; /*难度变量*/
int nandu; /*根据难度而确定产生的随机数的范围*/
int score=0; /*记录分数的变量*/
int eraser=0; /*记录橡皮的变量*/
int x , y; /*游戏界面的坐标*/
int GameSpace[ 26 ] [ 12 ] ;/*游戏空间的数组,以记录坐标*//*由于出现bug而不再是26行了,,又由
于上交时间紧是从第3号有效*/
int current[4][4]; /*为按照预览框的形状初始下落*/
int rec; /*为记录下个方块的形状*/
void main()
{
int gr = DETECT , gm = 0;
initgraph( &gr , &gm , "c:\\bgi" ) ; hello(); /*是hello()中嵌有game(),goodbye()函数,通过选择进入不同的函数,goodbye()为出口*/
} /*game()中也嵌有hello(),goodbye()函数,通过选择进入不同的函数,goodbye()为出口*/
/****************************** 第一个界面的具体函数
*****************************/ void hello()
{
int key; /*记录按键的变量*/
char ch[1];
setbkcolor( BLACK ) ;
interface(); /*输出不许改变的文字信息*/
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
setcolor( WHITE );
switch ( difficulty )
{
case 0:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(355,390,"easy");
break;
case 1:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(340,390,"normal");
break;
case 2:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(355,390,"hard");
break;
}
itoa(speed,ch,10); /*根据难度限制speed的范围并输出*/ outtextxy(370,360,ch); while (1)
{
while(!kbhit())
{
activemenu(); /*若没按键一直根据相应的值输出*/ }
key=bioskey(0); /*根据不同的按键,实现选择的功能*/ switch (key)
{
case UP:
menu_up();
break;
case DOWN:
menu_down();
break;
case LEFT:
menu_left();
break;
case RIGHT:
menu_right();
break;
case ENTER:
menu_enter();
break;
case ESC:
menu_escape();
break;
}
}
}
/***********************
游戏主体部分
************************/
void game()
{
int i , j ;
int key ; /*记录按键*/
int flrw; /*记录满行的所在行的行数*/ int times; /*记录在一次消行循环中消了多少行*/
setbkcolor( BLACK ) ; drawwal() ; /*画边界且画预览框的边界*/ initgamespace() ; /*初始化游戏空间*/
showtext(); /*显示提示信息*/
showvalue(); /*根据玩家所选的速度,难度输出信息*/
if (difficulty==2) /*根据难度而确定产生的随机数的范围*/ nandu=11;
else
nandu=7;
gamestart(); /*主要是第一次产生随机数*/ while(1)
{
randomize();
while( ! kbhit() ) /*若有按键返回一个非零值*/ {
if_end(); /*每次都判断游戏是否结束*/
if ( crash() == 1 ) /*若方块不再下落则坐标复位,又产生一个随机数*/
{
x = staLeft;
y = staTop-UNIT;
id( rec );
rec =next();
preview( rec );
}
if ( crash() == 0) /*若方块还可以下落,y坐标加一个单位长度*/
y += UNIT;
for(times=0;fullrow()!=0;++times) /*消行部分,实质上是每次只消一行*/
{
flrw=fullrow();
clearrow( flrw );
}
if (times!=0) /*若有消行,记录分数*/
showscore ( times );
drawsqa( RED ); /*画红色方块*/
for ( i = 0; i < 25 - speed*4 ; ++i )
{
if ( kbhit() ) /*为消除看起来很卡的感觉*/
{
break;
}
delay( 5000 ) ; /*与画黑色色方块一起,达到动画效果*/
}
drawsqa( BLACK ) ; /*与延时一起,达到动画效果*/ }
key=bioskey(0); /*根据不同的按键,实现不同的功能*/ switch ( key )
{
case DOWN:
down();
break;
case LEFT:
left();
break;
case RIGHT:
right();
break;
case UP:
up();
break;
case SPACE:
pause();
break;
case ESC:
escape();
break;
case W:
wipe ();
break;
case S:
speed_up();
break;
case D:
speed_down();
break;
}
}
}
/*****************************
退出界面的函数
*****************************/
void goodbye() /*为达到动态效果,写得稍微烦了点*/ {
static int color=9;
int i,j,tag=0; /*tag为标记变量*/
settextjustify( CENTER_TEXT,CENTER_TEXT );
settextstyle (0,0,7); while(1)
{
if (tag==1) /*标记变量若为1,跳出循环(即有按键则退出)*/
break;
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit()) /*若有按键,标记变量赋1,跳出循环,以下每个for 循环同样 */
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 50,110,"T");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 110,110,"H");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 170,110,"A");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 230,110,"N");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 290,110,"K");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 430,110,"Y");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 490,110,"O");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 550,110,"U");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 240,190,"F");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 300,190,"O");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 360,190,"R");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 110,270,"P");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 170,270,"L");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 230,270,"A");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 290,270,"Y");
delay(10000);
}
for (j=0;j<1;++j,setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 110,270,"P");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 170,270,"L");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 230,270,"A");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 290,270,"Y");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 350,270,"I");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 410,270,"N");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 470,270,"G");
delay(10000);
}
for (j=0;j<1;++j,++color)
{
if (tag==1)
break;
if (kbhit())
{
tag=1;
break;
}
setcolor ( color );
if ( color==14 )
color=9;
outtextxy( 530,270,"!");
delay(10000);
}
}
getch();
exit(0);
}
/***************************************
以下是开始菜单中各个函数的具体实现
**************************************/
void interface()
{
/*********************
画主要的线
*********************/
setlinestyle(0,0,THICK_WIDTH); setcolor(RED);
line(80,0,80,479);
line(0,285,639,285);
setcolor(GREEN);
line(87,0,87,479);
line(0,292,639,292);
setcolor(BLUE);
line(94,0,94,479);
line(0,299,639,299);
/***************************
显示相关的信息
****************************/
setcolor ( BROWN );
settextjustify( CENTER_TEXT,CENTER_TEXT );
settextstyle (0,0,2);
outtextxy( 325,60," Welcome to "); settextstyle (0,0,5);
setcolor ( LIGHTCYAN );
outtextxy( 325,120,"RUSSIAN"); setcolor ( LIGHTMAGENTA );
outtextxy( 325,170,"DIAMOND"); setcolor ( LIGHTGRAY );
settextstyle (0,0,1);
settextjustify( CENTER_TEXT,CENTER_TEXT );
outtextxy( 430,230,"by henry_black"); outtextxy( 430,250,"Jun 2nd 07"); /**************************** 显示菜单的文字
*************************/
setcolor ( CYAN );
settextjustify( RIGHT_TEXT,CENTER_TEXT ); settextstyle (1,0,3);
outtextxy( 325,330,"GAME "); outtextxy( 317,360,"GAME SPEED"); outtextxy( 317,390,"DIFFICULTY"); outtextxy( 325,420,"GAME "); }
void activemenu()
{
switch ( menu_color ) /*根据不同menu_color确定不同选项的颜色,达到选择的动态感*/
{
case 0:
start_color=14;
speed_color=3;
difficulty_color=3;
end_color=3;
break;
case 1:
start_color=3;
speed_color=14;
difficulty_color=3;
end_color=3;
break;
case 2:
start_color=3;
speed_color=3;
difficulty_color=14;
end_color=3;
break;
case 3:
start_color=3;
speed_color=3;
difficulty_color=3;
end_color=14;
break;
}
/*以下是根据确定不同选项的颜色值,输出各选项*/
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
setcolor ( start_color ); outtextxy( 350,330,"START"); setcolor ( end_color ); outtextxy( 350,420," END ");
setcolor ( speed_color ); /*画箭头*/
line(325,365,335,360);
line(325,365,335,370);
line(335,360,335,370);
line(435,365,425,360);
line(435,365,425,370);
line(425,360,425,370);
setcolor ( difficulty_color ); line(325,395,335,390);
line(325,395,335,400);
line(335,390,335,400);
line(435,395,425,390);
line(435,395,425,400);
line(425,390,425,400);
}
void menu_up()
{
if (menu_color==0) /*若选到了最顶,再按就下到最下边的选项*/
menu_color=4;
--menu_color; /*实现向上移动一个位置*/
}
void menu_down()
{
if (menu_color==3) /*若选到了最底,再按就下到最上边的选项*/ menu_color=-1;
++menu_color; /*实现向下移动一个位置*/
}
void menu_left() /*该函数为减小speed,difficulty的值*/ {
char ch[1]; /*为把数值转字符输出*/
if (menu_color == 1 ) /*说明当前选项为速度*/
{
if (speed==0 && difficulty!=0) /*根据难度,确定范围并从最小值变成最大值*/
speed=6;
if (speed==0 && difficulty==0)
speed=4;
--speed;
setfillstyle(0,BLACK); /*先画黑,再转字符输出*/ bar(345,355,385,375);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3); itoa(speed,ch,10);
outtextxy(370,360,ch); }
if (menu_color == 2) /*说明当前选项为难度*/ {
if (difficulty==0) /*最小值变成最大值*/
difficulty=3;
--difficulty;
switch ( difficulty ) /*根据difficulty的值,先画黑,再输出相应的信息*/
{
case 0:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(355,390,"easy");
break;
case 1:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(340,390,"normal");
break;
case 2:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(355,390,"hard");
break;
}
}
}
void menu_right() /*该函数为增大speed,difficulty的值*/ {
char ch[1];
if (menu_color == 1 ) /*说明当前选项为速度*/
{
if (speed==5 && difficulty!=0) /*根据难度,确定范围并从最大值变成最小值*/
speed=-1;
if (speed==3 && difficulty==0)
speed=-1;
++speed;
setfillstyle(0,BLACK); /*先画黑,再转字符输出*/
bar(345,355,385,375);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
itoa(speed,ch,10);
outtextxy(370,360,ch); }
if (menu_color == 2) /*说明当前选项为难度*/ {
if (difficulty==2) /*最大值变成最小值*/
difficulty=-1;
++difficulty;
switch ( difficulty ) /*根据difficulty的值,先画黑,再输出相应的信息*/
{
case 0:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(355,390,"easy");
break;
case 1:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(340,390,"normal");
break;
case 2:
setfillstyle(0,BLACK);
bar(338,385,420,410);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
outtextxy(355,390,"hard");
break;
}
}
}
void menu_enter()
{
if (menu_color == 0) /*说明当前选项为开始游戏*/ {
cleardevice(); /*若按回车则开始*/
game();
}
if (menu_color == 3) /*说明当前选项为退出游戏*/ {
cleardevice(); /*若按回车则退出*/
goodbye();
}
}
void menu_escape() /*若在选择时按esc键,直接跳到退出选项*/
{
menu_color=3;
}
/*******************************************
以下是game()函数中各个相关的函数的具体实现
*********************************************/
void gamestart() /*主要是开始时第一次产生随机数*/ {
randomize();
score=0;
eraser=0;
x = staLeft;
y = staTop-UNIT;
rec=rand()%nandu;
id( rec );
rec=rand()%nandu;
preview( rec );
}
void initgamespace()
{
int i , j ;
for ( i = 0 ; i < 25 ; ++ i ) for ( j = 1 ; j < 11 ; ++ j )
GameSpace[ i ][ j ] = 0 ; /*中间赋0,说明没有被占有*/ for ( i = 1 ; i < 11 ; ++ i ) GameSpace[ 25 ][ i ] = 1 ; /*最下边赋1,为了不让方块超出底部*/ for ( i = 0 ; i < 2 ; ++ i ) for( j = 0 ; j < 26 ; ++ j )
GameSpace[ j ][ i*11 ] = 1 ; /*两侧赋1,为了不让方块超出边界*/ }
void drawwal()
{
int i , j ;
/**************
画预览框
*************/
setfillstyle( SOLID_FILL , YELLOW ) ; /*先画大黄框*/ bar( 465 , 115 , 555 , 205 ) ; setfillstyle( SOLID_FILL , BLACK ) ; /*再中间画小黑框*/ bar( 473 , 123 , 547 , 197 ) ;
/***************
画游戏空间的边界
****************/ setfillstyle( SOLID_FILL , YELLOW ) ;
x = 204 ;
y = 34 ;
setcolor( 0 ) ;
setlinestyle(0,0,1);
for ( j = 0 ; j < 10 ; ++ j ) {
bar3d( x+j*UNIT , y +48 , x+j*UNIT+UNIT , y+64 , 0 , 1 ) ;
bar3d( x+j*UNIT , y+400 , x+j*UNIT+UNIT , y+416 , 0 , 1 ) ;
}
x = 188 ;
y = 34 ;
for ( i = 0 ; i < 2 ; ++ i ) for ( j = 3 ; j < 26 ; ++ j )
bar3d ( x+i*176 , y+j*UNIT , x+i*176+UNIT , y+j*UNIT+UNIT , 0 , 1 ) ;
}
void drawsqa( int color ) /*根据传进来的颜色画红的方块,黑的方块*/
{
int i , j;
setfillstyle( SOLID_FILL , color );
for ( i = 0 ; i < 4 ; ++ i )
for( j=0 ; j<4 ; ++ j )
{ /*画的时候只画方块数组中有1的地方*/
if ( (y + i * UNIT > staTop+48) && current[j] == 1) /*方块其实是从最顶还要上的地方开始掉落,为
*/ 好看
bar3d( x + j * UNIT , y + i * UNIT , x + j * UNIT + UNIT , y + i * UNIT + UNIT , 0 , 0 ) ;
}
}
void showtext() /*为显示游戏中的提示信息*/
{
int point1 [10]={15,40,180,40,180,240,15,240,15,40}; int point2 [18]={60,260,140,260,180,305,180,388,140,432,60,432,20,388,20,305,60,260};
setlinestyle(0,0,3) ;
/*******************
左上角
******************/
setcolor( LIGHTBLUE );
drawpoly(5,point1);
setcolor( BROWN );
settextjustify(RIGHT_TEXT,TOP_TEXT); settextstyle(0,0,0);
outtextxy(130,70,"TURN AROUND : "); outtextxy(130,85,"FALL DOWN : "); outtextxy(130,100,"LEFTWARD : "); outtextxy(130,115,"RIGHTWARD : "); outtextxy(130,130,"SPEED UP : "); outtextxy(130,145,"SPEED DOWN : "); outtextxy(130,160,"WIPE OFF : "); outtextxy(130,175,"PAUSE : "); outtextxy(130,190,"CONFIRM : "); outtextxy(130,205,"EXIT : "); setcolor( LIGHTGRAY );
settextjustify(LEFT_TEXT,TOP_TEXT); outtextxy(130,70,"UP");
outtextxy(130,85,"DOWN");
outtextxy(130,100,"LEFT");
outtextxy(130,115,"RIGHT"); outtextxy(130,130,"S");
outtextxy(130,145,"D");
outtextxy(130,160,"W");
outtextxy(130,175,"SPACE"); outtextxy(130,190,"ENTER"); outtextxy(130,205,"ESCAPE"); /**************
左下角
**************/
setcolor( LIGHTCYAN );
drawpoly(9,point2);
settextstyle(3,HORIZ_DIR,1);
settextjustify(RIGHT_TEXT,TOP_TEXT); setcolor( LIGHTMAGENTA );
outtextxy(100,300,"score :");
outtextxy(100,320,"speed :");
outtextxy(100,340,"eraser :");
outtextxy(120,360,"difficulty :");
/***********************
游戏框中上
************************/
settextstyle(0,HORIZ_DIR,2);
settextjustify(LEFT_TEXT,TOP_TEXT); setcolor( GREEN );
outtextxy(225,55,"ENJOY IT");
/************
右下角
**************/
setcolor( MAGENTA );
settextstyle(0,HORIZ_DIR,0);
settextjustify(CENTER_TEXT,CENTER_TEXT); outtextxy(510,230,"GuiLin University of "); outtextxy(510,245,"Electronica Technology"); setcolor( YELLOW );
outtextxy(510,270,"Department of Computer"); setcolor( BLUE);
outtextxy(510,295,"Major of SoftWare"); setcolor( LIGHTRED );
outtextxy(570,375,"by henry_black");
outtextxy(570,390,"Jun 2nd,07"); setcolor( GREEN );
settextjustify(LEFT_TEXT,CENTER_TEXT); outtextxy(410,320,"QQ : 42595947"); outtextxy(410,335,"nick name : Count. Black");
setcolor(BLACK);
setlinestyle(0,0,1);
}
void showvalue()
{
char ch[7]; /*为把数值转字符输出*/
/***************
输出分数
*****************/
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,2);
itoa(score,ch,10);
outtextxy(105,310,ch);
/***************
输出速度
****************/
itoa(speed,ch,10);
outtextxy(105,330,ch);
/***************
输出橡皮数
****************/
itoa(eraser,ch,10);
outtextxy(105,350,ch);
/***************
输出难度
****************/
settextjustify( CENTER_TEXT,CENTER_TEXT );
if (difficulty==0)
outtextxy(110,390,"easy"); if (difficulty==1)
outtextxy(110,390,"normal"); if (difficulty==2)
outtextxy(110,390,"hard"); setcolor(BLACK);
}
/***************************** 极度核心的变形函数
*****************************/ void up()
{ /*整体思路是先变形,后判断,若不可变形则变回;若可变形则不变,再画图形*/
int i,j,k,l,temp,tag,lab; /*需要两个标记变量*/ for ( i = 0 ; i < 3 ; ++ i ) /*主对角线对折*/ for ( j = i+1 ; j < 4 ; ++ j ) {
temp = current[ i ][ j ];
current[ i ][ j ] = current[ j ][ i ];
current[ j ][ i ] = temp ; }
for( i= 0 ; i < 4 ; ++ i) /*竖中轴对折*/
for( j = 0 ; j < 2 ; ++ j)
{
temp = current[ i ][ j ] ;
current[ i ][ j ] = current[ i ][ 3-j ] ;
current[ i ] [3-j ] = temp ;
}
tag=0; /*标记变量为跳出循环,当为1时跳出循环*/
for( i= 0 ; i < 4 ; ++ i)
{ /*变形后有可能插入已为1的地方,分情况讨论,讨论每行的第0,1,2,3号位置*/
/******************************
由于第1,2号位置情况复杂,且出现的情况极少,故舍弃。一有这种情况发生则认为不能变形跳出循环
************************************************************************************************/ if ( (GameSpace [ (y - spaTop) / UNIT + i ] [ (x - spaLeft) / UNIT + 1 ] == current[ i ][1]) && current[ i ] [1] == 1 )
{
tag=1;
break;
}
if ( (GameSpace [ (y - spaTop) / UNIT + i ] [ (x - spaLeft) / UNIT + 2 ] == current[ i ][2]) && current[ i ] [2] == 1 )
{
tag=1;
break;
}
/***************************************************************************************************
第0号位置由于变形后,有可能只是因为左侧位置不够而不能变形,只须稍微向右移动一个单位长度就
可以了的
****************************************************************************************************/
if ( (GameSpace [ (y - spaTop) / UNIT + i ] [ (x - spaLeft) / UNIT] == current[ i ][0]) && current[ i ] [0] == 1 )
{
x+=UNIT;
lab=0;
/*********************************************************************************************************************
********
人为的移动后,可能对应的第1,23号位置有可能也已经有物体,若有则说明即使移动了一个单位长度也
还是不够的两个标记位均赋1,跳出循环
**********************************************************************************************************************
********/
for (k=0;k<4;++k)
for (l=1;l<4;++l)
{
if (lab==1)
break;
if((GameSpace [ (y - spaTop) / UNIT +k] [ (x - spaLeft) / UNIT+l] == current[k][l]) && current[k]
[l] == 1)
{
tag=1;
x-=UNIT;
lab=1;
}
}
}
/******************************************************************************************************
第3号位置由于变形后,有可能只是因为右侧位置不够而不能变形,只须稍微向左移动一个单位长度就
可以了的
*********************************************************************************************************/ if ( (GameSpace [ (y - spaTop) / UNIT + i ] [ (x - spaLeft) / UNIT+3] == current[ i ][3]) && current[ i ] [3] == 1 )
{
x-=UNIT;
lab=0;
/*********************************************************************************************************************
********
人为的移动后,可能对应的第0,1,2号位置有可能也已经有物体,若有则说明即使移动了一个单位长度
也还是不够的两个标记位均赋1,跳出循环
**********************************************************************************************************************
********/
for (k=0;k<4;++k)
for (l=0;l<3;++l)
{
if (lab==1)
break;
if((GameSpace [ (y - spaTop) / UNIT +k] [ (x - spaLeft) / UNIT+l] == current[k][l]) && current[k]
[l] == 1)
{
tag=1;
x+=UNIT;
lab=1;
}
}
}
}
switch ( tag ) /*若标记变量始终为0,则说明没有插到别的有一的地方*/ {
case 0:
break;
case 1: /*若标记变量始终为1,则说明插到别的有一的地方,须变回来*/
for( i= 0 ; i < 4 ; ++ i) /*逆序,先竖中轴对折*/
for( j = 0 ; j < 2 ; ++ j)
{
temp = current[ i ][ j ];
current[ i ][ j ] = current[ i ][ 3-j ] ;
current[ i ] [3-j ] = temp ;
}
for ( i = 0 ; i < 3 ; ++ i ) /*再主对角线对折*/
for ( j = i+1 ; j < 4 ; ++ j )
{
temp = current[ i ][ j ];
current[ i ][ j ] = current[ j ][ i ];
current[ j ][ i ] = temp ;
}
break;
}
}
void down()
{
if (crash()==0) /*向下若不碰撞,则y坐标加一个单位长度*/
y += UNIT ;
}
void left()
{
int i , j , tag=0; /*标记变量跳出循环体,这是一种方法*/
for( i = 0 ; i < 4 ; ++ i )
for( j = 0 ; j < 4 ; ++ j )
{
/**************************************************************************************************************
若整个方块数组向左一个单位长度,与游戏空间有重叠的1,说明下落的方块左边以有物体,不能向左,一检测重叠有就跳出循环
***************************************************************************************************************/
if ( ( GameSpace[ ( y - spaTop ) / UNIT + i ] [( x - spaLeft - UNIT ) / UNIT + j] == current[ i
][ j ] ) && current[ i ][ j ] == 1 )
{
if ( tag == 1)
break;
tag = 1 ;
}
}
if ( tag == 0 ) /*标记变量始终不改变则说明可以向左移动*/
x -= UNIT ;
}
void right()
{
int i , j , tag = 0 ;
for( i = 0 ; i < 4 ; ++ i )
for( j = 0 ; j < 4 ; ++ j )
{
/*******************************************************
*******************************************************
若整个方块数组向右一个单位长度,与游戏空间有重叠的1,说明下落的方块右边已有物体,不能向右,一检测重叠有就跳出循环
***************************************************************************************************************/
if ( ( GameSpace[ ( y - spaTop ) / UNIT + i ] [( x - spaLeft + UNIT ) / UNIT + j] == current[
i ][ j ] ) && current[ i ][ j ] == 1 )
{
if ( tag == 1)
break;
tag = 1 ;
}
}
if ( tag == 0) /*标记变量始终不改变则说明可以向右移动*/
x += UNIT ;
}
void pause()
{
settextstyle(0,HORIZ_DIR,0);
settextjustify(LEFT_TEXT,TOP_TEXT);
setcolor( WHITE );
outtextxy(225,35,"press any key");
getch(); /*主要应用getch()的性质*/
setfillstyle( SOLID_FILL , BLACK );
bar(225,35,350,50);
setcolor( BLACK );
}
void escape()
{
int key; /*按键值的记录*/
int esc_color=0; /*控制当前选项到了哪的重要判断标记*/ int try_color=9; /*说明当前选项到了选择重玩的地方*/ int order_color=8; /*说明当前选项到了返回hello()界面的地方*/ int exit_color=8; /*说明当前选项到了选择退出的地方*/ while(1)
{
while(!kbhit()) /*若不按键,整个游戏停留在选项上*/ {
switch ( esc_color ) /*根据标记显示不同的颜色,以说明到了哪个选项*/
{
case 0:
try_color=9;
order_color=8;
exit_color=8;
break;
case 1:
try_color=8;
order_color=9;
exit_color=8;
break;
case 2:
try_color=8;
order_color=8;
exit_color=9;
break;
}
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,3);
setcolor(try_color);
outtextxy(445,35,"TRY AGAIN ");
setcolor(order_color);
outtextxy(464,60," MENU ");
setcolor(exit_color);
outtextxy(470,85," EXIT "); }
key=bioskey(0);
switch ( key )
{
case UP:
if (esc_color == 0) /*若选到了最顶,再按就下到最下边的选项*/
esc_color=3;
--esc_color;
break;
case DOWN:
if (esc_color == 2) /*若选到了最底,再按就下到最上边的选项*/
esc_color=-1;
++esc_color;
break;
case ENTER:
if (esc_color == 0) /*若选到了重玩,按回车则重新游戏*/
{
cleardevice();
game();
}
if (esc_color == 1) /*若选到了最开始的界面,按回车则到开始界面*/
{
cleardevice();
hello();
}
if (esc_color == 2) /*若选到了退出,按回车则退出游戏*/
{
cleardevice();
goodbye();
}
break;
case ESC: /*若按esc键,则直接跳到推出选项*/
esc_color = 2;
break;
}
}
}
void speed_up() {
char ch[2];
if ( (speed==3) && (difficulty==0) ) /*根据所选难度,加速到在一定范围的速度后不再加速*/
;
else if (speed==5)
;
else
++speed;
setfillstyle( SOLID_FILL , BLACK ) ; /*根据改变的值,输出对应的信息*/ bar(105,325,170,343);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT ); settextstyle (1,0,2);
itoa(speed,ch,10);
outtextxy(105,330,ch);
setcolor(BLACK);
}
void speed_down() /*根据所选难度,减慢到在一定范围的速度后不再减慢*/ {
char ch[2];
if ( (speed==0))
;
else
--speed;
setfillstyle( SOLID_FILL , BLACK ) ; /*根据改变的值,输出对应的信息*/ bar(105,325,170,343);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT ); settextstyle (1,0,2);
itoa(speed,ch,10); outtextxy(105,330,ch); setcolor(BLACK);
}
void wipe()
{
char ch[2];
int i;
if ( eraser!=0 ) /*当橡皮还有时,消两次最底下那行,并橡皮减1*/
{
for(i=0;i<2;++i)
clearrow(24);
--eraser;
}
setfillstyle( SOLID_FILL , BLACK ) ; /*根据还剩的橡皮数,输出相应的信息*/
bar(105,345,170,363); setcolor( WHITE ); settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,2); itoa(eraser,ch,10); outtextxy(105,350,ch); setcolor(BLACK);
}
void if_end()
{
int i,tag=0;
for (i=1;i<11;++i)
if ( GameSpace[3]==1) /*最顶上一检测出有方块,则结束*/ {
tag=1;
break;
}
if (tag==1) /*标记变量为一说明,游戏结束,进入退出的菜单*/ {
escape();
}
}
void preview(int for_next) {
int i,j;
setfillstyle( SOLID_FILL , BLACK ) ; /*先画黑*/
bar( 473 , 123 ,547 , 197 ) ;
setfillstyle(SOLID_FILL,RED); /*再画方块数组中有1的地方*/ for (i=0;i<4;++i)
for (j=0;j<4;++j)
if (a[ for_next ][j]==1)
bar3d(480+j*UNIT,128+i*UNIT,480+j*UNIT+UNIT,128+i*UNIT+UNIT,0,0);
setfillstyle(SOLID_FILL,BLACK); }
void clearrow( int temp) {
int i , j ;
/************************************************************************* 根据传进来满行的所在行的编号,从那行起由下到上每行的上一行的值赋给这行
***************************************************************************/
for (i=temp;i>0;--i)
for(j=1;j<11;++j)
GameSpace[ i ][ j ] = GameSpace[ i -1 ][ j ] ;
setfillstyle(SOLID_FILL,BLACK); /*整个游戏空间画黑*/
bar(spaLeft+UNIT,spaTop+64,spaLeft+UNIT+160,spaTop+UNIT+384);
setfillstyle (SOLID_FILL,RED); /*画红方块,再游戏空间中有1 的地方*/
for (i=1;i<25;++i)
for (j=1;j<11;++j)
if (GameSpace[j]==1)
{
bar3d(spaLeft+j*UNIT,spaTop+i*UNIT,spaLeft+j*UNIT+UNIT,spaTop+i*UNIT+UNIT,0,0);
}
setfillstyle(SOLID_FILL,BLACK);
setcolor(BLACK);
}
void showscore(int t)
{
char ch[6];
switch( t ) /*根据传进来的一次消行循环中消了多少行,确定加多少分*/
{
case 0:
break;
case 1:
score+=10;
break;
case 2:
score+=20;
break;
case 3:
score+=40;
break;
case 4:
score+=60;
break;
}
setfillstyle( SOLID_FILL , BLACK ) ;
bar(105,305,170,323); setcolor( WHITE ); settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,2); itoa(score,ch,10); outtextxy(105,310,ch);
if ( (score%1000==0) && (score!=0)) /*若分数每满1000分,速度加1*/
{
if ( (speed==3) && (difficulty==0) ) /*若选的难度为简单,速度最大为3*/
;
else if (speed==6) /*别的最大为6*/
;
else
++speed;
setfillstyle( SOLID_FILL , BLACK ) ; /*根据速度的值,先画黑再输出*/ bar(105,325,170,343);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,2);
itoa(speed,ch,10);
outtextxy(105,330,ch);
}
if ( (score%500==0) && score!=0) /*若分数每满500分,橡皮加1*/ {
++eraser;
setfillstyle( SOLID_FILL , BLACK ) ; /*根据橡皮数,先画黑再输出*/ bar(105,345,170,363);
setcolor( WHITE );
settextjustify( LEFT_TEXT,CENTER_TEXT );
settextstyle (1,0,2);
itoa(eraser,ch,10);
outtextxy(105,350,ch);
}
setcolor(BLACK);
}
void id(int rec) /*为了能按照预览框的形状开始掉落*/ {
int i , j ;
for ( i = 0 ; i < 4 ; ++ i )
for ( j = 0 ; j < 4 ; ++ j )
current[ i ][ j ]=a[ rec ][ i ][ j ]; }
/************************
极度核心的碰撞函数
************************/
int crash()
{
int i , j , k ,l;
/****************************************************************************************************************
若方块数组整体下移一个单位长度,与游戏空间有重叠的1,则在当前位置赋值给游戏空间,表示停止下
1的地方 落,并画红有
*****************************************************************************************************************/
for ( i = 0 ; i < 4; ++ i )
for ( j = 0 ; j <4 ; ++j )
if ( ( GameSpace[ ( y - spaTop + UNIT ) / UNIT + i ][ ( x - spaLeft ) / UNIT + j ] == current
[ i ][ j ] ) && (current[ i ][ j ] == 1) )
{
for ( k = 0 ; k < 4 ; ++ k )
for ( l = 0 ; l < 4 ; ++ l )
if (current[k][l]==1) /*只能赋下落数组中是1的地方,为解决这个错误用了三天*/
GameSpace[ ( y - staTop ) / UNIT + k ][ ( x - spaLeft ) / UNIT + l ] = current[ k ][ l ];
setfillstyle( SOLID_FILL , RED );
for ( k = 3 ; k < 25 ; ++ k )
for ( l = 1 ; l < 11 ; ++ l )
{
if( GameSpace[k][l] == 1 )
bar3d( spaLeft + l * UNIT, spaTop + k * UNIT, spaLeft + l * UNIT + UNIT,spaTop + k * UNI
T + UNIT, 0, 0);
}
setfillstyle( SOLID_FILL , BLACK );
return 1; /*若有赋值返回1说明碰撞了*/
}
return 0; /*若经过了判断,则返回0说明没有碰撞*/ }
int fullrow()
{ /*若有满行,也每次只返回一行的行编号*/ int i, j, tag;
for (i=1;i<25;++i)
{
tag=1; /*先认为满行,标记变量赋初值为1*/ for (j=1;j<11;++j)
{
if (GameSpace[j]==0) /*若发现一行中一有0,说明这行没满,跳出扫下一行*/
{
tag=0;
break;
}
}
if (tag == 1) /*若当前i行满行,返回该行值,即*/
return (i);
}
if (i==25) /*扫描了整个游戏空间,i为25说明没有满行*/
return (0);
}
int next()
{ /*主要产生一个随机数*/
int i;
randomize();
i=rand()%nandu;
return i; }