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;
}

 

Guess you like

Origin blog.csdn.net/pdx_ll/article/details/122215601