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语言实现有色界面俄罗斯方块的主要内容,如果未能解决你的问题,请参考以下文章

俄罗斯方块(C语言实现)

用c语言编写俄罗斯方块程序 求详解

俄罗斯方块游戏:C语言程序设计初步感受

搭建VC2010 开发环境,创建《C语言实现俄罗斯方块游戏》教程

C语言:俄罗斯方块

第一个C语言项目开发------俄罗斯方块的设计与实现