#include <stdio.h> #include <graphics.h> #include <Windows.h> #include <time.h> #include <conio.h> int score=0;//总分 int rank=0;//等级 #define BLOCK_COUNT 5 #define BLOCK_WIDTH 5 #define BLOCK_HEIGHT 5 #define UNIT_SIZE 20 #define START_X 130 #define START_Y 30 #define KEY_UP 72 #define KEY_RIGHT 77 #define KEY_LEFT 75 #define KEY_DOWN 80 #define KEY_SPACE 32 int speed=500; int minX=30; int minY=30; typedef enum { BLOCK_UP, BLOCK_RIGHT, BLOCK_DOWN, BLOCK_LEFT }block_dir_t; typedef enum { MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT, }move_dir_t; int NextIndex=-1;//下一个方块序号(种类) int BlockIndex=-1;//当前方块序号(种类) int color[BLOCK_COUNT]= { GREEN,CYAN,MAGENTA,BROWN,YELLOW }; int visit[30][15];//访问数组 int markColor[30][15];//需要定义这个,因为要判断什么颜色的方块停留在这里(表示对应位置的颜色) int block[BLOCK_COUNT*4][BLOCK_HEIGHT][BLOCK_WIDTH]= { // | 形方块markColor { 0,0,0,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,0,0,0, 0,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,0,0,0, 0,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0 }, // L 形方块 { 0,0,0,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,1,1,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,0,0,0, 0,1,1,1,0, 0,1,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,1,1,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,0,1,0, 0,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0 }, // 田 形方块 { 0,0,0,0,0, 0,1,1,0,0, 0,1,1,0,0, 0,0,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,1,1,0,0, 0,1,1,0,0, 0,0,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,1,1,0,0, 0,1,1,0,0, 0,0,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,1,1,0,0, 0,1,1,0,0, 0,0,0,0,0, 0,0,0,0,0 }, // T 形方块 { 0,0,0,0,0, 0,1,1,1,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,0,1,0, 0,0,1,1,0, 0,0,0,1,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,1,0,0, 0,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,1,0,0,0, 0,1,1,0,0, 0,1,0,0,0, 0,0,0,0,0 }, // Z 形方块 { 0,0,0,0,0, 0,1,1,0,0, 0,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,1,0,0, 0,1,1,0,0, 0,1,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,1,1,0,0, 0,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0 }, { 0,0,0,0,0, 0,0,1,0,0, 0,1,1,0,0, 0,1,0,0,0, 0,0,0,0,0 }, }; //欢迎界面 void welcome(void) { //初始化画布 initgraph(550,660); //设置窗口标题 HWND window=GetHWnd();//获取窗口 SetWindowText(window,"俄罗斯方块");//设置窗口标题 //设置文本的字体样式 setfont(40,0,"微软雅黑"); setcolor(WHITE); outtextxy(205,200,"俄罗斯方块"); setfont(22,0,"楷体"); outtextxy(175,300,"编程,从俄罗斯方块开始!"); Sleep(3000); } //初始化游戏场景 void initGameScene(void) { char str[16]; //清除屏幕 cleardevice(); setcolor(WHITE); rectangle(27,27,336,635); rectangle(29,29,334,633); rectangle(370,50,515,195); setfont(24,0,"楷体"); setcolor(LIGHTGRAY); outtextxy(405,215,"下一个"); setcolor(RED); outtextxy(405,280,"分数"); sprintf(str,"%d",score); outtextxy(415,310,str); outtextxy(405,375,"等级"); sprintf(str,"%d",rank); outtextxy(425,405,str); //操作说明 setcolor(LIGHTBLUE); outtextxy(390,475,"操作说明:"); outtextxy(390,500,"↑:旋转"); outtextxy(390,525,"←:左移"); outtextxy(390,550,"↓:下移"); outtextxy(390,575,"→:右 移"); outtextxy(390,600,"空格:暂停"); } void clearBlock() { setcolor(BLACK); setfont(23,0,"楷体"); for(int i=0;i<BLOCK_HEIGHT;i++) { for(int j=0;j<BLOCK_WIDTH;j++) { int x=391+j*UNIT_SIZE; int y=71+i*UNIT_SIZE; outtextxy(x,y,"■"); } } } //清除指定位置指定方向的方块 //参数x:方块的左上角的x坐标 //参数y:方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离 void clearBlock(int x,int y,block_dir_t dir) { setcolor(BLACK); int id=BlockIndex*4+dir; y=y+START_Y; for(int i=0;i<5;i++) { for(int j=0;j<5;j++) { if(block[id][i][j]==1) { //擦除该方块的第i行的第j列 outtextxy(x+j*20,y+i*20,"■"); } } } } //在右上角区域中,绘制下一个方块 void drawBlock(int x,int y) { setcolor(color[NextIndex]); setfont(23,0,"楷体"); for(int i=0;i<BLOCK_HEIGHT;i++) { for(int j=0;j<BLOCK_WIDTH;j++) { if(block[NextIndex*4][i][j]==1) { outtextxy(x+j*UNIT_SIZE,y+i*UNIT_SIZE,"■");//这点注意不要从新定义个x和y,否则会叠加值,致使方块位置不对 } } } } void drawBlock(int x,int y,int blockIndex,block_dir_t dir) { setcolor(color[blockIndex]); setfont(23,0,"楷体"); int id=blockIndex*4+dir; for(int i=0;i<BLOCK_HEIGHT;i++) { for(int j=0;j<BLOCK_WIDTH;j++) { if(block[id][i][j]==1) { int x2=x+j*UNIT_SIZE; int y2=y+i*UNIT_SIZE; outtextxy(x2,y2,"■"); } } } } void nextblock() { int x=391; int y=71; clearBlock();//清除右上角区域 //随机选择一种方块 srand(time(NULL));//使用时间函数的返回值,来作为随机种子 NextIndex=rand()%BLOCK_COUNT; drawBlock(x,y); } //如果在指定位置可以向指定方向移动,就返回1,否则就返回0 int moveable(int x0,int y0,move_dir_t moveDir,block_dir_t blockDir) { //计算当前方块的左上角在30*15的游戏区中的位置(第多少行,第多少列) int x=(y0-minY)/UNIT_SIZE; int y=(x0-minX)/UNIT_SIZE; int id=BlockIndex*4+blockDir;//算出是哪种类型的方块 int ret=1; if(moveDir==MOVE_DOWN) { for(int i=0;i<5;i++) { for(int j=0;j<5;j++) { if(block[id][i][j]==1&& (x+i+1>=30||visit[x+i+1][y+j]==1)) { ret=0; } } } }else if(moveDir==MOVE_LEFT) { for(int i=0;i<5;i++) { for(int j=0;j<5;j++) { if(block[id][i][j]==1&& (y+j==0||visit[x+i][y+j-1]==1)) { ret=0; } } } }else if(moveDir==MOVE_RIGHT) { for(int i=0;i<5;i++) { for(int j=0;j<5;j++) { if(block[id][i][j]==1&& (y+j+1>=15||visit[x+i][y+j+1]==1)) { ret=0; } } } } return ret; } void failCheck() { if(!moveable(START_X,START_Y,MOVE_DOWN,BLOCK_UP)) { setcolor(WHITE); setfont(45,0,"隶体"); outtextxy(75,300,"GAME OVER"); Sleep(1000); system("pause"); exit(0); } } //判断当前方块是否可以转向到指定方向 //注意,此时还没有转到该方向!!! int rotatable(int x,int y,block_dir_t dir) { int xIndex=(y-minY)/20; int yIndex=(x-minX)/20; if(!moveable(x,y,MOVE_DOWN,dir)) { return 0; } int id=BlockIndex*4+dir; for(int i=0;i<5;i++) { for(int j=0;j<5;j++) { if(block[id][i][j]==1&& (yIndex+j<0||yIndex+j>=15||visit[xIndex+i][yIndex+j]==1)) { return 0; } } } return 1; } void wait(int interval) { int count=interval/10; for(int i=0;i<count;i++) { Sleep(10); if(_kbhit()) { return; } } } void mark(int x,int y,int blockIndex,block_dir_t dir) { int id=blockIndex*4+dir; int x2=(y-minY)/20; int y2=(x-minX)/20; for(int i=0;i<5;i++) { for(int j=0;j<5;j++) { if(block[id][i][j]==1) { visit[x2+i][y2+j]=1; markColor[x2+i][y2+j]=color[blockIndex]; } } } } void move() { int x=START_X; int y=START_Y; int k=0;//方块向下移动的偏移量 block_dir_t blockDir=BLOCK_UP; int curSpeed=speed; //当前速度 //检查游戏是否结束 failCheck(); //持续向下降落 while(1) { if(_kbhit()) { int key=getch(); if(key==KEY_SPACE) { getch(); } } //清除当前方块 clearBlock(x,k,blockDir); if(_kbhit()) { int key=getch(); if(key==KEY_UP) { block_dir_t nextDir=(block_dir_t)((blockDir+1)%4); if(rotatable(x,y+k,nextDir))//判断能否转向 { blockDir=nextDir; } }else if(key==KEY_DOWN) { curSpeed=50; }else if(key==KEY_LEFT) { if(moveable(x,y+k+20,MOVE_LEFT,blockDir))//同时下降,所以要+20 { x=x-20; } }else if(key==KEY_RIGHT) { if(moveable(x,y+k+20,MOVE_RIGHT,blockDir)) { x=x+20; } } } k=k+20; //绘制当前方块 drawBlock(x,y+k,BlockIndex,blockDir); wait(curSpeed); //方块的“固化”处理 if(!moveable(x,y+k,MOVE_DOWN,blockDir)) { mark(x,y+k,BlockIndex,blockDir); break; } } } void newblock() { //确定即将使用的方块的类别 BlockIndex=NextIndex; //绘制一个刚从顶部下降的方块 drawBlock(START_X,START_Y); //让新出现的方块暂停一会,让用户识别到 Sleep(100);//0.1秒 //在右上角区域,绘制一个下一个方块 nextblock(); //方块降落 move(); } void down(int x) { for(int i=x;i>0;i--)//这里i不能>=0,因为总是判断上一行,不能为-1 { for(int j=0;j<15;j++) { if(visit[i-1][j]) { visit[i][j]=1; markColor[i][j]=markColor[i-1][j]; setcolor(markColor[i][j]); outtextxy(20*j+minX,20*i+minY,"■"); }else { visit[i][j]=0; setcolor(BLACK); outtextxy(20*j+minX,20*i+minY,"■"); } } } //清除最顶上的那一行(就是行标为0的那一行),因为不能为-1,所有最顶行要单独判断 setcolor(BLACK); for(int j=0;j<15;j++) { visit[0][j]=0; outtextxy(20*j+minX,minY,"■"); } } void addScore(int lines) { char str[32]; setcolor(RED); score=score+lines*10; sprintf(str,"%d",score); outtextxy(415,310,str); } void updateGrade() { //更新等级的提示 //假设:50分一级 rank=score/50; char str[16]; sprintf(str,"%d",rank); outtextxy(425,405,str); //更新速度,等级越高,速度越快,speed越小! //最慢:500,最快:100 speed=500-rank*100; if(speed<=100) { speed=100; } } void check(void) { int i,j; int clearLines=0; for(i=29;i>=0;i--) { for(j=0;j<15&&visit[i][j];j++); //执行到此处时,有两种情况: //2.第i行已经满了,此时j>=15 //1.第i行没有满,即表示有空位 此时就j<15 if(j>=15) { //此时,第i行已经满了,就需要消除第i行 down(i);//消除第i行,并把上面的行都下移 i++;//因为最外层的循环中有i--,所以我们先i++,使得下次循环时,再把这一行检查以下 clearLines++; } } //更新分数 addScore(clearLines); //更新等级 updateGrade(); } int main(void) { //设置游戏窗口 welcome(); //初始化游戏界面 initGameScene(); //产生新方块 nextblock(); Sleep(500);//在刚开始的时候等一会儿,让用户体验感更好 //初始化访问数组 memset(visit,0,sizeof(visit)); while(1) { newblock(); check(); } system("pause"); closegraph(); return 0; }
需要安装esayX,vc2010或者2019