俄罗斯方块(C语言实现)

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include <windows.h>

#define BLKCHAR "■"
#define NUCHAR "□"
#define P_X 8
#define P_Y 5
#define M_X 10
#define M_W 58
#define M_Y 13
#define M_H 11
#define NEXTBLOCK_X 29
#define NEXTBLOCK_Y 10
#define PL_W 12
#define PL_H 20
#define N_S 4
#define N_T 7
#define B_W 4
#define B_H 4
#define INFO_NAME_X 32
#define INFO_NAME_Y 20
#define INFO_LEVEL_X 32
#define INFO_LEVEL_Y 23
#define INFO_MARK_X 32
#define INFO_MARK_Y 26
#define TIME_WAIT 10
#define TIME_FALL 50/curuser.level
#define WIN_CWSIZE "mode con cols=80 lines=30"
#define FILE_WELCOME "welcome"
#define FILE_OUTLINE "outline"
#define FILE_GAMEOVER "gameover"
#define FILE_BLOCKS "blocks"
#define FILE_PROGRESS "progress"
#define FILE_HELP "help"
#define DEF_NAME "NOMANE"


struct tpblk
{
	int x, y;
	int type;
	int state;
};

struct tpuser
{
	long level;
	long mark;
	char name[30];
}curuser;

int panel[PL_H][PL_W]={0};
int blocks[N_T][N_S][B_H][B_W];


void gotoxy(int x,int y)
{
	COORD c;
	c.X = x*2, c.Y = y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}  
struct tpblk initblock()
{
	struct tpblk rdblk;
	rdblk.x=PL_W/2;
	rdblk.y=-B_H+2;
	rdblk.type=rand()%N_T;
	rdblk.state=rand()%N_S;
	return rdblk;
}

void prtfiletoscr(char *file)
{
	FILE *fp;
	char str[100];
	system("cls");
	gotoxy(0,0);
	if((fp=fopen(file,"r"))==NULL)
	{
		printf("打开文件:\"%s\"失败!\n",file);
		getch();
		exit(0);
	}
	while(fgets(str,100,fp)!=NULL)
		printf("%s",str);
	fclose(fp);
}
//下一个方块
void prtnxtblk(struct tpblk nxtblock)
{
	int i,j;
	for(i=0;i<B_H;i++)
		for(j=0;j<B_W;j++)
		{					
			gotoxy(NEXTBLOCK_X+j,NEXTBLOCK_Y+i);
			printf("%s",blocks[nxtblock.type][nxtblock.state][i][j]?BLKCHAR:NUCHAR);
		}

}
//打印面板
void prtpanel()
{
	int i,j;
	for(i=0;i<PL_H;i++)
	{
		gotoxy(P_X,P_Y+i);
		for(j=0;j<PL_W;j++)
		{
			printf("%s",panel[i][j]?BLKCHAR:NUCHAR);
		}
		//putchar(0);
	}

}
//返回 是否有效
int onkeyboard(int c,struct tpblk *block)
{	
	int i,j;
	switch(c)
	{
	case 75:
		block->x--;break;
	case 77:
		block->x++;break;
	case 72:
	case ' ':
	case 13:
		if(block->state>=N_S-1)block->state=0;
		else block->state++;break;
	case -1://自动下落
	case 80:
		block->y++;break;
	case 'p'://暂停
		gotoxy(5,3);
		printf("暂停中...按任意键恢复。");
		getch();
		gotoxy(5,3);
		printf("                       ");
		return 2;
		break;
	case 27://esc
		return -1;
		break;
	default:
		return 2;
	}
	for(i=0;i<B_H;i++)
		for(j=0;j<B_W;j++)
			if(blocks[block->type][block->state][i][j])//保证有点	
			{
				if(block->x+j<0||block->x+j>=PL_W)return 1;//左右边界
				if(block->y+i>=PL_H)return 1;//是否到底
				if(block->y+i>=0&&panel[block->y+i][block->x+j])return 1;//与其他块碰撞
			}
	return 0;
}

void moveblock(struct tpblk oldblock,struct tpblk newblock)
{
	int i,j;
	//清除原有的方块
	for(i=0;i<B_H;i++)
		for(j=0;j<B_W;j++)
		{
			if(blocks[oldblock.type][oldblock.state][i][j]&&oldblock.y+i>=0)
			{
				gotoxy(P_X+oldblock.x+j,P_Y+oldblock.y+i);
				printf("%s",NUCHAR);		
			}
		}
		
		//画新方块
		for(i=0;i<B_H;i++)
			for(j=0;j<B_W;j++)
			{					
				if(blocks[newblock.type][newblock.state][i][j]&&newblock.y+i>=0)
				{//面板中
						gotoxy(P_X+newblock.x+j,P_Y+newblock.y+i);
						printf("%s",BLKCHAR);
				}
			}
}

void loadblocks()
{
	FILE *fp;
	int i,j,cx,cy,ch;
	//blocks初始化
	if((fp=fopen(FILE_BLOCKS,"rt"))==NULL)
	{
		gotoxy(0,0);
		printf("打开文件:%s失败\n",FILE_BLOCKS);
		getch();
		exit(0);
	}
	for(i=0;i<N_T;i++)
		for(j=0;j<N_S;j++)
			for(cy=0;cy<B_H;cy++)
				for(cx=0;cx<B_W;cx++)
				{
					while((ch=fgetc(fp)-48)!=0&&ch!=1)
					{	if(ch==EOF)
						{
							gotoxy(0,0);
							printf("读取文件:%s错误!\n",FILE_BLOCKS);
						 	getch();exit(0);
						}
					}
						blocks[i][j][cy][cx]=ch;
				}
	fclose(fp);	
}


void saveprogress(struct tpuser user)
{
	FILE* fp;
	struct tpuser tmpuser[10];
	int iffind=0,i;
	if(!strcmp(user.name,DEF_NAME)) return;
	if((fp=fopen(FILE_PROGRESS,"rb+"))==NULL)
	{
		printf("打开文件:\"%s\"失败!\n",FILE_PROGRESS);
		getch();
		exit(0);
	}	
	for(i=0;iffind==0&&fread(&tmpuser[i],sizeof(struct tpuser),1,fp);i++)
	{
		if(!strcmp(user.name,tmpuser[i].name))
			iffind=1;
	}

	if(iffind)
		fseek(fp,-(long)sizeof(struct tpuser),1);
	else
		fseek(fp,0,2);
	fwrite(&user,sizeof(struct tpuser),1,fp);
	if(ferror(fp))
	{
		printf("存档错误!忽略按Enter\n",FILE_PROGRESS);
		while(getch()!=13);
	}
	fclose(fp);

}

int onbumpbtm(struct tpblk block)
{
	int i,j,lf,nlf=0;
	//合并
	for(i=0;i<B_H;i++)
		for(j=0;j<B_W;j++)
		{
			if(blocks[block.type][block.state][i][j])
			{
				//游戏结束??
				if(block.y+i<=0)return -1;
				//合并
				panel[block.y+i][block.x+j]=1;
				gotoxy(P_X+block.x+j,P_Y+block.y+i);
				printf("%s",BLKCHAR);
			}
		}
		//是否满行?
		for(i=0;i<B_H;i++)
		{
			lf=1;
			for(j=0;j<PL_W;j++)
				if(!panel[block.y+i][j])
				{lf=0;break;}
				
				if(lf)//满行
				{
					for(i=block.y+i;i>0;i--)
						for(j=0;j<PL_W;j++)
							panel[i][j]=panel[i-1][j];
						nlf++;
				}
				
		}
		curuser.mark+=nlf*nlf;
		saveprogress(curuser);
		return lf;
}


void prtinfo()
{
	gotoxy(INFO_NAME_X,INFO_NAME_Y);
	printf("%s",curuser.name);
	gotoxy(INFO_LEVEL_X,INFO_LEVEL_Y);
	printf("%d",curuser.level);
	gotoxy(INFO_MARK_X,INFO_MARK_Y);
	printf("%d",curuser.mark);

}
void clspanel()
{
	int i,j;
	for(i=0;i<PL_H;i++)
		for(j=0;j<PL_W;j++)
			panel[i][j]=0;

}

struct tpuser loadprogress()
{
	struct tpuser user[10]={0};
	FILE* fp;
	int i,j,c,sel=0,alex=0;
	if((fp=fopen(FILE_PROGRESS,"rb"))==NULL)
	{
		printf("打开文件:\"%s\"失败!\n",FILE_PROGRESS);
		getch();
		exit(0);
	}

	for(i=M_Y;i<=M_Y+M_H;i++)
	{
		gotoxy(M_X/2,i);
		for(j=M_X/2;j<=M_X/2+M_W;j++)
		{
			putchar(' ');
		}
	}
	for(i=0;fread(&user[i],sizeof(struct tpuser),1,fp);i++)
	{
		gotoxy(M_X+5,M_Y+2+i);
		printf("NAME:%s LEVEL:%ld MARK:%ld",user[i].name,user[i].level,user[i].mark);
	}
	gotoxy(M_X+5,M_Y+2+i);
	printf("[NEW USER]");

	fclose(fp);
	gotoxy(M_X+3,M_Y+2+sel); printf("◆");
	while(1)
	{
		c=getch();
		switch(c)
		{
		case 72://up
			gotoxy(M_X+3,M_Y+2+sel); printf(" ");
			if(sel<=0)sel=i;
			else sel--;
			gotoxy(M_X+3,M_Y+2+sel); printf("◆");

			break;
		case 80://down
			gotoxy(M_X+3,M_Y+2+sel); printf(" ");
			if(sel>=i)sel=0;
			else sel++;
			gotoxy(M_X+3,M_Y+2+sel); printf("◆");
			break;
		case 27://esc
			user[0].level=-1;
			return user[0];
			break;
		case 13://enter
			if(sel==i)//New User
			{
				gotoxy(M_X+5,M_Y+2+i+1);
				printf("Input Name:");
				scanf("%s",user[i].name);
				for(j=0;j<i&&!alex;j++)
				{
					if(!strcmp(user[j].name,user[i].name))
					{
						gotoxy(M_X+5,M_Y+2+i+1);
						printf("\"%s\"already exist!",user[i].name);
						getch();
						alex=1;
					}
				}
				if(!alex)
				{
					user[i].level=1L;
					user[i].mark=0;
					saveprogress(user[i]);
				}
			}
			if(user[sel].name<=0||user[sel].level<=0||user[sel].mark<0) user[sel].level=-1;
			return user[sel];
			break;
		default:;
		}
	}
	user[0].level=-1;
	return user[0];
}

int gameloop()
{
	int iskh=0,c,val,timec=0,bumpst=0,timewait=TIME_WAIT;
	struct tpblk curblock,nxtblock,tmpblock;
	
	curblock=initblock();
	nxtblock=initblock();
	
	prtfiletoscr(FILE_OUTLINE);
	prtpanel();
	prtinfo();
	prtnxtblk(nxtblock);
	while(1)
	{
		
		if(kbhit()||iskh)
		{
			tmpblock=curblock;
			if(!iskh)c=getch();
				val=onkeyboard(c,&tmpblock);
				if(val==2) continue;
				else if(val==-1) return 2;
				else if(val==0)
				{
					moveblock(curblock,tmpblock);
					curblock=tmpblock;
				}
				else if(val==1 && (c==-1||c==80)) //向下撞到
				{
					bumpst=onbumpbtm(curblock);
					if(bumpst==-1) return 1;
					else
					{
						//是否过关
						if(curuser.mark>=100*curuser.level)
						{
							clspanel();
							curuser.mark=0;
							curuser.level++;
						}

						prtinfo();
						prtpanel();
					}
					
					//产生新的BLOCK
					curblock=nxtblock;
					nxtblock=initblock();
					//画下一个方块
					prtnxtblk(nxtblock);
					
				}
				if(c==80)timewait=TIME_WAIT/2;
			

			gotoxy(0,0);
			iskh=0;
		}
		Sleep(timewait);
		timewait=TIME_WAIT;
		if(timec>=TIME_FALL)
		{
			c=-1;
			iskh=1;
			timec=0;
		}
		else timec++;
	}
	return 0;
}


int showmenu()
{
	int i,j,c,cursel=0;
	gotoxy(M_X,M_Y);
	for(i=M_Y;i<=M_Y+M_H;i++)
	{
		gotoxy(M_X/2,i);
		for(j=M_X/2;j<=M_X/2+M_W;j++)
		{
		//	putchar('\b');
		//	gotoxy(j,i);
			putchar(' ');
		}
	}
	gotoxy(M_X+5,M_Y+2);
	printf("快 速 游 戏");
	gotoxy(M_X+5,M_Y+4);
	printf("读 取 存 档");
	gotoxy(M_X+5,M_Y+6);
	printf("帮 助");
	gotoxy(M_X+5,M_Y+8);
	printf("退 出");
	gotoxy(M_X+3,M_Y+2+cursel*2); printf("◆");

	while(1)
	{
		c=getch();
		switch(c)
		{
		case 72://up
			gotoxy(M_X+3,M_Y+2+cursel*2); printf(" ");
			if(cursel<=0)cursel=3;
			else cursel--;
			gotoxy(M_X+3,M_Y+2+cursel*2); printf("◆");

			break;
		case 80://down
			gotoxy(M_X+3,M_Y+2+cursel*2); printf(" ");
			if(cursel>=3)cursel=0;
			else cursel++;
			gotoxy(M_X+3,M_Y+2+cursel*2); printf("◆");
			break;
		case 27://esc
			return 3;
			break;
		case 13://enter
			return cursel;
			break;
		default:;
		}
	}

	return 0;
}

int main()
{
	int looprst=0,ch;
    CONSOLE_CURSOR_INFO cursor_info = {1, 0}; 
	struct tpuser tmpuser;
	system("color F8");
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
	system(WIN_CWSIZE);

	loadblocks();

	while(1)
	{
		prtfiletoscr(FILE_WELCOME);	
		if(getch()==27) break;
		switch(showmenu())
		{
		case 0://fast mode
			strcpy(curuser.name,DEF_NAME);
			curuser.level=1;
			curuser.mark=0;
			clspanel();
			looprst=gameloop();
			break;
		case 1://read
			tmpuser=loadprogress();
			if(tmpuser.level!=-1)
			{
				curuser=tmpuser;
				clspanel();
				looprst=gameloop();	
			}
			break;
		case 2://help
			prtfiletoscr(FILE_HELP);
			getch();
			break;
		case 3://esc
			break;
		}
		if(looprst==1)
		{
			prtfiletoscr(FILE_GAMEOVER);
			gotoxy(10,3);
			printf("[%s] [LEVEL %ld] [MARK %ld]",curuser.name,curuser.level,curuser.mark);
			while((ch=getch())!=27&&ch!=13);
			looprst=0;
		}
	}
	return 0;
}

 

 

“blocks”:方块的定义,0代表无点,1代表有点,纯文本,增加或减少方块时须修改源代码中的#define N_S 4和#define N_T 7,N_S代表每个方块能够变形的多少;N_T代表总共有多少种方块。
“welcome”:游戏封面,纯文本,可直接修改。
“gameover”:游戏结束显示的画面,纯文本,可直接修改。
“help”:帮助页面显示的画面,纯文本,可直接修改。
“outline”:游戏时显示的轮廓,纯文本,可直接修改。
“progress”:保持进度的文件,二进制文件,需要用hex等软件修改,依次为玩家名char[30]、等级long、分数long。
以上文件必须和BlockGame.exe在同一目录下,否则会提示出错。

 

源代码地址: http://pan.baidu.com/share/link?shareid=107251&uk=337919926

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值