// 程序名称:俄罗斯方块
// 编译环境:Visual C++ 6.0 
// 程序编写:龙航四海
#include <graphics.h> 
#include<windows.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>
#include<math.h>
#include<stdio.h>
#define box_k 10
#define box_g 18
#define BEGIN 10
#define WIDTH 24
struct board
{
	int hang;
	int lie;
	int live;
}board_box[box_k*box_g+1];
char direction,str1[100],grade_temp[3];
int box_num,board_num,delay=800,box1[4],box3[4],shape[4],grade_num=0;
//box1[]数组保存的是移动的方块是底板中的第多少个,
//box3[]则存放的是新产生的方块的位置
int box[28][4]={{0xf0,0,0,0},{0x20,0x20,0x20,0x20},{0xf0,0,0,0},{0x20,0x20,0x20,0x20},
			{0x60,0x60,0,0},{0xc0,0xc0,0,0},{0xc0,0xc0,0,0},{0xc0,0xc0,0,0},
			{0x60,0x30,0,0},{0x40,0xc0,0x80,0},{0xc0,0x60,0,0},{0x40,0xc0,0x80,0},
			{0x30,0x60,0,0},{0x80,0xc0,0x40,0},{0x60,0xc0,0,0},{0x80,0xc0,0x40,0},
			{0x20,0x70,0,0},{0x40,0x60,0x40,0},{0xe0,0x40,0,0},{0x40,0xc0,0x40,0},
			{0x70,0x10,0,0},{0x40,0x40,0xc0,0},{0x80,0xe0,0,0},{0xc0,0x80,0x80,0},
			{0x70,0x40,0,0},{0xc0,0x40,0x40,0},{0x20,0xe0,0,0},{0x80,0x80,0xc0,0}
			};

void drawline(int left, int top, int right, int bottom,COLORREF col1,COLORREF col2)
{
	int i;
	setcolor(col1);
	for(i=1;i<WIDTH;i++)
	{
		line(left+i,top,left+i,bottom);
	}
	setcolor(col2);
	line(left,top,left,bottom);
	line(left,bottom,right,bottom);
	line(left,top,right,top);
	line(right,top,right,bottom);
}
void  move();			//方块移动函数声明

void creat_box()		//随机产生新方块并保存新方块的位置
{
	int i,j,num2=0,temp;
	srand(time(0));
	box_num=rand()%28;
	for(i=0;i<4;i++)
	{
		shape[i]=0xf0&box[box_num][i];
	}
	for(i=0;i<4;i++)
	{
		if(shape[i]>127)
		{
			drawline(BEGIN+box_k*WIDTH/2-WIDTH,
					 i*WIDTH+BEGIN,
					 BEGIN+box_k*WIDTH/2,
					 WIDTH+i*WIDTH+BEGIN,WHITE,BLACK);
			box1[num2]=(i+1)*box_k+box_k/2-1;
			num2++;
		}
		else
		{
			board_box[(i+1)*box_k+box_k/2].live=0;	
		}
		for(j=0;j<3;j++)
		{
			shape[i]=shape[i]<<1;
			temp=shape[i]&0x80;
			if(temp!=0)
			{
				drawline(BEGIN+box_k*WIDTH/2+j*WIDTH,
						 i*WIDTH+BEGIN,
						 BEGIN+box_k*WIDTH/2+WIDTH+j*WIDTH,
						 WIDTH+i*WIDTH+BEGIN,WHITE,BLACK);
				box1[num2]=(i+1)*box_k+box_k/2+j;
				num2++;
			}
		}		
	}
	for(i=0;i<4;i++)
	{
		box3[i]=box1[i];
		box1[i]=box1[i]-box_k;
		board_box[box1[i]].live=2;
	}
	Sleep(delay);
}

int leapchange()
{
	int i,j,flag3=1;
	for(i=0;i<4;i++)
	{
		if((box1[i])%box_k!=0)
			continue;
		for(j=0;j<4;j++)
		{
			if((box1[j])%box_k==box_k-1)
				flag3=2;
		}
	}
	for(i=0;i<4;i++)
	{
		if(box1[i]/box_k==box_g-1)
		{
			flag3=2;
			return flag3;
		}
	}
	return flag3;
}

void bianxing()			//当'w'键按下时,方块变形
{
	int i,j,num4=0,temp,num5,box2[4],flag2=0,flag3=0;
	num5=box1[0];
	for(i=0;i<4;i++)
	{
		box2[i]=box1[i];
	}
	for(i=0;i<4;i++)
	{
		drawline(board_box[box1[i]].hang,
				 board_box[box1[i]].lie,
	 			 board_box[box1[i]].hang+WIDTH,
				 board_box[box1[i]].lie+WIDTH,BLACK,WHITE);
		 board_box[box1[i]].live=0;
	}
	if(box_num%4==3)
		box_num=box_num-3;
	else
		box_num++;
	for(i=0;i<4;i++)
	{		
		shape[i]=0xf0&box[box_num][i];
		if(shape[i]>127)
		{
			if(board_box[num5+i*box_k].live==1)
				flag2++;
			box1[num4]=num5+i*box_k;
			num4++;							
		}
		for(j=0;j<3;j++)
		{
			shape[i]=shape[i]<<1;
			temp=shape[i]&0x80;
			if(temp!=0)
			{
				if(board_box[num5+i*box_k+j+1].live==1)
					flag2++;
				box1[num4]=num5+i*box_k+j+1;
				num4++;
			}			
		}
	}
	flag3=leapchange();
	if(flag2!=0||flag3==2)	//当flag2不等于0时表示变形失败,此时恢复原来的形状
	{
		if(box_num%4==0)
			box_num=box_num+3;
		else
			box_num--;
		for(i=0;i<4;i++)
		{
			box1[i]=box2[i];
			drawline(board_box[box1[i]].hang,
					 board_box[box1[i]].lie,
					 board_box[box1[i]].hang+WIDTH,
					 board_box[box1[i]].lie+WIDTH,WHITE,BLACK);
			board_box[box1[i]].live=2;
		}
	}
	else		//变形成功
	{
		for(i=0;i<4;i++)
		{
			drawline(board_box[box1[i]].hang,
					 board_box[box1[i]].lie,
					 board_box[box1[i]].hang+WIDTH,
					 board_box[box1[i]].lie+WIDTH,WHITE,BLACK);
			board_box[box1[i]].live=2;
		}
		Sleep(delay);
	}
}

void print()
{
	int gdriver=DETECT,gmode;
	int i,j;
    initgraph(&gdriver,&gmode,"c:\\tc");    
    cleardevice();       					
    setbkcolor(GREEN);
	for(j = BEGIN;j <=BEGIN+ box_g*WIDTH;j +=WIDTH)
	   for(i= BEGIN;i<=BEGIN+box_k*WIDTH;i++)
	   {
		   if(j==BEGIN||j==BEGIN+box_g*WIDTH||i==BEGIN||i==BEGIN+box_k*WIDTH)
			   putpixel(i,j,BLUE);
		   else
	   			putpixel(i,j,WHITE);      
	   }
	for(i = BEGIN;i <= BEGIN+box_k*WIDTH;i+=WIDTH)
	   for(j = BEGIN;j <= BEGIN+box_g*WIDTH;j++)             
	   {	
		   if(j==BEGIN||j==BEGIN+box_g*WIDTH||i==BEGIN||i==BEGIN+box_k*WIDTH)
				putpixel(i,j,BLUE);
		   else
			   putpixel(i,j,WHITE);
	   }

	 for(board_num=0;board_num<box_k*box_g;board_num++)
	 {
	      board_box[board_num].lie=BEGIN+(board_num/box_k)*WIDTH;
		  board_box[board_num].hang=(board_num%box_k)*WIDTH+BEGIN;
		  board_box[board_num].live=0;
	 }
}

int leap()			//判断方块形状
{
	int num3,shu;		//num3存储方块周围的空白数
	shu=box1[3]-box1[0];
	if(shu<box_k)
		num3=1;
	else if(shu<box_k+3)
		num3=2;
	else if(shu<2*box_k+3)
		num3=3;
	else 
		num3=4;
	return num3;
}

void  move()			//方块移动及变换,并且更新方块的存储位置
{
	int i,flag=0,flag1=0,num;//扫描变量行和列;
	num=leap();			//num为当前方块想要移动所需的移动方向的空格数
	if(direction=='d')
	{
		for(i=3;i>=0;i--)
		{
			if(board_box[box1[i]+1].live==0)
				flag++;		//flag为当前方块移动方向的空白方格数
			if((box1[i]%box_k)<box_k-1)
				flag1++;
		}
		if(flag==num&&flag1==4)		//两者相等时,表示可以移动
		{	
			flag=0;
			flag1=0;
			for(i=3;i>=0;i--)
			{	
				board_box[box1[i]].live=0;
				board_box[box1[i]+1].live=2;
				drawline(board_box[box1[i]].hang,		//消除移动前的方块
			             board_box[box1[i]].lie,
	 					 board_box[box1[i]].hang+WIDTH,
					     board_box[box1[i]].lie+WIDTH,BLACK,WHITE);
				drawline(board_box[box1[i]+1].hang,		//产生移动后的方块
						 board_box[box1[i]].lie,
	 				     board_box[box1[i]+1].hang+WIDTH,
						 board_box[box1[i]].lie+WIDTH,WHITE,BLACK);	
				box1[i]++;
			}
			Sleep(delay);
		}		
	}
	if(direction=='a')
	{
		for(i=0;i<4;i++)
		{
			if(board_box[box1[i]-1].live==0)
				flag++;		//flag为当前方块移动方向的空白方格数
			if((box1[i]%box_k)>0)
				flag1++;
		}
		if(flag==num&&flag1==4)		//两者相等时,表示可以移动
		{	
			flag=0;
			flag1=0;
			for(i=0;i<4;i++)
			{
				board_box[box1[i]-1].live=2;
				board_box[box1[i]].live=0;
				drawline(board_box[box1[i]].hang,		//消除移动前的方块
						 board_box[box1[i]].lie,
	 				   	 board_box[box1[i]].hang+WIDTH,
						 board_box[box1[i]].lie+WIDTH,BLACK,WHITE);
				drawline(board_box[box1[i]-1].hang,		//产生移动后的方块
						 board_box[box1[i]].lie,
	 					 board_box[box1[i]-1].hang+WIDTH,
						 board_box[box1[i]].lie+WIDTH,WHITE,BLACK);
				box1[i]--;
			}
			Sleep(delay);
		}
	}
	if(direction=='s')
	{
		for(i=3;i>=0;i--)
		{
			if(board_box[box1[i]+box_k].live==0||board_box[box1[i]+box_k].live==2)
				flag++;
			if(box1[i]/box_k<box_g-1)
				flag1++;
		}
			if(flag==4&&flag1==4)
			{
				flag1=0;
				flag=0;
				for(i=3;i>=0;i--)
				{					
					board_box[box1[i]+box_k].live=2;
					board_box[box1[i]].live=0;
					drawline(board_box[box1[i]].hang,		//消除移动前的方块
							 board_box[box1[i]].lie,
	 						 board_box[box1[i]].hang+WIDTH,
							 board_box[box1[i]].lie+WIDTH,BLACK,WHITE);				
					drawline(board_box[box1[i]].hang,		//产生移动后的方块
							 board_box[box1[i]].lie+WIDTH,
	 						 board_box[box1[i]].hang+WIDTH,
							 board_box[box1[i]].lie+2*WIDTH,WHITE,BLACK);
					box1[i]=box1[i]+box_k;
				}
				Sleep(delay);
			}
	}
	if(direction=='w')
	{	
		bianxing();
	}
	if(direction==' ')
		system("pause");
	if(direction=='q')
	{
		getch();
		exit(0);
	}
}

void check()	//满行检测
{
	int i,j,k,num6;
	for(i=box_g-1;i>1;i--)
	{
		num6=0;
		for(j=0;j<box_k;j++)
		{
			if(board_box[i*box_k+j].live==1)
				num6++;
		}
		if(num6==box_k)
		{
			for(k=0;k<box_k;k++)
			{
				drawline(board_box[i*box_k+k].hang,
						 board_box[i*box_k+k].lie,
						 board_box[i*box_k+k].hang+WIDTH,
						 board_box[i*box_k+k].lie+WIDTH,BLACK,WHITE);
				board_box[i*box_k+k].live=0;
			}
			for(k=i*box_k-1;k>=0;k--)
			{
				if((board_box[k].live==1)&&(board_box[k+box_k].live==0))
				{
					drawline(board_box[k].hang,
							 board_box[k].lie,
							 board_box[k].hang+WIDTH,
							 board_box[k].lie+WIDTH,BLACK,WHITE);
					board_box[k].live=0;
					drawline(board_box[k].hang,
							 board_box[k+box_k].lie,
							 board_box[k].hang+WIDTH,
							 board_box[k+box_k].lie+WIDTH,WHITE,BLACK);					
					board_box[k+box_k].live=1;
				}
			}
			grade_num++;
			itoa(grade_num,grade_temp,10);
			outtextxy(box_k*WIDTH+2*BEGIN+3*WIDTH,50+3*WIDTH,grade_temp);
			delay=delay-100;
			if(delay==100)
			{
				outtextxy(box_k*WIDTH+2*BEGIN+3*WIDTH,50+4*WIDTH,"你好棒!");
				getch();
				exit(0);
			}
		}
	}
}

void life()	//判断游戏是否结束
{
	int i,flag4=0;
	for(i=0;i<4;i++)
	{
		if(board_box[box3[i]+box_k].live==1)
			flag4++;
	}
	if(flag4!=0)	//表示新产生的方块下面有方块,游戏结束
	{
		flag4=0;
		outtextxy(box_k*WIDTH+2*BEGIN+3*WIDTH,50+5*WIDTH,"game over!");
		getch();
		exit(1);
	}
}

void state()	//方块的状态,是否变成底板
{
	int i,flag3=0;//num6=0;//num6默认为0,表示方块可以下移
	for(i=0;i<4;i++)
	{
		if(board_box[box1[i]+box_k].live==1||box1[i]/box_k==box_g-1)
		flag3++;
	}
	if(flag3!=0)	//表示方块不可以下移
	{
		flag3=0;
		for(i=0;i<4;i++)
		{
			board_box[box1[i]].live=1;
		}
		check();
		creat_box();
		life();
	}
}
void init()		//初始化
{
	outtextxy(box_k*WIDTH+2*BEGIN,50,"游戏规则");
	outtextxy(box_k*WIDTH+2*BEGIN,50+WIDTH,"任意键开始,空格键暂停,'q'退出");
	outtextxy(box_k*WIDTH+2*BEGIN,50+2*WIDTH,"'a'键向左移,'d'键向右移,'w'键变形");
	outtextxy(box_k*WIDTH+2*BEGIN,50+3*WIDTH,"当前得分:");
	itoa(grade_num,grade_temp,10);
	outtextxy(box_k*WIDTH+2*BEGIN+3*WIDTH,50+3*WIDTH,grade_temp);
	creat_box();
}
void main()
{
	print();
//	char keyhit;
	init();
	while(1)
	{
		if(!kbhit())
			direction='s';
		else
			direction=getch();
		move();
		state();
	}
	getch();
}