Linux下c语言实现有色界面俄罗斯方块
Posted 派大星有个梦想
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux下c语言实现有色界面俄罗斯方块相关的知识,希望对你有一定的参考价值。
本文俄罗斯方块实现主要思路:把方块看做一个4x4的小数组在规定界面(看做一个大数组)内移动,用简单的信号控制并发,做到方块边下落边响应键盘的操作。
本代码运用ANSI控制码来实现方块和界面的上色,提供从文件读取并更新历史最高分,暂停游戏,根据等级加快方块下落速度等功能,具体效果如下图所示:
本文为博主第一次发表文章,不懂的地方可以留言,都会回复,喜欢的可以点赞收藏,代码不足的地方欢迎大家提出建议和改进方法 ,大家共同进步,以下为代码实现:
函数声明:
#ifndef GAME_H__
#define GAME_H__
#define ROW 19
#define LINE 14
#define DIA 4
#define BASE 7
#define SPINSTA 4
//方块函数,base为基本类型,spinsta为旋转状态
struct Blocks
int space[DIA][DIA];
blocks[BASE][SPINSTA];
void Game_start(); //游戏开始函数
void Interface(); //游戏界面
int Init_blocks(); //方块初始化
void Next_blocks(); //下一个方块的打印
int Read_record(); //读取历史记录
void Grade_print(int); //分数打印
void Select_colour(int); //选择颜色
void Ols_load(); //棋盘读入数据
int Set_time(); //设置下落速度
void Game_pause(); //游戏暂停
int Run(); //游戏运行
int Judge_over(); //判断游戏结束
int Judge_move(int); //判断方块是否能移动
int Judge_chage(); //判断方块能否旋转
void Judge_dis(); //判断能否消行
void Exec_command(int); //执行命令
void Dis_last(int); //销毁上一位置方块
void Exec_dis(int); //执行消行
#endif
主要代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <time.h>
#include <sys/time.h>
#include "game.h"
#include "kbhit.h" //Linux需要自己实现,windows下自带
#define BUFFSIZE 1024
//历史最高记录读取文件名
#define FILENAME "/home/ll/Gitee/c-code/项目/俄罗斯方块/log"
int Els[ROW][LINE] = 0; //棋盘数组
int row_ = 0; //方块在棋盘中的行
int line_ = 0; //方块在棋盘中的列
int colour_[ROW][LINE] = 0; //每个方块对应的颜色
int colour = 0; //当前方块的颜色
int colour_next = 0; //下一个方块的颜色
int base = 0; //当前方块类型
int base_next = 0; //下一个方块类型
int spinsta = 0; //方块的4种变化
int grade = 0; //成绩
int level = 1; //等级
int stop_ = 1; //当前方块能否继续下落
static void trave_() //根据棋盘数值打印相对应的颜色
int i, j;
int colour_cur = 0;
printf("%c[6;12H",'\\033');
for (i = 2; i < ROW-2; i++)
for (j = 2; j < LINE-2; j++)
if (Els[i][j] == 1) //表示正在下落的方块
Select_colour(colour);
printf("[]\\033[0m");
else if(Els[i][j] == 2) //已经下落完成的方块
Select_colour(colour_[i][j]);
printf("[]\\033[0m");
else
printf(" ");
printf("\\n\\033[11C");
void Interface() //利用ANSI控制码打印界面
int i, j, k;
printf("\\033[2J\\033[5;10H\\033[45m--====================-------------------\\033[0m\\n");
printf("%c[6;10H",'\\033');
for (i = 2; i < ROW-2; i++)
printf("\\033[45m||\\033[0m");
for (j = 2; j < LINE-2; j++)
printf(" ");
printf("\\033[45m||\\t\\t||\\033[0m\\n\\033[9C");
printf("\\033[21;10H\\033[45m--====================-------------------\\033[0m\\n");
printf("\\033[12;34H\\033[45m---------------\\033[0m\\n");
printf("\\033[16;34H\\033[45m---------------\\033[0m\\n");
printf("\\033[13;36H\\033[32mHigh Record\\033[0m");
printf("\\033[14;40H\\033[31m%d\\033[0m",Read_record(0));
printf("\\033[18;38H\\033[32mScore\\033[0m");
printf("\\033[19;40H\\033[31m%d\\033[0m",grade);
//初始化棋盘边界
for (i = 0,k = ROW-2; i < 2 && k < ROW; i++,k++)
for (j = 0; j < LINE; j++)
Els[i][j] = 2;
Els[k][j] = 2;
for (i = 2; i < ROW-2; i++)
for (j = 0,k = LINE-2; j < 2 && k < LINE; j++,k++)
Els[i][j] = 2;
Els[i][k] = 2;
int Init_blocks() //初始化方块,共28种
int i, j;
int temp[DIA][DIA] = 0;
int base_, spinsta_;
for (i = 0; i < 2; i++)
//田
blocks[0][0].space[1][i+1] = 1;
blocks[0][0].space[2][i+1] = 1;
//Z
blocks[1][0].space[1][i] = 1;
blocks[1][0].space[2][i+1] = 1;
//反Z
blocks[2][0].space[1][i+1] = 1;
blocks[2][0].space[2][i] = 1;
for (i = 1;i < 4; i++)
//7
blocks[3][0].space[1][0] = 1;
blocks[3][0].space[i][1] = 1;
//反7
blocks[4][0].space[1][2] = 1;
blocks[4][0].space[i][1] = 1;
for (i = 0; i < 4; i++)
//|
blocks[5][0].space[i][1] = 1;
for (i = 0; i < 3; i++)
//土
blocks[6][0].space[1][1] = 1;
blocks[6][0].space[2][i] = 1;
//根据7种基本形态衍生的其他方块
for (base_ = 0; base_ < 7; base_++)
for (spinsta_ = 0; spinsta_ < 3; spinsta_++)
for (i = 0; i < DIA; i++)
for (j = 0;j < DIA; j++)
temp[i][j] = blocks[base_][spinsta_].space[i][j];
for (i = 0; i < DIA; i++)
for (j = 0; j < DIA; j++)
blocks[base_][spinsta_+1].space[i][j] = temp[4-1-j][i];
return 0;
void Next_blocks() //下一个方块的打印
int i, j;
printf("\\033[7;38H");
for (i = 0;i < DIA; i++)
for (j = 0; j < DIA; j++)
if (blocks[base_next][0].space[i][j] == 1)
Select_colour(colour_next);
printf("[]\\033[0m");
continue;
printf(" ");
printf("\\n\\033[37C");
int Read_record() //从文件中读取最高记录
FILE *fps = NULL;
FILE *fpd = NULL;
char buff[BUFFSIZE] = 0;
int n = 0;
fps = fopen(FILENAME,"r+");
if (fps == NULL)
perror("fopen()");
exit(1);
fgets(buff,BUFFSIZE,fps);
n = atoi(buff);
if (grade > n)
fpd = fopen(FILENAME,"w+");
if (fpd == NULL)
perror("fopen()");
exit(1);
fprintf(fpd,"%d",grade);
fclose(fpd);
fclose(fps);
return n;
void Grade_print(int grade) //打印成绩
printf("\\033[18;38H\\033[32mScore\\033[0m");
printf("\\033[19;40H\\033[31m%d\\033[0m",grade);
if (grade > 0 && grade % 100 == 0) //每一100分等级加一
level++;
printf("\\033[14;40H\\033[31m%d\\033[0m",Read_record());
void Select_colour(int select) //颜色选择函数
switch(select)
case 0:
printf("\\033[46m");
break;
case 1:
printf("\\033[44m");
break;
case 2:
printf("\\033[41m");
break;
case 3:
printf("\\033[42m");
break;
case 4:
printf("\\033[43m");
break;
void Ols_load() //方块赋值给棋盘
int i, j;
for (i = 0; i < DIA; i++)
for (j = 0;j < DIA; j++)
if (Els[i+row_][line_+j] != 2) //防止覆盖已经下落完成的方块
Els[i+row_][line_+j] = blocks[base][spinsta].space[i][j];
trave_();
void Game_start() //关闭终端回显和\\n结束getchar()的功能能
struct termios new,old;
tcgetattr(0,&old);
tcgetattr(0,&new);
new.c_lflag = new.c_lflag & ~(ICANON | ECHO);
new.c_cc[VMIN] = 1;
new.c_cc[VTIME] = 0;
tcsetattr(0,TCSANOW,&new);
Interface();
Init_blocks();
printf("\\033[?25l"); //隐藏光标
srand((unsigned)time(NULL));
base = rand() % 7;
colour = rand() % 5;
while(Run());
tcsetattr(0,TCSANOW,&old); //还原终端原始设置
printf("\\033[?25h\\033[2J");
static void alrm_handler(int s) //信号控制函数,内为下落函数
if (Judge_move(3) == 1)
Exec_command(3);
else
Judge_dis();
stop_ = 0;
int Set_time() //根据等级设置下落速度并用信号控制并发
char ch;
signal(SIGALRM,alrm_handler);
if (level == 1)
struct itimerval new = 0,700000,0,700000;
setitimer(ITIMER_REAL,&new,NULL);
if (level == 2)
struct itimerval new = 0,600000,0,600000;
setitimer(ITIMER_REAL,&new,NULL);
if (level >= 3)
struct itimerval new = 0,400000,0,400000;
setitimer(ITIMER_REAL,&new,NULL);
while(stop_)
if (kbhit() == 1)
ch = getchar();
switch(ch)
//退出
case 'q':
case 'Q':
return 0;
case 'p':
Game_pause();
break;
case 't': //变形
if (Judge_chage() == 1)
Exec_command(0);
break;
case 'a': //左移
if (Judge_move(1) == 1)
Exec_command(1);
break;
case 'd': //右移
if (Judge_move(2) == 1)
Exec_command(2);
break;
case 's': //加速下落
if (Judge_move(3) == 1)
Exec_command(3);
else
Judge_dis();
return 1;
return 1;
void Game_pause() //游戏暂停
char ch;
struct itimerval pause_ = 0, old;
setitimer(ITIMER_REAL,&pause_,&old);
printf("\\033[2J\\033[10;20H\\033[31mGame is pause...\\033[0m");
fflush(stdout);
while(1)
ch = getchar();
if (ch == 'p')
break;
printf("\\033[2J\\033[10;20H\\033[32mGame continue\\033[0m");
fflush(stdout);
sleep(1);
Interface();
Next_blocks();
setitimer(ITIMER_REAL,&old,NULL);
int Run() //运行函数
//初始化方块下落位置
int ret = 0;
spinsta = 0;
row_ = 2;
line_ = 6;
stop_ = 1;
//给下一个方块确定类型和颜色
srand((unsigned)time(NULL));
base_next = rand() % 7;
colour_next = rand() % 5;
Next_blocks();
//判断游戏是否结束
if (Judge_over() == 0)
return 0;
Ols_load();
//根据等级匹配下落速度
ret = Set_time();
base = base_next;
colour = colour_next;
return ret;
//游戏结束判断
int Judge_over()
int i, j;
//最上一格存在方块
for (j = 2; j < LINE-2; j++)
if (Els[2][j] == 2)
return 0;
//下一个方块不能进入棋盘
for (i = 0; i < DIA; i++)
for (j = 0; j < DIA; j++)
if (blocks[base][spinsta].space[i][j] == 1)
if (Els[row_+i][line_+j] == 2)
return 0;
return 1;
//判断方块能否移动
int Judge_move(int move)
int i, j;
int f_row,f_line;
switch(move)
case 1: //left
f_row = 0;
f_line = -1;
break;
case 2: //right
f_row = 0;
f_line = 1;
break;
case 3: //down
f_row = 1;
f_line = 0;
break;
for (i = 0; i < DIA; i++)
for (j = 0; j < DIA; j++)
if (Els[row_+i][line_+j] == 1)
if (Els[row_+i+f_row][line_+j+f_line] == 2)
return 0;
return 1;
//判断方块是否能旋转
int Judge_chage()
int i, j;
int count = 0;
if (spinsta+1 > 3)
spinsta = -1;
for (i = 0; i < DIA; i++)
for (j = 0; j < DIA; j++)
if (blocks[base][spinsta+1].space[i][j] == 1)
if (Els[row_+i][line_+j] != 2)
count++;
if (count == 4)
return 1;
return 0;
void Judge_dis()
int i, j;
int count = 0;
int temp = 0;
int flag = 0;
int n = 0;
int score = 0;
//下落完成的方块记录颜色并赋值为2
for (i = 2; i < ROW-2; i++)
for (j = 2; j < LINE-2; j++)
if (Els[i][j] == 1)
Els[i][j] = 2;
colour_[i][j] = colour;
//判断是否能消行
for (i = ROW-3; i >= 2; i--)
count = 0;
flag = 0;
for (j = 2; j < LINE-2; j++)
if (Els[i][j] == 2)
count++;
if (count == 10)
temp = i;
Exec_dis(temp);
n++;
grade += 10;
i = temp+1; //i从原来位置重新判断
flag = 1;
if (flag)
score += n*10;
if (n >= 2)
grade += score;
Grade_print(grade);
trave_();
//执行命令
void Exec_command(int command)
switch(command)
case 0: //旋转
spinsta++;
Dis_last(command);
break;
case 1: //左移
line_--;
Dis_last(command);
break;
case 2: //右移
line_++;
Dis_last(command);
break;
case 3: //下落
row_++;
Dis_last(command);
//执行消行
void Exec_dis(int row)
int i, j;
for (j = 2;j < LINE-2; j++)
Els[row][j] = 0;
for (i = row; i > 2; i--)
for (j = 2; j < LINE-2; j++)
Els[i][j] = 0;
Els[i][j] = Els[i-1][j];
void Dis_last(int flag) //消除上个状态
int i, j;
int last_row, last_line;
//变形
if (flag == 0)
last_row = 0;
last_line = 0;
//左移
else if(flag == 1)
last_row = 0;
last_line = 1;
//右移
else if(flag == 2)
last_row = 0;
last_line = -1;
//下移
else
last_row = -1;
last_line = 0;
for (i = 0; i < DIA; i++)
for (j = 0; j < DIA; j++)
if (Els[i+row_+last_row][line_+j+last_line] != 2)
Els[i+row_+last_row][line_+j+last_line] = 0;
Ols_load();
由于linux下没有kbhit函数(判断键盘是否被敲击)的实现,以下为函数实现:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
int kbhit(void)
struct termios oldt, newt;
int ch;
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF)
ungetc(ch, stdin);
return 1;
return 0;
以上是关于Linux下c语言实现有色界面俄罗斯方块的主要内容,如果未能解决你的问题,请参考以下文章