俄罗斯方块linux服务器,基与Linux环境下 C 俄罗斯方块

俄罗斯方块

一、设置控制台

1、更改屏幕的背景色,字体颜色

printf("\33[%dm", i);

30<= i <=37  设置字体颜色

30黑,31红,32绿,33黄,34蓝,35紫,36深绿,37白

40<= i <=47  设置背景颜色

40黑,41红,42绿,43黄,44蓝,45紫,46深绿,47白

2、任意指定屏幕的输出坐标

printf("\33[x;yH");  将输出坐标定位在第x行,第y列

printf("\33[%d;%dH", x,y);  将输出坐标定位在第x行,第y列

二、菜单

1、边框

游戏区宽48,高31;辅助区宽27,高31

游戏区起始坐标(2,3);辅助区起始坐标(2,53)(18,53)

2、开始

用空格和[]描出一个fight单词

3、结束

用空格和[]描出一个game over单词

三、绘制方块

1、一个小方格

由于一个字符在屏幕上占的空间是一个长方形,所以用[]来表示一个方格

2、俄罗斯方块

总共有7个类型的方块,每个方块有4种摆放的方式(可以旋转)

一个方块最多占用4个小方格

用4x4的矩阵来表示一个俄罗斯方块,矩阵中为1的地方,绘制[]

{0, 0, 0, 0}

{0, 0, 1, 0}    ----->            []

{0, 0, 1, 1}                     [][]

{0, 0, 1, 0}                     []

3、用一个三位数组表示所有的俄罗斯方块

int shape[i][j][k]

i表示方块的类型,j表示方块摆放的方式,k将4x4矩阵变为一维数组

将上面的二维数组变成一维的{0,0,0,0, 0,0,1,0, 0,0,1,0, 0,0,1,0}

4、起始坐标

绘制方块从4x4矩阵的左下角开始,制定其坐标为row、col

俄罗斯方块中小方格的坐标rrow = row + j/4-3   ccol = ccol + (i%4)*2

四、 自动下落

1、内核定时器

setitimer(int which, const struct itimerval *val, struct itimerval *oldval)

参数which:

ITIMER_REAL 用系统实时的时间计算,时间减1,减到0发送SIGALRM信号

ITIMER_VIRTUAL 当前进程用户态计数, 计数完成发送SIGVTALRM信号

ITIMER_PROF 用户态和内核态同时计数,计数完成发送SIGPROF信号

参数val:

struct itimerval{

struct timeval it_interval;  //定时时间

struct timeval it_value;     //定时器开始执行的时间

}

struct timeval{

long second;        //秒

long usec;            //微妙

}

参数oldval:

保存旧的状态

struct itimerval val = {

{0,500000},   //0.5秒的定时时间

{1,0}          //1s后开启定时器

};

setitimer{ITIMER_REAL, &val, NULL};

2、信号捕捉

sigaction(int signo, struct sigaction *act, struct sigaction *oldact);

参数signo:

要捕捉的信号

参数act:

为信号设置动作

参数oldact:

保存旧的状态,NULL就是不保存

struct sigaction{

void (*sa_handler)(int);    //信号执行的函数

sigset_t mask;                //信号执行过程中要屏蔽的其他信号

}

struct sigaction act;

act.sa_handler = self_down;

sigaction(SIGALRM, &act, NULL);

五、 响应键盘

1、设置终端,键盘可以输入,但是不能在屏幕上显示输入的字符,否则影响游戏画面。

struct termios old, new;

tcgetattr(0, &old);        //保存旧的状态

tcgetattr(0, &new);     //获取旧的状态

new.c_lflag = new.c_lflag & ~(ICANON|ECHO);  //设置屏幕不显示输入的字符

new.c_cc[VTIME] = 0;

new.c_cc[VMIN] = 1;

tcsetattr(0, TCSANOW, &new);  //修改终端的属性,立即生效

2、响应键盘输入

判断输入字符

while(1)

{

c = getchar();

switch(c)

{

case 'a':

left();    //左移,列-2,因为一个小方格占2列

break;

case 'd':

right();    //右移,列+2

break;x

case 'w':

change();  //逆时针旋转

break;

case 's':

self_down(1); //自由下落 row+1

break;

default:

break;

}

}

3、设置标志位

方块降落到最后,需要用以个二维数组来标志,数组中为1的地方证明有方格

int flag[30][24];

void set_flag()

{

int i;

int row, col;

for(i=0; i<16; i++)

{

if(shape[box.sh][box.num][i] == 1)

{

row = box.row + i/4 -3 -2;

col = (box.col + (i%4)*2 + 1)/2-2;

flag[row][col] = 1;

}

}

}

4、边界判断

1)、下落判断

//判断能有下落,当对应方格的下一行位置有东西,那么就不能下落来了

//比如现在方格在8行9列,那么如果9行9列有东西(判断标志位),那么

//方格就不能在下落了

int is_down()

{

int err = 1;

int i;

if(box.row >= 31)    //第一次超过31行就不能下落,那是最低的边界

{

err = 0;

return err;

}

for(i = 0; i < 16; i++)

{

if(shape[box.sh][box.num][i] == 1)

{

if(flag[box.row + i/4 -3 -2 + 1][(box.col+i%4*2+1)/2-2] == 1)

{

err = 0;

return err;

}

}

}

return err;

}

2)、左移判断

//首先要获取大方块中的最左边的列

int get_left()

{

int col[4];

int i, j, temp;

for(i=0, j=0; i<16; i++)

{

if(shape[box.sh][box.num][i] == 1)    //获取方块中小方格占的列

{

col[j] = box.col + (i%4)*2;

j++;

}

}

for(i=1; i<4; i++)

{

if(col[0] > col[i])        //求出列中最小的,也就是最左边的列

{

temp = col[0];

col[0] = col[i];

col[i] = temp;

}

}

return col[0];

}

int is_left()                //判断能否左移

{

int err = 1;

int i;

int col = get_left();

if(col == 3)            //最左边的列是第3列,移动到第3列就不能左移了

return 0;

//如果左边有方格,也不能左移,通过标志位来判断

for(i = 0; i < 16; i++)

{

if(shape[box.sh][box.num][i] == 1)

{

if(flag[box.row + i/4 -3 -2][(box.col+i%4*2+1)/2-2-1] == 1)

{

err = 0;

return err;

}

}

}

return err;

}

3)、右移判断

//首先要获取大方块中的最右边的列

int get_right()

{

int col[4];

int i, j, temp;

for(i=0, j=0; i<16; i++)

{

if(shape[box.sh][box.num][i] == 1)    //获取方块中小方格占的列

{

col[j] = box.col + (i%4)*2;

j++;

}

}

for(i=1; i<4; i++)

{

if(col[0] < col[i])        //求出列中最大的,也就是最右边的列

{

temp = col[0];

col[0] = col[i];

col[i] = temp;

}

}

return col[0];

}

int is_right()                //判断能否右移

{

int err = 1;

int i;

int col = get_right();

if(col == 3)            //最右边的列是第49列,移动到第49列就不能右移了

return 0;

//如果右边有方格,也不能右移,通过标志位来判断

for(i = 0; i < 16; i++)

{

if(shape[box.sh][box.num][i] == 1)

{

if(flag[box.row + i/4 -3 -2][(box.col+i%4*2+1)/2-2+1] == 1)

{

err = 0;

return err;

}

}

}

return err;

}

4)、旋转判断

旋转依赖4x4大方格,在4x4大方格内有东西到地方都不能旋转

int is_change()

{

int err=1;

int i;

for(i=0; i<16; i++)

{

if(flag[box.row+i/4-3-2+1][(box.col+1+i%4*2)/2-2] == 1)

{

err = 0;

return err;

}

if(box.col<3)

{

err = 0;

return err;

}

}

return err;

}

六、消行

1、判断是否需要消行,获取需要消的行号

在flag数组中,如果有一行全部为1,那么就需要消行了

int is_clean()

{

int err=0, fg=1;

int num=0;

int i,j;

for(i=0; i<30; i++)

{

fg = 1;                    //假设这一行能消去

for(j=0; j<24; j++)

{

if(flag[i][j]==0)    //如果这一行中有一个0,那么就不能消去

{

fg = 0;

break;

}

}

if(fg == 1)

{

clean_row[num] = i;    //如果能消行,那么需要记录行号

num++;

err = 1;

}

}

return err;

}

2、记录以前的状态,设值新的状态

void swap()

{

int i,j,k;                    //old_flag记录以前的状态

for(i=0; i<30; i++)

for(j=0; j<24; j++)

old_flag[i][j] = flag[i][j];

for(k=0; k<4; k++)            //更新flag,消行后,上面的行要掉下来

for(i=0; i

for(j=0; j<24; j++)

{

flag[i+1][j] = old_flag[i][j];

}

}

3、重新画图

void redraw()

{

int i,j;

//将以前的图形全部擦掉

for(i=0; i<30; i++)

{

for(j=0; j<24; j++)

{

if(old_flag[i][j]==1)

single_box(i+2, (j+1)*2+1, 0);

}

}

//绘制新的图形

for(i=0; i<30; i++)

{

for(j=0; j<24; j++)

{

if(flag[i][j]==1)

single_box(i+2, (j+1)*2+1, 1);

}

}

}

七、右侧指示下一个

1、一开始就产生两个图形,box、next_box

使用box下落,next_box显示在右侧

box.sh = 1;

box.num = 1;

box.row = 1;

box.col = 13;

big_box(box, 1);

next();

display_next( 1);

void next()

{

next_box.sh = rand_num(7);

next_box.num = rand_num(4);

next_box.row = 1;

next_box.col = 13;

}

void display_next(int mode)

{

struct box_info nbox;

memcpy(&nbox, &next_box, sizeof(struct box_info));

nbox.row = 10;

nbox.col = 60;

big_box(nbox, mode);

}

2、当下落结束的时候,需要使用next_box下落,然后接着产生另一个next_box

if(new_flag == 1)

{

new_flag = 0;

display_next(0);    //将右侧的提示去掉

memcpy(&box, &next_box, sizeof(struct box_info));//将next_box拷贝到box

next();                    //产生新的next_box

display_next( 1);        //在右侧提示next_box

}

八、判断游戏是否结束

//判断是否结束游戏

int is_gameover()

{

int err = 0;

int i,j;

for(j=0; j<24; j++)

{

//flag第0行有1就是结束游戏

if(flag[0][j] == 1)

{

err = 1;

break;

}

}

return err;

}

//产生下一个box的时候判断是否游戏结束

void next()

{

if(is_gameover()==1)

{

flag_over = 1;

return;

}

next_box.sh = rand_num(7);

next_box.num = rand_num(4);

next_box.row = 1;

next_box.col = 13;

}

九、修改菜单,完善游戏

增加游戏的灵活性,进入游戏后,按1是开始,按2是选关,按3是退出。

选关的时候+是增加难度,-是减少难度,1是开始,3是退出

最后未完成的双人版,日后补上,有源代码

压缩文件中有:els.c els.h main.c Makefile

运行方式:1  make

2  ./main

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值