本文俄羅斯方塊實作主要思路:把方塊看做一個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;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/397489.html
標籤:其他
上一篇:Android:如何通過c++來呼叫java程式:接上篇
下一篇:Unity 裁剪
