c语言俄罗斯方块实验报告

时间:2024.4.30

一、需求分析

1.该程序是完成一个简易的俄罗斯方块的任务,其要完成几个重要的功能:界面,方块下落,旋转,判断是否还能下落,左右移动,分数,速度设置,清楚满的每行,下个方块的预览等;

2.可用#include <graphics.h>的头文件来实用几个函数来控制并完成游戏的界面;

3.可用7个二维5*5数组去实现下落方块的全部类型,再用随机函数使其随机下落;

4.用#include <conio.h>的头文件去使用按键的控制,以保证用户能够合理操作;并用#include <stdlib.h>的头文件来使用rand函数来随机出示七种方块;完成正常的显示和下个方块的预览;

5.用 #include <bios.h>来运用时针去控制时间;

6.程序执行过程:然后加上几个基本的头文件来执行函数,首先设置按键操作,用:w,a,d,s控制,然后进行界面的初始化设置,启动,时针参数设置,开始新游戏,开始随机下落方块,显示下一个方块,再行旋转,移动,清除,加分,速度参数,最后判断是否已满,停止游戏,打出分数,结束的画面设置,游戏终止...

二、概要设计

该程序中的函数设置总体主要功能大致如下

1.找到合适的方块盒子(7种)使其一一下落 box[MAX_C][5][5] = { /*MAX_C(7)种预定义的盒子*/ 这样用类似的7个5*5二维数组来实现每个盒子方块的形状,自此就引荐两个作为介绍, {

{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},

{1,1,1,1,0},

{0,0,0,0,0},

{0,0,0,0,0}

},如此中用1代表显示的格子,0代表空。

2.设置时间指针,实现旋转,移动,消去,显示加分,改变速度分别用下面函数表示

int setTimer(Timer *t, unsigned int intv, BOOL en);

/*设置时钟t,参数分别为时钟指针,时间间隔,是否活动*/

void rotateBox(int box1[5][5], int box2[5][5])

/*旋转box1输出到box2*/

int move(int dir) /*实现移动,返回成功与否*/

void clear() /*清除掉满行*/

void prscore() /*打印现在的分数*/

void spe()/*显示速度的改变*/

c语言俄罗斯方块实验报告

3.外部函数功能如下图

4.建立整个函数的流程图如下:

c语言俄罗斯方块实验报告

流程简介:玩家定义游戏开始,游戏显示开始界面,时针已经设定好,然后随机出示方块,玩家进行方块的旋转,左右移动,和下落控制,然后在旁边的表格中出示另一个表格显示下一个即将下落的方块,然后判断是否到达底部,若到,继续出示方块,若没,则可继续变换,下落到低端后在进行判断,是否可以消除该行,再进行分数的变换,速度的调整,然后判断是否已经满,若未满,则继续出示下一个方块,若满,则结束游戏,打印分数,出示结束界面,游戏终止。

三、详细设计

1.设置七种方块:这个用int box[MAX_C][5][5] = /*MAX_C(7)种预定义的盒子*/ 来实现;即七个5*5的盒子数组,方块实体用1表示,0表示空;七种如下所示,细心就会发现{

{0,0,0,0,0},

{0,0,0,0,0},

{1,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,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,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,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,0,1,0,0},

{0,0,1,0,0},

{0,0,0,0,0}

},

{

{0,0,0,0,0},

{0,0,1,1,0},

{0,0,1,0,0},

{0,0,1,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}

}

}; 这样七种盒子的方块就被清楚的展现在你们面前。用来保证方块不会出来

2.我们要进行按键的设置#define KEY_UP 'w' /*定义上下左右按按键*/

#define KEY_DOWN 's'

#define KEY_LEFT 'a'

#define KEY_RIGHT 'd'

#define KEY_ESC 27 /*退出*/

3.时钟的控制 ,用来控制方块的下落间隔时间及速度/*时钟结构控制*/

typedef struct { /*时钟结构*/

BOOL enabled; /*时钟是否开启*/

unsigned int intervel; /*定时间隔*/

unsigned int lasttime; /*这个属于内部使用变量*/

} Timer;

4.int GetTickCount() { /*读取BIOS时钟*/

int ret;

ret = peek(0x0,0x46e); /*实际上读取了内存0:046e处的内容*/

ret <<= 8; /*这个地方是$%#$^$%&^*/

ret += peek(0x0,0x46c); /*太多新的东西了,找点书看一看吧*/

return (ret);

}

int setTimer(Timer *t, unsigned int intv, BOOL en)

{

t -> enabled = en; /*设置一个时钟罗*/

t -> intervel = intv;

t -> lasttime = GetTickCount(); /*lasttime记录的是上一个*/

/*tickcount返回的东西*/

/*这样当再一次测试时间时新的tickcount产生了

它来减去上一次的tickcount就得出了一个时间 间隔,这个就可以和intervel比较从而得出是否

激活了

*/

return 0;

} 实现了以上的操作后,接下来要实现界面的设置一方便盒子在界面上的显示和操作;

5.void initMap(void) { /*初始化地图*/ /*

我们须要一圈卫兵呵呵,全是1...用来保证方块不会出来*/

int x, y;

for(y = 0; y < MAX_Y; y++) {

for(x = 0; x < MAX_X; x++) {

if(x < 2 || x > MAX_X - 3 || y > MAX_Y - 3)

map[y][x] = 1;

else map[y][x] = 0;

}

} /*这里初始化出这个形状*/

} /*当然是无盖的...*/

void render(void) /*这里唯一的绘图函数*/

int x, y;

static int cPage = 0; /*当前页,换页用*/

#define STARTX 50 /*定义几个常量*/

#define STARTY 0 /*数值根据自己的需要可以自己设置*/

#define LEN 18

setactivepage(cPage=(cPage == 0?1:0)); /*选择页*/

cleardevice(); /*清屏*/

setcolor(12);/*前景颜色*/

prscore(); setcolor(15);

rectangle( STARTX + LEN * 2 - 2,

STARTY + LEN * 3 - 2,

STARTX + LEN * (MAX_X - 2) + 2,

STARTY + LEN * (MAX_Y - 2) + 2);

/*用白色画一个外框*/

setfillstyle(SOLID_FILL, 5);

for(y = 3; y < MAX_Y - 2; y++) { /*画地图 */

for(x = 2; x < MAX_X - 2; x++) {

if(map[y][x]) {

rectangle( x * LEN + STARTX,

y * LEN + STARTY,

x * LEN + STARTX + LEN,

y * LEN + STARTY + LEN);

bar( x * LEN + STARTX + 1,

y * LEN + STARTY + 1,

x * LEN + STARTX + LEN - 2,

y * LEN + STARTY + LEN - 2);

}

}

}

/*绘图操作就不要作太复杂的介绍了,这只写作用*/

/*以上段,根据地图上的点阵情况将地图反映到屏幕上*/

for(y = 0; y < 5; y++) { /*画下落物*/

for(x = 0; x < 5; x++) {

if(curbox[y][x]) {

if(y + cury > 2) {

rectangle( (x + curx) * LEN + STARTX,

(y + cury) * LEN + STARTY,

(x + curx) * LEN + STARTX + LEN,

(y + cury) * LEN + STARTY + LEN);

bar( (x + curx) * LEN + STARTX +1,

(y + cury) * LEN + STARTY + 1,

(x + curx) * LEN + STARTX + LEN - 2,

(y + cury) * LEN + STARTY + LEN - 2);

}

}

}

}

/*以上将下落的盒子按昭它在地图上的坐标,画到对应的区域里*/

for(y = 0; y < 5; y++) { /*画下一个*/

for(x = 0; x < 5; x++) {

if(nextbox[y][x]) {

rectangle( x * LEN + 320,

y * LEN + 10,

x * LEN + 338,

y * LEN + 28);

bar( x * LEN + 321,

y * LEN + 11,

x * LEN + 336,

y * LEN + 26);

}

}

}

/*这个画出下一个盒子的预览*/

setvisualpage(cPage); /*确认在cPage页里画好了*/

/*将它显示出来*/

}

这样我们的初始化界面和盒子在图上的意义显示已经完成;

5.接下里我们要对即将出现的方块的形状进行预览即建立一个新的函数用来显示下一个下落的盒子;

void rebuidNext() { /*新建下一个形状并放到nextbox中*/

int i, x, y;

i = random(MAX_C); /*从几种方块里面选一种*/

for(y = 0; y < 5; y++) /*并复制过来*/

for(x = 0; x < 5; x++)

nextbox[y][x] = box[i][y][x]; /*复制*/

}

void putBox() { /*将curbox填充到地图上*/

int x, y;

for(y = 0; y < 5; y++) /*这个也简单,主要是要根*/

for(x = 0; x < 5; x++) /*据curx,cury指出位置 */

if(curbox[y][x])

map[y + cury][x + curx] = curbox[y][x];

}

int newfall() { /*创建下落元素失败返回0*/

int x, y;

curx = MAX_X / 2 - 2; /*重新指定小盒位置*/

cury = 0;

for(y = 0; y < 5; y++)

for(x = 0; x < 5; x++)

curbox[y][x] = nextbox[y][x];/*将nextBox复制过来*/

rebuidNext(); /*重建nextBox*/

return test(curx, cury, curbox);

}

这样一来,我们的预览盒子也就完成了。

4.完成以后,我们要进行下落的控制,这个在开始的控制指针已经做好的下落间隔设置,然后开始随机出示方块,玩家应开始进行旋转,移动的操作,下落的过程;

void rotateBox(int box1[5][5], int box2[5][5]) {

/*旋转box1输出到box2*/

int x, y;

for(x = 0; x < 5; x++) /*这个函数可以须要实际*/

for(y = 4; y >= 0; y--) /*编写一下才能印像深刻*/

box2[y][x] = box1[x][4 - y];

}

int rotate() /*整个旋转的操作并将盒子打印到屏幕上*/{

int x, y;

int newbox[5][5]; /*我们必须将当前盒子转动到新的盒子*/

/*再对这个新的盒子的冲突作测试*/

rotateBox(curbox, newbox); /*转动到新的盒子*/

if(test(curx, cury, newbox)) {

/*并且新的盒子能放到地图上而不冲突*/

for(y = 0; y < 5; y++)

for(x = 0; x < 5; x++)

curbox[y][x] = newbox[y][x]; /*复制进来*/

return 1;

}

else return 0;

}

int move(int dir) { /*返回成功与否*/

int newx;

if(dir) newx = curx + 1;

/*与drop一样,准备移动后的坐标*/

else newx = curx - 1;

if(test(newx, cury, curbox)) { /*测试是否冲突*/

curx = newx; /*可以的话切换curx*/

return 1;

}

return 0;

} 这个就完成了左右移动的操作;

int drop() { /*下落,返回成功与否*/

int newy; /*盒子要下落的新位置*/

newy = cury + 1; /*为当前Y位置+1*/

if(test(curx, newy, curbox)) {

cury = newy; /*测试下落盒在这个位置*/

return 1; /*上是否有冲突,没有的话*/

} /*直接设置cury*/

return 0;

}

int test(int mx, int my, int box[5][5]) {

/*测试box在map里mx,my位置上是否能着陆*/

/*这个是最关键的一个函数,它判断是否产生非空冲突*/

/*但算法还是很简单的*/

int x, y;

for(y = 0; y < 5; y++)

for(x = 0; x < 5; x++)

if(map[y + my][x + mx] && box[y][x])

return 0;

return 1;

}

这样就完成了旋转,移动,测试并下落的功能。

6.接下来我们要完成下落后的分数的改变速度的重新设定;

void clear() { /*清除掉满行*/ /*具体的算法为:

从第0行开始到最后一行,测试地图点阵是否为满,如果是的话

从当前行算起,之上的地图向下掉一行*/

int x, y;

int dx, dy;

int fullflag;

for(y = 0; y < MAX_Y - 2; y++) { /*最后两行保留行*/

fullflag = 1; /*假设为满*/

for(x = 2; x < MAX_X - 2; x++) { /*保留列~*/

if(!map[y][x]) {

fullflag = 0;

break;

}

}

if(fullflag) { /*向下移动一行*/

for(dy = y; dy > 0; dy--)

for(dx = 2; dx < MAX_X - 2; dx++)

map[dy][dx] = map[dy - 1][dx];

for(dx = 2; dx < MAX_X - 2; dx++)

map[0][dx] = 0; score+=10;/*输出新得分*/

/*并清除掉第一行*/

}

}

}

void prscore()/*输出分数*/{

char str[10];setfillstyle(SOLID_FILL,YELLOW);

rectangle(90,15,260,35);setcolor(6);

settextstyle(0,0,2);sprintf(str,"score:%d",score);

outtextxy(115,20,str);}

void spe()/*显示速度的改变*/{

if(score%50==0&&score!=0) setTimer(&tDown, speed--, 1);}

7.然后在主函数之前中定义以下的操作,这个应该会很容易理解

int map[MAX_Y+4][MAX_X+4]; /*地图\大盒子...MAX_X,Y是可见面积*/

/*我已说过需要在外面布两圈"卫兵"*/

int curbox[5][5]; /*当前下落的盒子*/

int curx, cury; /*保存着当前活动盒子在地图上的位置*/

int nextbox[5][5]; /*保存着下一个形状的盒子*/

8.接下俩用newgame函数将其串联起来,实现函数的内嵌;

void newGame() { /*新建游戏*/

int x, y;

initMap(); /*初始化地图*/

srand(GetTickCount()); /*初始化随机发生器*/

rebuidNext(); /*建立下一个*/

setTimer(&tDown, speed, 1); /*启动时钟(快慢两个)*/

setTimer(&tFast, FAST_INTV, 1);

newfall(); /*对下落的盒子操作一下*/

/*这样第一个下落的方块

就在地图顶部准备好了*/

}

9.接下来最为重要的主函数将要开始;

int main() {

char key; /*记录当前按键*/

int i;

int gd = VGA, gm = VGAMED; /*初始化的图形模式*/

Timer *ptDown; /*下落所指向的时钟(有快慢)*/

Timer trender; /*为了避免渲染给程序造成过大的负担*/

/*用一个时钟来控制渲染速度*/

/*把它设置interval = 1,*/

/*这样就是18 FPS了,当然无法达到标*/

/*准的60 FPS...毕竟这是DOS...*/

setTimer(&trender, 1, 1);

initgraph(&gd, &gm, ""); /*初始化图形*/

newGame(); /*新游戏...*/

prscore();

while(1) { /*主游戏循环*/

if(kbhit()) { /*如果键盘有按下*/

key = getch(); /*读取一个按键值到key*/

}

else key = 0;

switch(key) { /*对读到的key进行判断*/

case KEY_UP:

rotate(); /*上,旋转下落盒子*/

break;

case KEY_DOWN:

ptDown = &tFast; /*使用tFast时钟 */

break;

case KEY_LEFT:

move(0); /*左移*/

break;

case KEY_RIGHT:

move(1); /*右移*/

break;

case KEY_ESC:

closegraph(); /*结束游戏*/

exit(0);

default:

ptDown = &tDown; /*使用原来速度 */

}

if(testTimer(ptDown)) { /*在上面已设置了下落要

使用的时钟在ptDown里*/

if(!drop()) { /*下落,失败返回0*/

putBox(); /*写到地图里*/

clear();prscore();spe();setcolor(6) ;/*清除满行*/

if(!newfall()) { /*新建下落,失败则游戏结束*/

setcolor(14);

settextstyle(0,0,2);

outtextxy(100,200,"GAME OVER");/*游戏结束,在屏幕上打印字符串*/

outtextxy(100,240,"THANK YOU");

sleep(3);

exit(0);

}

}

}

if(testTimer(&trender)) /*最后...渲染...*/

render();

}

}

10.最后main函数已经介绍完毕,只需在开始加上程序所需的头文件和值的宏定义后整个程序就全部完毕

#include <stdio.h>

#include <stdlib.h>

#include <bios.h> /*这里须要读取系统运行时间来作为定时器*/

#include <graphics.h> /*很不幸,TC2的简单图形,让我放弃了用*/

#include <conio.h> /*win32+openGL来讲解.*/

#define MAX_X 14 /*可见最大X*/

#define MAX_Y 21 /*可见最大Y*/

/*我们定义了最大的可见X和Y,那么即还有不可见的部分,事实上地图(大盒子)里的左右两侧和底部各两行都被1填充,这样大大简化 出界的判断,事实上,在本例中没有这样的代码,因为旁边有一圈1阻止小盒子越出大盒子的按制范围

*/

#define MAX_C 7 /*最大种类,这个无须解释*/

#define FALSE 0

#define TRUE 1

到此为止,整个程序全部结束,详解到此完毕。

四、调试分析

1.开始第一次运行时方块不能显示出来,只能看到一个大白方框中几个空的白色的线,方块中间没东西,并且这个虚方块下落时不能被控制,这是源于在初始化界面时并没有将方块内部进行填充,再加用一个setfillstyle()函数即可填充内部的空动,方块不能被控制是因为为未判断是否有键按下在main函数中,若加上一个选择判断语句便可,

char key; /*记录当前按键*/ if(kbhit()) { /*如果键盘有按下*/

key = getch(); /*读取一个按键值到key*/ 这样便可以解决问题。

2.运行后发现方块无法旋转,并且反复块的出示有规律,并不是随机的,这个因为设定的函数有问题,重新修正后void rotateBox(int box1[5][5], int box2[5][5]) {

/*旋转box1输出到box2*/

int x, y;

for(x = 0; x < 5; x++) /*这个函数可以须要实际*/

for(y = 4; y >= 0; y--) /*编写一下才能印像深刻*/

box2[y][x] = box1[x][4 - y]; }如此一来,将整个边线都逆度针旋转90 度,便完成

要求, 然后在newgame函数和rubulid函数中中加上rand()随机数的设置,编能使整个方块随机出示。

3.分数只是会闪一下,但是并不能样一直显示,并且速度不会随这分数的增加进行改变,功能不完善,原因在于prscore函数应该在刚开始的绘图函数(即界面设置)中体现出来,然后才能将其体现到屏幕上,速度的设置在if(score%50==0&&score!=0) speed--;操作中是不能起作用的,不能单纯的进行speed--;应该将其放在时针控制中,所以应改为if(score%50==0&&score!=0) setTimer(&tDown, speed--, 1);}这样既可完成每增加50分进行速度的增加。

4.在终止程序时发生分数只能停留瞬间吗“game over”画面仅仅停留瞬间,这个只需在main函数结尾加上一个睡眠sleep()即可;

五、用户手册

由于这个程序很简单,故玩家操作也很简单,再次就做个简单介绍:游戏开始,

然后进入界面,方块自动下落,然后玩家用游戏键‘w’‘a’‘s’‘d’来控制左右移动和变换,w为方块的旋转,s为加速下落,a与d就是方块的左右移动。完成满行的消去,分数的增加,若玩家操作不当,到达顶部,则游戏结束。

更多相关推荐:
俄罗斯方块实验报告

《软件工程与开发实践1》软件设计报告题目:俄罗斯方块学院:计算机学院专业:计算机科学与技术一、软件设计概述(目的、任务、开发环境、参考资料)俄罗斯方块游戏属于经典小游戏,游戏规则简单,但又不乏趣味。而计算的一大…

JAVA 俄罗斯方块实验报告

目录一需求分析错误未定义书签二系统运行环境2三系统功能需求描述2四总体设计2五系统结构图3六程序模块设计3七实验总结体会15一需求分析在个人电脑日益普及的今天一些有趣的桌面游戏已经成为人们在使用计算机进行工作或...

俄罗斯方块实验报告

程序设计实践报告20xx20xx学年第2学期题目专学生姓班级学指导教指导单日俄罗斯方块游戏设计业名号师位软件工程系期20xx0327俄罗斯方块游戏设计一课题内容和要求本程序的主要任务就是编写简单的俄罗斯方块游戏...

俄罗斯方块设计实验报告

数字电路与逻辑设计实验报告基于VHDL的简易俄罗斯方块实验名称姓名班级简易俄罗斯方块电信工程学院04107班辅导老师日期高英20xx年11月6日摘要俄罗斯方块游戏是我们熟知的经典小游戏之一本实验通过硬件编成实现...

C++俄罗斯方块实验报告(附实验体会)

程序设计综合实验设计文档惠州学院HUIZHOUUNIVERSITY课程名称程序设计综合实验姓名实验名称俄罗斯方块学号任课教师专业班级计算机科学与技术1班实验时间第一周至第十二周实验成绩批阅教师签字第1页共18页...

游戏设计俄罗斯方块实验报告

江苏省惠山中等专业学校实验报告课程名称C程序设计实验名称游戏设计俄罗斯方块班级G1241姓名鲍顺亮日期指导教师卫洛斌成绩

俄罗斯方块实验报告

河南城建学院《JAVA基础》课程设计设计说明书课程名称:《JAVA基础》课程设计设计题目:俄罗斯方块指导教师:班级:学号:学生姓名:同组人员:计算机科学与工程学院20##年1月9日目录目录第1章选题...21.…

c语言俄罗斯方块游戏程序设计报告

C语言课程设计报告主标题:C语言课程设计副标题:俄罗斯方块游戏----界面设计姓名:指导教师:院系:信息工程学院专业:计算机科学与技术班级:11计本(二)班小组成员:提交日期:20##-6-7俄罗斯方块程序设计…

俄罗斯方块实验报告

俄罗斯方块实验报告编写俄罗斯方块游戏1、问题分析。编写俄罗斯方块游戏,首先是界面问题,要有一个相对美观的游戏界面,可以有很多种解决的方法,可以用DOS的界面,也可以用MFC做。界面做好后,最重要的就是七个方块如…

崔卿国VC++基于MFC的俄罗斯方块实验报告

课程设计报告学专业年级二一四年一月一日1目录1俄罗斯方块的游戏概述311游戏简介312游戏功能描述32需求分析与概要设计421游戏开发基本策略422俄罗斯方块的功能需求63开发工具介绍831VC的优点832WI...

C_设计报告_俄罗斯方块

目录一成员分工3二需求分析错误未定义书签21系统概述222系统运行环境223功能需求描述3三总体设计331屏幕的组成332形状的组成433形状的统一434移动与旋转的统一4四详细设计1041程序流程图11411...

俄罗斯方块设计报告

湖南农业大学课程设计报告主题专业年级姓名学号20xx4184231320xx4184232720xx41842312指导老师时间目录1课程设计目的11目的2需求分析21功能需求22数据需求23性能需求24运行需...

俄罗斯方块实验报告(44篇)