俄罗斯方块(简易实现)
#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
posted on 2022-08-06 09:22 会飞的鱼-blog 阅读(10) 评论(0) 编辑 收藏 举报 来源
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~