实现俄罗斯方块
功能列表:
1 出边框
2 能在顶部随机出一个图形
3 图形共 (7种), 每种由4个方块组成
4 图形自动向下落
5 左移,右移,下移加速
6 旋转
7 当方块铺满1行,消行(上面下落)
8 当方块落到最下面时,出一个新的,原来的保留不动
9 加分、加等级、加速
10 提示下一个图形
11 暂停
12 触顶结束
13 颜色(变换)
14 计时
15 存档(以后实现)
实现方法:
显示带颜色的 俄罗斯方块
○■
属性列表如下:
通用格式控制:
0 重置所有属性 (恢复默认值)
1 高亮/加粗
2 暗淡
4 下划线
5 闪烁
7 反转
8 隐藏
前景色:
30 黑色
31 红色
32 绿色
33 黄色
34 蓝色
35 品红
36 青色
37 白色
如何指定光标位置打印
void gotoxy(int x, int y)
{
printf(“\033[%d;%dH”, y + 1, x + 1);
/// \033 打印属性设置
}如何打印出颜色
printf(“\033[1;33m★\033[0m”);
\033 打印属性设置
[1; 打印亮度 (1 高亮 2 暗淡)
#include <stdio.h>
void gotoxy(int x, int y)
{
printf(“\033[%d;%dH”, y + 1, x + 1);
}
int main()
{
system(“clear”); //调用linux的 clear 命令
gotoxy(10, 15); //光标跳转到 x = 10, y = 15处
printf(“\033[1;31m○○○○\033[0m”);
gotoxy(30, 15);
printf(“\033[1;33m■■■■\033[0m”);
}
俄罗斯方块map(地图)
21 行 12列
int block[21][12] = { 0 };
1----> 边框○
2----> 方块■
0----> 空格
#include <stdio.h>
void gotoxy(int x, int y)
{
printf(“\033[%d;%dH”, y + 1, x + 1);
}
int block[21][12] = { 0 }; //1----> 边框 //2----> 方块 //0----> 空格
void init_block() //初始化地图
{
int i;
for(i = 0; i < 21; i++)
{
block[i][0] = 1;
block[i][11] = 1;
}
for(i = 0; i < 12; i++)
{
block[0][i] = 1;
block[20][i] = 1;
}
}
void display_all() //显示地图
{
int i, j;
gotoxy(0, 0);
for(i = 0; i < 21; i++)
{
for(j = 0; j < 12; j++)
{
if(block[i][j] == 1)
printf(“\033[1;31m○ \033[0m”);
else if(block[i][j] == 2)
printf(“■ “);
else if(block[i][j] == 0)
printf(” “);
}
printf(”\n”);
}
}
int main()
{
system(“clear”);
init_block();
display_all();
}
英文字母: 8 * 16(横向8个点,纵向16个点),高是宽的2倍
AB
例1: “hello world” 自动往下走(每隔1秒钟,向下走一个坐标点)
x , y-----> gotoxy 指定坐标点 ----> 隔1秒,y++, 重新打印
#include <unistd.h>
sleep(1); //延时1秒
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void gotoxy(int x, int y)
{
printf(“\033[%d;%dH”, y + 1, x + 1);
}
int main()
{
int x = 0, y = 0;
while(1)
{
system(“clear”); //清屏, system linux系统库函数,它可以调用linux命令
gotoxy(x, y);
printf(“\033[1;31m○○○○○○\033[0m”);
fflush(stdout); //刷新屏幕
y++;
sleep(1); //延时1秒
}
}
如何实现非阻塞I/O
程序运行时不断打印hello world, 同时扫描键盘
要实现 hello world 每隔1秒往下走,同时扫描键盘,如果按下了 a, 左, 按下了d 右
例2: 用getchar实现接收键盘数据, 按下a, hello world左移, 按下 d, hello world右移 ,
同时 hello world自动 往下走
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void gotoxy(int x, int y)
{
printf(“\033[%d;%dH”, y + 1, x + 1);
}
int main()
{
int x = 5, y = 0, c;
while(1)
{
system(“clear”); //清屏, system linux系统库函数,它可以调用linux命令
gotoxy(x, y);
printf(“\033[1;31m○○○○○○\033[0m”);
fflush(stdout); //刷新屏幕
y++;
c = getchar(); //scanf(“%c”, &c); 他们都是阻塞I/O, 程序执行到这就停下来,等待输入
//getchar 不行,我们应该用非阻塞I/O, 即使没输入任何按键,程序仍然能往下走
if(c == ‘a’)
x–;
else if(c == ‘d’)
x++;
sleep(1); //延时1秒,时间有点长
}
}
阻塞I/O讲解 (程序运行到这个函数,就停下来)
getchar scanf
改成非阻塞I/O
#include <stdio.h>
#include <stdlib.h>
#define TTY_PATH “/dev/tty”
#define STTY_US "stty raw -echo -F "
#define STTY_DEF "stty -raw echo -F "
static int get_char() //能实现非阻塞IO, 如果没有按下按键,程序继续往下走
{
fd_set rfds;
struct timeval tv;
int ch = 0;
FD_ZERO(&rfds);FD_SET(0, &rfds);tv.tv_sec = 0;tv.tv_usec = 10; //设置等待超时时间//检测键盘是否有输入if (select(1, &rfds, NULL, NULL, &tv) > 0){ch = getchar(); }return ch;//返回按键值
}
void gotoxy(int x, int y)
{
printf(“\033[%d;%dH”, y + 1, x + 1);
}
int main()
{
int x = 0, y = 0;
char ch;
system(STTY_US TTY_PATH); //不用理解
while(1)
{
system(“clear”);
gotoxy(x, y);
printf(“\033[1;31m○○○○○○\033[0m”);
fflush(stdout); //刷新屏幕
ch = get_char(); //换成get_char 非阻塞IO
if(ch == ‘d’)
x++;
else if(ch == 3) //ctrl + c
{
system(STTY_DEF TTY_PATH); //恢复非阻塞IO设置 //不用理解
return 0; //程序结束
}
y++;
//sleep(1); //延时1秒, 因为需要调速,此函数不行
usleep(300000); //以微妙为单位延时, 300ms (1s = 1000 ms 1ms = 1000us)
}
}
整合方案
#include <stdio.h>
void gotoxy(int x, int y)
{
printf(“\033[%d;%dH”, y + 1, x + 1);
}
int block[21][12] = { 0 }; //1----> 边框 //2----> 方块 //0----> 空格
void init_block() //初始化地图
{
int i;
for(i = 0; i < 21; i++)
{
block[i][0] = 1;
block[i][11] = 1;
}
for(i = 0; i < 12; i++)
{
block[0][i] = 1;
block[20][i] = 1;
}
}
void display_all() //显示地图
{
int i, j;
gotoxy(0, 0);
for(i = 0; i < 21; i++)
{
for(j = 0; j < 12; j++)
{
if(block[i][j] == 1)
printf(“\033[1;31m○ \033[0m”);
else if(block[i][j] == 2)
printf(“■ “);
else if(block[i][j] == 0)
printf(” “);
}
printf(”\n”);
}
}
int current_line = 1; //当前行
void new_point() //出一个新的方块 point 点
{
block[1][4] = 2;
block[1][5] = 2;
block[1][6] = 2;
block[1][7] = 2;
}
void point_move_down() //方块下落 ( y++ )
{
block[current_line][4] = 0;
block[current_line][5] = 0;
block[current_line][6] = 0;
block[current_line][7] = 0;
current_line++;
block[current_line][4] = 2;
block[current_line][5] = 2;
block[current_line][6] = 2;
block[current_line][7] = 2;
}
int main()
{
system(“clear”);
init_block();
new_point();
while(1)
{
display_all();
usleep(300000);
point_move_down();
}
}
方案改进
现在方案是通过填充二维数组实现的,但是在变形时,消除原来的图形是一个难点(工作量大)
新方案:
图形分成两部分
静态图形(边框, 当方块落到下面,就变成静态的)
用二维数组动态图形(新出的,向下落的,可以变换的)
用结构体数组
struct point_pos
{
int x;
int y;
};
struct point_pos point[4];
//更改之后的方案/
#include <stdio.h>
#include <stdlib.h>
#define TTY_PATH “/dev/tty”
#define STTY_US "stty raw -echo -F "
#define STTY_DEF "stty -raw echo -F "
static int get_char() //能实现非阻塞IO, 如果没有按下按键,程序继续往下走
{
fd_set rfds;
struct timeval tv;
int ch = 0;
FD_ZERO(&rfds);FD_SET(0, &rfds);tv.tv_sec = 0;//设置非阻塞I/O 等待时长(秒值)tv.tv_usec = 10; //设置等待超时时间 (微秒值)//检测键盘是否有输入if (select(1, &rfds, NULL, NULL, &tv) > 0){ch = getchar(); }return ch;//返回按键值
}
void gotoxy(int x, int y)
{
printf(“\033[%d;%dH”, y + 1, x + 1);
}
struct point_pos
{
int x;
int y;
};
struct point_pos point[4];
int speed = 10; //速度 值越大,越慢
int score = 0; //分数,每消1行 + 100分
int level = 1;
int block[21][12] = { 0 }; //1----> 边框 //2----> 方块 //0----> 空格
void init_block() //初始化地图
{
int i;
for(i = 0; i < 21; i++)
{
block[i][0] = 1;
block[i][11] = 1;
}
for(i = 0; i < 12; i++)
{
block[20][i] = 1;
}
}
void display_all() //显示地图
{
int i, j;
gotoxy(0, 0);
for(i = 0; i < 21; i++)
{
for(j = 0; j < 12; j++)
{
if(block[i][j] == 1)
printf(“\033[1;31m○ \033[0m”);
else if(block[i][j] == 2)
printf(“■ “);
else if(block[i][j] == 0)
printf(” “);
}
printf(”\r\n”);
}
}
int current_point = 0;
int current_status = 0; //对于横线来说 0(横) 1(竖)
void new_point() //出一个新的方块 point 点
{
current_point = rand() % 2; //current = 0 ####
if(current_point == 0) // = 1 ##
{ // ##
current_status = 0;
point[0].x = 4;
point[0].y = 0;
point[1].x = 5;
point[1].y = 0;
point[2].x = 6;
point[2].y = 0;
point[3].x = 7;
point[3].y = 0;
}
else if(current_point == 1)
{
point[0].x = 5;
point[0].y = 0;
point[1].x = 6;
point[1].y = 0;
point[2].x = 5;
point[2].y = 1;
point[3].x = 6;
point[3].y = 1;
}
}
void draw_point()
{
int i;
for(i = 0; i < 4; i++)
{
gotoxy(point[i].x * 2, point[i].y);
printf("■ ");
}
}
//将动态图形添加到静态二维数组中
void add_point_to_block()
{
int i;
for(i = 0; i < 4; i++)
{
block[point[i].y][point[i].x] = 2;
}
}
void clear_one_line(int line) //line 将要消除的行
{ //从被消除的行开始,每行往下移
int i, j;
for(i = line; i > 0; i–)
{
for(j = 0; j < 12; j++)
{
block[i][j] = block[i - 1][j];
}
}
}
void judge_clear_line() //判断是否消行
{ //从 下往上扫描,如果某一行满了(某行二维数组全是2 (10个)),消除
int i, j;
for(i = 19; i > 0; i–) //? i >= 0 or i > 0
{
int n = 0; //每次重新计数
for(j = 1; j < 11; j++)
{
if(block[i][j] == 2)
{
n++;
}
}
if(n == 10)
{
score += 100;
if(score % 500 == 0)
{
level++;
speed–;
if(speed < 1)
speed = 1;
}
clear_one_line(i); //消除一行
i++;
}
}
}
void point_move_down() //方块下落 ( y++ )
{
int i;
for(i = 0; i < 4; i++) //限制: 当y+1 的那个点如果正好碰到静态图形,就不下落
{
if(block[point[i].y + 1][point[i].x] != 0) //不好理解
{
add_point_to_block();
judge_clear_line(); //判断是否需要消行
new_point();
return; //结束整个函数
}
}
for(i = 0; i < 4; i++)
{
point[i].y++;
}
}
void point_move_left() //方块左移 ( x-- )
{
int i;
for(i = 0; i < 4; i++) //限制: 当y+1 的那个点如果正好碰到静态图形,就不下落
{
if(block[point[i].y][point[i].x - 1] != 0) //不好理解
return; //结束整个函数
}
for(i = 0; i < 4; i++)
{
point[i].x–;
}
}
void point_move_right() //方块右移 ( x++ )
{
int i;
for(i = 0; i < 4; i++) //限制: 当y+1 的那个点如果正好碰到静态图形,就不下落
{
if(block[point[i].y][point[i].x + 1] != 0) //不好理解
return; //结束整个函数
}
for(i = 0; i < 4; i++)
{
point[i].x++;
}
}
void change_point()
{
if(current_point == 0)
{
if(current_status == 0)
{
current_status = 1;
point[0].x++;
point[0].y–;
point[2].x–;
point[2].y++;
point[3].x -= 2;
point[3].y += 2;
}
else
{
current_status = 0;
point[0].x–;
point[0].y++;
point[2].x++;
point[2].y–;
point[3].x += 2;
point[3].y -= 2;
}
}
}
int game_over() //第一行如果有方块,游戏结束
{
int i;
for(i = 1; i < 11; i++)
{
if(block[0][i] == 2)
{
return 1;
}
}
return 0;
}
int main()
{
int i;
char ch;
srand(time(0));
system(STTY_US TTY_PATH); //不用理解
system(“clear”);
init_block();
new_point();
while(1)
{
display_all();
draw_point();
gotoxy(30, 0);
printf(“score:%d”, score);
gotoxy(30, 1);
printf(“level:%d”, level);
fflush(stdout); //刷新屏幕
for(i = 0; i < speed; i++) //speed = 10
{
ch = get_char(); //换成get_char 非阻塞IO
if(ch == ‘a’) //左移
{
point_move_left(); //左移
break;
}
else if(ch == ‘d’)
{
point_move_right(); //右移
break;
}
else if(ch == ‘s’)
{
break;
}
else if(ch == ‘w’)
{
change_point();
break; //如果不加break 会丢掉一个中间状态
}
else if(ch == 3) //ctrl + c
{
system(STTY_DEF TTY_PATH); //恢复非阻塞IO设置 //不用理解
return 0; //程序结束
}
usleep(50000); //以微妙为单位延时, 300ms (1s = 1000 ms 1ms = 1000us)
}
if(ch != 'a' && ch != 'd' && ch != 'w')point_move_down();if(game_over() == 1){gotoxy(0, 10);printf("-------game over-------\n");system(STTY_DEF TTY_PATH);//恢复非阻塞IO设置 //不用理解return 0;//程序结束}}
}