C开发
在图书馆看到一本书 有俄罗斯方块的源程序
自己当年也是俄罗斯方块的爱好者
便想分析分析这个小游戏的源代码
这几天有空就看了看
发现读源码对编程领悟很有帮助
读完深深的感觉到 程序的确是好的数据结构加上好的算法
这段程序定义了两个数据结构
分别是
//游戏底板结构,表示每个小方块所具有的属性
struct BOARD
{
//当前状态,只有0或1,1表示次小方块已被占用
int var;
//小方块的颜色
int color;
}Table_board[Vertical_boxs][Horizontal_boxs];//游戏跳出的方块结构
struct SHAPE
{
//一个字节等于8位,每四位来表示一个游戏方块的一行
//如box[0]="0x88",box[1]="oxc0"表示的是:
//1000
//1000
//1100
//0000
//以此来表示游戏方块的各种形状
char box[2];
//游戏方块的颜色
int color;
//下一个游戏方块的编号
int next;//这个next设计的也是相当妙啊
};该源码的算法 更确切的说是游戏的逻辑
读完也是很有体会
把握好大的系统 强大的逻辑能力是必要的
平时自己还是要再数据结构和算法这些方面加强学习、锻炼
这段程序还有就是中断不是很理解
还需要时间领悟一下
下面贴一下我的程序注释
这个游戏的层次描述:
1预览功能
2控制功能
俄罗斯方块 3显示更新功能
4分数更新功能
5游戏帮助功能
下面是源码:(该程序用到了TC的图形库,所以VC下是运行不了的,不过VC做代码编辑阅读比较方便,所以代码注释采用了VC下的//)
//加载头文件
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <graphics.h>//加载tc图形库//定义按键码
#define VK_LEFT 0x4b00
#define VK_RIGHT 0x4d00
#define VK_DOWN 0x5000
#define VK_UP 0x4800
#define VK_ESC 0x011b
//设置中断号 1c:时钟中断时得到控制权,若要固定时间发生事件,可以通过修改1c中断来得到
#define TIMER 0x1c//共有19种形态的游戏方块
#define MAX_BOX 19
//方块的边长为20
#define BSIZE 20
//显示游戏界面的左上角x坐标
#define Sys_x 160
//显示游戏界面的左上角y坐标
#define Sys_y 25
//水平方向的方块数量
#define Horizontal_boxs 10
//垂直方向的方块数量
#define Vertical_boxs 15
//第一个游戏方块的产生起始位置
#define Begin_boxs_x Horizontal_boxs/2
//前景色
#define FgColor 3
//背景色
#define BgColor 0
//右边状态栏的x坐标
#define LeftWin_x Sys_x+Horizontal_boxs*BSIZE+46
#define false 0
#define true 1
//移动的方向
#define MoveLeft 1
#define MoveRight 2
#define MoveDown 3
#define MoveRoll 4//保存当前游戏方块编号
int current_box_numb;
//保存游戏方块的当前坐标
int Curbox_x=Sys_x+Begin_boxs_x*BSIZE,Curbox_y=Sys_y;
//是否要产生新游戏方块
int flag_newbox=false;
//下落速度
int speed=1;
//游戏得分
int score=0;
//每等级所需要分数
int speed_step=30;
//指向原来时钟中断处理过程入口的中断处理函数指针 关键字interrupt指定一个函数应该被看成一个中断函数
void interrupt(*oldtimer)(void);//游戏底板结构,表示每个小方块所具有的属性
struct BOARD
{
//当前状态,只有0或1,1表示次小方块已被占用
int var;
//小方块的颜色
int color;
}Table_board[Vertical_boxs][Horizontal_boxs];//游戏跳出的方块结构
struct SHAPE
{
//一个字节等于8位,每四位来表示一个游戏方块的一行
//如box[0]="0x88",box[1]="oxc0"表示的是:
//1000
//1000
//1100
//0000
//以此来表示游戏方块的各种形状
char box[2];
//游戏方块的颜色
int color;
//下一个游戏方块的编号
int next;
};//初始化游戏方块的内容,包括形状 颜色 和下一个游戏方块编号
struct SHAPE shapes[MAX_BOX]=
{
{0x88,0xc0,CYAN,1},
{0xe8,0x0,CYAN,2},
{0xc4,0x40,CYAN,3},
{0x2e,0x0,CYAN,0},{0x44,0xc0,MAGENTA,5},
{0x8e,0x0,MAGENTA,6},
{0xc8,0x80,MAGENTA,7},
{0xe2,0x0,MAGENTA,4},{0x8c,0x40,YELLOW,9},
{0x6c,0x0,YELLOW,8},{0x4c,0x80,BROWN,11},
{0xc6,0x0,BROWN,10},{0x4e,0x0,WHITE,13},
{0x8c,0x80,WHITE,14},
{0xe4,0x0,WHITE,15},
{0x4c,0x40,WHITE,12},{0x88,0x88,RED,17},
{0xf0,0x0,RED,16},{0xcc,0x0,BLUE,18},
};
//定时计数器变量
unsigned int TimerCounter=0;//以下为函数声明
void initialize(int,int,int,int);
void interrupt newtimer(void);
void SetTimer(void interrupt(*IntProc)(void));
void KillTimer(void);
void ShowScore(int);
void ShowSpeed(int);
void show_help(int,int);
void setFullRow(int);
int DelFullRow(int);
void show_box(int,int,int,int);
void EraseBox(int,int,int);
void ErasePreBox(int,int,int);
int MkNextBox(int);
int MoveAble(int,int,int,int);//主函数
void main()
{
int GameOver=0;
int key,nextbox;
int Currentaction=0;
int gd=VGA,gm=VGAHI,errorcode;
initgraph(&gd,&gm,"");
errorcode=graphresult();
if(errorcode!=grOk)
{
printf("\nNotice:Graphics error: %s\n",grapherrormsg(errorcode));
printf("Press any key to quit!");
getch();
exit(1);
}
setbkcolor(BgColor);
setcolor(FgColor);
randomize();
SetTimer(newtimer);
initialize(Sys_x,Sys_y,Horizontal_boxs,Vertical_boxs);//初始化
nextbox=MkNextBox(-1);
show_box(Curbox_x,Curbox_y,current_box_numb,shapes[current_box_numb].color);
show_box(LeftWin_x,Curbox_y+320,nextbox,shapes[nextbox].color);
show_help(Sys_x,Curbox_y+320);getch();
while (1)
{
Currentaction=0;
flag_newbox=false;
//int bioskey(int cmd)
//完成直接键盘操作,cmd的值决定执行什么操作
//cmd=0,返回下一个键盘键入的值
//cmd=1,查询是否按下一个键,若按下一个键返回非零值,否则返回0
if(bioskey(1))
{
key=bioskey(0);
}
else
{
key=0;
}
switch(key)
{
case VK_LEFT:
if(MoveAble(Curbox_x,Curbox_y,current_box_numb,MoveLeft))
{
EraseBox(Curbox_x,Curbox_y,current_box_numb);
Curbox_x-=BSIZE;
Currentaction=MoveLeft;
}
break;
case VK_RIGHT:
if(MoveAble(Curbox_x,Curbox_y,current_box_numb,MoveRight))
{
EraseBox(Curbox_x,Curbox_y,current_box_numb);
Curbox_x+=BSIZE;
Currentaction=MoveRight;
}
break;
case VK_DOWN:
if(MoveAble(Curbox_x,Curbox_y,current_box_numb,MoveDown))
{
EraseBox(Curbox_x,Curbox_y,current_box_numb);
Curbox_y+=BSIZE;
Currentaction=MoveDown;
}
else
flag_newbox=true;
break;
case VK_UP:
if(MoveAble(Curbox_x,Curbox_y,shapes[current_box_numb].next,MoveRoll))
{
EraseBox(Curbox_x,Curbox_y,current_box_numb);
current_box_numb=shapes[current_box_numb].next;
Currentaction=MoveLeft;
}
break;
case VK_ESC:
GameOver=1;
break;
default:
break;
} if (Currentaction)//当前有动作就执行
{
show_box(Curbox_x,Curbox_y,current_box_numb,shapes[current_box_numb].color);
Currentaction=0;
} if(flag_newbox)//按了向下键,但不能下移就产生新的游戏方块
{
ErasePreBox(LeftWin_x,Sys_y+200,nextbox);
nextbox=MkNextBox(nextbox);
show_box(LeftWin_x,Curbox_y+200,nextbox,shapes[nextbox].color);
if(!MoveAble(Curbox_x,Curbox_y,current_box_numb,MoveDown))
{
show_box(Curbox_x,Curbox_y,current_box_numb,shapes[current_box_numb].color);
GameOver=1;
}
else
{
flag_newbox=false;
}
Currentaction=0;
}
else//自由下落
{
if(Currentaction==MoveDown||TimerCounter>(20-speed*2))
{
if(MoveAble(Curbox_x,Curbox_y,current_box_numb,MoveDown))
{
EraseBox(Curbox_x,Curbox_y,current_box_numb);
Curbox_y+=BSIZE;
show_box(Curbox_x,Curbox_y,current_box_numb,shapes[current_box_numb].color);
}
TimerCounter=0;
}
} if(GameOver)
{
printf("game over,thank you! your score is %d",score);
getch();
break;
}}
getch();
KillTimer();
closegraph();}
//********************************************************************
//显示帮助,提示用户如何进行游戏的相关操作
//
void show_help(int xs,int ys)
{
char stemp[50];
setcolor(15);//void far rectangle(int x0,int y0,int x1,int y1)
//以(x0,y0)为左上角,(x1,x2)为右下角画一个矩形框
rectangle(xs,ys,xs+239,ys+100);
//sprintf()
//将字串格式化到字符串
sprintf(stemp," -Roll -Downwards");
stemp[0]=24;
stemp[8]=25;
setcolor(14);
//void far outtextxy(int x,int y,char far *text)
//将文本输出到指定位置
outtextxy(xs+40,ys+30,stemp);sprintf(stemp," -Turn Left -Turn Right");
stemp[0]=27;
stemp[13]=26;outtextxy(xs+40,ys+45,stemp);
outtextxy(xs+40,ys+60,"Esc-Exit");
setcolor(FgColor);
}//*******************************************************************
//对游戏界面的初始化
void initialize(int x,int y,int m,int n)
{
int i,j,oldx;
oldx=x;
for(j=0;j<n;j++)
{
for(i=0;i<m;i++)
{
Table_board[j][i].var=0;
Table_board[j][i].color=BgColor;
line(x,y,x+BSIZE,y);
line(x,y,x,y+BSIZE);
line(x,y+BSIZE,x+BSIZE,y+BSIZE);
line(x+BSIZE,y,x+BSIZE,y+BSIZE);
x+=BSIZE;
}
y+=BSIZE;
x=oldx;
}
Curbox_x=x;
Curbox_y=y;flag_newbox=false;
speed=1;
score=0;
ShowScore(score);
ShowSpeed(speed);
}//*************************************************************
//显示当前用户的成绩
void ShowScore(int score)
{
int x,y;
char score_str[5];
setfillstyle(SOLID_FILL,BgColor);
x=LeftWin_x;
y=100;
//确定一个以(x0,y0)为左上角,(x1,x2)为右下角的矩形窗口,再按规定图形和颜色填充
bar(x-BSIZE,y,x+BSIZE*3,y+BSIZE*3);
sprintf(score_str,"%3d",score);
outtextxy(x,y,"SCORE");
outtextxy(x,y+10,score_str);
}//**********************************************************
//显示当前下落速度
void ShowSpeed(int speed)
{
int x,y;
char speed_str[5];
setfillstyle(SOLID_FILL,BgColor);
x=LeftWin_x;
y=150;
bar(x-BSIZE,y,x+BSIZE*3,y+BSIZE*3);
sprintf(speed_str,"%3d",speed);
outtextxy(x,y,"Level");
outtextxy(x,y+10,speed_str);
outtextxy(x,y+50,"Nextbox");
}//**********************************************************
//定义新的时钟中断处理函数
void interrupt newtimer(void)
{
//调用原来的例程
(*oldtimer)();
//全局计数器变量加1
TimerCounter++;
}//********************************************************
//设置新的时钟中断处理
void SetTimer(void interrupt (*IntProc)(void))
{
//获取中断号为TIMER的中断处理函数的入口地址
oldtimer=getvect(TIMER);
//设置新的时钟中断处理过程时,禁止所有中断
disable();
//将中断号为TIMER的中断处理函数入口地址改为IntProc()函数的入口地址
setvect(TIMER,IntProc);
//开启中断
enable();
}//*********************************************************
//恢复原有的时钟中断处理过程
void KillTimer()
{
disable();
setvect(TIMER,oldtimer);
enable();
}//********************************************************
//在(x,y)位置开始,用指定的颜色显示编号为box_num的游戏方块
void show_box(int x,int y,int box_num,int color)
{
int i,ii,ls_x=x;
//指定的游戏方块不存在
if(box_num<0||box_num>=MAX_BOX)
box_num=MAX_BOX/2;
//void far setfillstyle(int pattern,int color)
//以pattern为填充模式 以color为填充颜色对指定图形进行填充
setfillstyle(SOLID_FILL,color);for(ii=0;ii<2;ii++)
{
int mask=128;//掩码,用于位运算
//单个方块填充
for(i=0;i<8;i++)
{
if(i%4==0&&i!=0)//表示转到游戏方块的下一行了
{
y+=BSIZE;
x=ls_x;
}
if((shapes[box_num].box[ii])&mask)
{
bar(x,y,x+BSIZE,y+BSIZE);
line(x,y,x+BSIZE,y);
line(x,y,x,y+BSIZE);
line(x,y+BSIZE,x+BSIZE,y+BSIZE);
line(x+BSIZE,y,x+BSIZE,y+BSIZE);
}
x+=BSIZE;
mask/=2;
}
y+=BSIZE;
x=ls_x;
}
}//***************************************************************
//清除(x,y)位置开始的编号为box_num的box
void EraseBox(int x,int y,int box_num)
{
int mask=128,t_boardx,t_boardy,n,m;
setfillstyle(SOLID_FILL,BgColor);
for(n=0;n<4;n++)
{
for (m=0;m<4;m++)
{
if(((shapes[box_num].box[n/2])&mask))
{
bar(x+m*BSIZE,y+n*BSIZE,x+m*BSIZE+BSIZE,y+n*BSIZE+BSIZE);
line(x+m*BSIZE,y+n*BSIZE,x+m*BSIZE+BSIZE,y+n*BSIZE);
line(x+m*BSIZE,y+n*BSIZE,x+m*BSIZE,y+n*BSIZE+BSIZE);
line(x+m*BSIZE,y+n*BSIZE+BSIZE,x+m*BSIZE+BSIZE,y+n*BSIZE+BSIZE);
line(x+m*BSIZE+BSIZE,y+n*BSIZE,x+m*BSIZE+BSIZE,y+n*BSIZE+BSIZE);
}
mask/=2;
if(mask==0)
mask=128;
}
}
}//*******************************************************
//同EraseBox()
void ErasePreBox(int x,int y,int box_numb)
{
int mask=128,t_boardx,t_boardy,n,m;
setfillstyle(SOLID_FILL,BgColor);
for(n=0;n<4;n++)
{
for(m=0;m<4;m++)
{
if(((shapes[box_numb].box[n/2])&mask))
{
bar(x+m*BSIZE,y+n*BSIZE,x+m*BSIZE+BSIZE,y+n*BSIZE+BSIZE);
}
mask/=2;
if(mask==0)
mask=128;
}
}
}//***************************************************************
//将新图形的游戏方块放置在游戏板上,并返回此游戏方块号
int MkNextBox(int box_numb)
{
int mask=128,t_boardx,t_boardy,n,m;
t_boardx=(Curbox_x-Sys_x)/BSIZE;
t_boardy=(Curbox_y-Sys_y)/BSIZE;
for(n=0;n<4;n++)
{
for(m=0;m<4;m++)
{
if(((shapes[current_box_numb].box[n/2])&mask))
{
//设置游戏方块
Table_board[t_boardy+n][t_boardx+m].var=1;
Table_board[t_boardy+n][t_boardx+m].color=shapes[current_box_numb].color;
}
mask=mask/2;
if(mask==0)
mask=128;
}
}setFullRow(t_boardy);
//初始化坐标
Curbox_x=Sys_x+Begin_boxs_x*BSIZE;
Curbox_y=Sys_y;
if(box_numb==-1)
box_numb=rand()%MAX_BOX;//随机产生游戏方块
current_box_numb=box_numb;
flag_newbox=false;
return (rand()%MAX_BOX);
}//***********************************************************
//用于找到满行,参数t_boardy表示当前的游戏方块号
void setFullRow(int t_boardy)
{
int n,full_numb=0,top=0;//top保存当前游戏主板在消除满行后的最高点,用于游戏主板的重绘
register m;//寄存器类型,存取速度快
for(n=t_boardy+3;n>=t_boardy;n--)
{
if(n<0||n>=Vertical_boxs)
continue;
for(m=0;m<Horizontal_boxs;m++)
{
if(!Table_board[n+full_numb][m].var)//发现有一个是空的就跳过
break; }
if(m==Horizontal_boxs)//找到满行
{
if(n==t_boardy+3)
top=DelFullRow(n+full_numb);//清除该行,并保存最高点
else
DelFullRow(n+full_numb);
full_numb++;//保存满行的行数
}
}
if(full_numb)//存在满行
{
int oldx,x=Sys_x,y=BSIZE*top+Sys_y;
oldx=x;
score=score+full_numb*10;
for(n=top;n<t_boardy+4;n++)
{
if(n>=Vertical_boxs)
continue;
//重绘游戏主板
for(m=0;m<Horizontal_boxs;m++)
{
if(Table_board[n][m].var)
{
setfillstyle(SOLID_FILL,Table_board[n][m].color);
}
else
setfillstyle(SOLID_FILL,BgColor); bar(x,y,x+BSIZE,y+BSIZE);
line(x,y,x+BSIZE,y);
line(x,y,x,y+BSIZE);
line(x,y+BSIZE,x+BSIZE,y+BSIZE);
line(x+BSIZE,y,x+BSIZE,y+BSIZE);
x+=BSIZE; }
y+=BSIZE;
x=oldx;
}
ShowScore(score);
if(speed!=score/speed_step)
{
speed=score/speed_step;
ShowSpeed(speed);
}
else
ShowSpeed(speed);}
}
//************************************************************
//处理删除行,参数y指明具体哪一行为满行
int DelFullRow(int y)
{
int n,top=0;
register m,totoal;for (n=y;n>=0;n--)
{
totoal=0;
for(m=0;m<Horizontal_boxs;m++)
{
if(!Table_board[n][m].var)
totoal++;//没有方格,对计数器加1
//上行不等于下行就把上行传给下行,此处为程序优化部分,也可不优化
if(Table_board[n][m].var!=Table_board[n-1][m].var)
{
Table_board[n][m].var=Table_board[n-1][m].var;
Table_board[n][m].color=Table_board[n-1][m].color;
}
}
//发现上面有连续的空行,提前结束
if (totoal==Horizontal_boxs)
{
top=n;
break;
}
}
return top;//返回最高点
}//********************************************************8
//判断方块是否可以移动,(x,y)为当前游戏方块位置,box_numb为游戏方块号,direction为动作标识
int MoveAble(int x,int y,int box_numb,int direction)
{
int n,m,t_boardx,t_boardy;
int mask;if(direction==MoveLeft)//如果向左移动
{
mask=128;
x-=BSIZE;
t_boardx=(x-Sys_x)/BSIZE;
t_boardy=(y-Sys_y)/BSIZE;
for(n=0;n<4;n++)
{
for(m=0;m<4;m++)
{
if((shapes[box_numb].box[n/2])&mask)
{
if((x+BSIZE*m)<Sys_x)//碰到最左边
return false;
else if(Table_board[t_boardy+n][t_boardx+m].var)//左移一个单位后,与游戏主板冲突
return false; }
mask/=2;
if(mask==0)
mask=128;
}
}
return true;
}
else if(direction==MoveRight)//右移动
{
x+=BSIZE;
t_boardx=(x-Sys_x)/BSIZE;
t_boardy=(y-Sys_y)/BSIZE;
mask=128;
for(n=0;n<4;n++)
{
for(m=0;m<4;m++)
{
if((shapes[box_numb].box[n/2])&mask)
{
if((x+BSIZE*m)>=(Sys_x+BSIZE*Horizontal_boxs))//碰到最右边
return false;
else if(Table_board[t_boardy+n][t_boardx+m].var)//与游戏主板冲突
return false; }
mask/=2;
if(mask==0)
mask=128;
}
}
return true;
}
else if(direction==MoveDown)//下移动
{
mask=128;
y+=BSIZE;
t_boardx=(x-Sys_x)/BSIZE;
t_boardy=(y-Sys_y)/BSIZE;
for(n=0;n<4;n++)
{
for(m=0;m<4;m++)
{
if((shapes[box_numb].box[n/2])&mask)
{
if((y+BSIZE*n)>=(Sys_y+BSIZE*Vertical_boxs)||Table_board[t_boardy+n][t_boardx+m].var)//碰到最下边或向下有冲突
{
flag_newbox=true;
break;
}
}
mask/=2;
if(mask==0)
mask=128;
}
}
if(flag_newbox)
return false;
else
return true;
}
else if(direction==MoveRoll)//旋转
{
mask=128;
t_boardx=(x-Sys_x)/BSIZE;
t_boardy=(y-Sys_y)/BSIZE;
for(n=0;n<4;n++)
{
for(m=0;m<4;m++)
{
if((shapes[box_numb].box[n/2])&mask)
{
if((y+BSIZE*n)>=(Sys_y+BSIZE*Vertical_boxs))//碰到最下边
return false;
if((x+BSIZE*n)>=(Sys_x+BSIZE*Horizontal_boxs))//碰到最左边
return false;
if((x+BSIZE*m)>=(Sys_x+BSIZE*Horizontal_boxs))//碰到最右边
return false;
else if (Table_board[t_boardy+n][t_boardx+m].var)//向下有冲突
{
return false;
}
}
mask/=2;
if(mask==0)
mask=128;
}
}
return true;
}
else
return false;
}