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