参考了这个博客俄罗斯方块(C语言实现)_2021dragon的博客-CSDN博客_c语言俄罗斯方块游戏代码
//俄罗斯方块 C 语言
#include
#include
#include
#include
#pragma comment(lib,"Winmm.lib")
#include //getch函数
enum { ROW = 29, COL = 20 }; //row为行数,col是列数 游戏界面的大小
enum { DOWN = 80, LEFT = 75, RIGHT = 77, SPACE = 32, ESC = 27, ENTER = 13 };//定义方向键以及空格和退出键的 数值
int max = 0, grade = 0, flag = 0;
void userinterface();
void Hidecursor();
void gotoxy(int x, int y);
void color(int c);
void initinterface();
void initblock();
void drawblock(int shape, int form, int x, int y);
void deleteblock(int shape, int form, int x, int y);
void Grade();
int judgestop(int shape, int form, int x, int y);
int judgeout();
void initoutface();
void startgame();
void ReadGrade();
void WriteGrade();
void show(int x, int y);
void mode();
void ReadGrade()
{
FILE* pf = fopen("俄罗斯方块最高得分记录.txt", "r"); //以只读方式打开文件
if (pf == NULL) //打开文件失败
{
pf = fopen("俄罗斯方块最高得分记录.txt", "w"); //以只写方式打开文件(文件不存在可以自动创建该文件)
fwrite(&grade, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高历史得分初始化为0
}
fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
fread(&max, sizeof(int), 1, pf); //读取文件中的最高历史得分到max当中
fclose(pf); //关闭文件
pf = NULL; //文件指针及时置空
}
//更新最高分到文件
void WriteGrade()
{
FILE* pf = fopen("俄罗斯方块最高得分记录.txt", "w"); //以只写方式打开文件
if (pf == NULL) //打开文件失败
{
printf("保存最高得分记录失败\n");
exit(0);
}
fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中(更新最高历史得分)
fclose(pf); //关闭文件
pf = NULL; //文件指针及时置空
}
struct Face { //定义界面结构体,每一坐标的 有无方块,以及颜色
int data[ROW][COL + 10];
int color[ROW][COL + 10];
}face;
struct Block {
int space[4][4]; //每种状态都可以用4×4的方格表示出来
}block[7][4]; //存储七种类型的方块,以及每个方块的四种状态
char player[20];
int main() {
time_t start, end;
start = time(NULL);
PlaySound(TEXT("上海アリス幻樂団 - 神々が恋した幻想郷.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
userinterface();
system("mode con lines=29 cols=60"); //窗口大小
srand((unsigned int)time(NULL)); //时间种子
Hidecursor(); //隐藏光标
initinterface(); //初始化页面
initblock(); //初始化方块
startgame(); //开始游戏
system("pause>nul");
system("cls");
end = time(NULL);
gotoxy(COL / 2 + 5, ROW / 2 - 5);
printf("最终得分:%d", grade);
gotoxy(COL / 2 + 5, ROW / 2 - 3);
printf("最终耗时:time=%.0fs\n", difftime(end, start));
return 1;
}
void gotoxy(int x, int y) { //移动光标
COORD point;
point.X = x;
point.Y = y;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
SetConsoleCursorPosition(handle, point); //设置光标位置
}
void Hidecursor() { //隐藏光标
CONSOLE_CURSOR_INFO cursor = { 1,0 }; //0:设置光标不可见
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cursor); //设置光标信息
}
void mode()
{
color(8);
gotoxy(35, 9);
printf("模式1 >> 标准模式\n"); gotoxy(35, 10);
printf("模式2 >> 地狱模式\n"); gotoxy(35, 11);
printf("模式3 >> 作弊模式\n"); gotoxy(35, 12);
printf("模式4 >> 隐身模式\n"); gotoxy(35, 13);//隐身模式 是指经过一次消块后 把存在的方块变成黑色
printf("您的选择是");
scanf(" %d", &flag);
while (flag > 4 || flag <= 0) {
scanf("%d", &flag);
}
}
void userinterface() {
printf("\n\n\n"); printf(" ");
show(0, 0);
gotoxy(25, 7);
char str[100] = { "welcome to your favorite game --- tetris" };
color(7);
for (int i = 0; i < strlen(str); i++)
{
printf("%c", str[i]);
Sleep(50);
}
printf("\n");
color(1);
gotoxy(25, 8);
printf(" >> 请输入玩家名字:");
scanf("%s", &player);
mode();
gotoxy(25, 6);
printf(" OK %s Let's go!\n", player);
printf(" "); gotoxy(25, 7);
char str1[60] = { " >> Please enjoy adventure! " };
color(2);
for (int i = 0; i < strlen(str1); i++)
{
printf("%c", str1[i]);
Sleep(60);
}
color(7);
Sleep(500);
system("cls");
}
void show(int x, int y)
{
int flagl = 0;
while (1) {
system("cls");
color(rand() % 6);
gotoxy(x, y);
printf("■■■■■ ■■■■■ ■■■■■ ■■■■ ■■■ ■■■■");
gotoxy(x, y + 1);
printf(" ■ ■ ■ ■ ■ ■ ■");
gotoxy(x, y + 2);
printf(" ■ ■■■■ ■ ■■■ ■ ■■■");
gotoxy(x, y + 3);
printf(" ■ ■ ■ ■ ■ ■ ■");
gotoxy(x, y + 4);
printf(" ■ ■■■■■ ■ ■ ■ ■■■ ■■■■");
Sleep(100); x = x + 1; flagl++;
if (flagl == 20)
{
break;
}
}
}
void initinterface() { //初始化界面
for (int j = 28; j >= 0; j--) {
for (int i = 0; i < 30; i++) {
color(8);
gotoxy(i * 2, j);
printf("■");
}Sleep(50);
}Sleep(200);
system("cls");
color(7);
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL + 10; j++) {
if (j == 0) face.data[i][j] = 1;
else if (i == ROW - 1) face.data[i][j] = 1;
else if (j == COL - 1 || j == COL + 9) face.data[i][j] = 1;
else face.data[i][j] = 0;
}
}
for (int i = COL; i < COL + 10; i++) {
face.data[8][i] = 1;
}
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL + 10; j++) {
if (face.data[i][j] == 1) {
gotoxy(j * 2, i);
printf("■");
}
}
}
gotoxy(COL * 2 + 1, ROW / 2 + 1);
printf(" '↓' :下移一格");
gotoxy(COL * 2 + 1, ROW / 2 + 3);
printf(" '→' :右移一格 ");
gotoxy(COL * 2 + 1, ROW / 2 + 5);
printf(" '←' :左移一格 ");
gotoxy(COL * 2 + 1, ROW / 2 + 7);
printf("'空格键':翻转方块");
gotoxy(COL * 2 + 1, ROW / 2 - 3);
printf("Player: %s", player);
gotoxy(COL * 2 + 1, ROW / 2 - 1);
printf("当前得分:%d", grade);
gotoxy(COL * 2 + 1, ROW / 2 + 11);
printf("R:重新开始");
gotoxy(COL * 2 + 1, ROW / 2 + 13);
printf("S:停止游戏");
gotoxy(COL * 2 + 1, ROW / 2 - 5);
printf("MAX:%d", max);
if (flag == 3)
{
gotoxy(COL * 2 + 1, ROW / 2 + 9);
printf("ENTER:变换方块");
}
}
void color(int c) {
switch (c) {
case 0:c = 10; break; //七种图形各个的颜色
case 1:c = 11; break;
case 2:c = 12; break;
case 3:c = 14; break;
case 4:c = 8; break;
case 5:
case 6:c = 13; break;
case 99:c = 0; break;
default:c = 7; break; //其他设置为白色
}
HANDLE consolehend;
consolehend = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(consolehend, c);
}
void initblock() {
// T形
for (int i = 1; i < 4; i++) {
block[0][0].space[1][i] = 1;
}
block[0][0].space[0][2] = 1;
// L形
for (int i = 0; i < 3; i++) {
block[1][0].space[i][1] = 1;
}
block[1][0].space[2][2] = 1;
// 镜像L形
for (int i = 0; i < 3; i++) {
block[2][0].space[i][2] = 1;
}
block[2][0].space[2][1] = 1;
// I 形
for (int i = 0; i < 4; i++) {
block[3][0].space[i][2] = 1;
}
// Z形
for (int i = 1; i < 3; i++) {
block[4][0].space[0][i] = 1;
}
for (int i = 2; i < 4; i++) {
block[4][0].space[1][i] = 1;
}
// 镜像Z形
for (int i = 1; i < 3; i++) {
block[5][0].space[1][i] = 1;
}
for (int i = 2; i < 4; i++) {
block[5][0].space[0][i] = 1;
}
// 田形
for (int i = 0; i < 2; i++) {
for (int j = 1; j < 3; j++) {
block[6][0].space[i][j] = 1;
}
}
// 顺时针进行翻转
for (int t = 0; t < 7; t++) { //七种图形
for (int i = 1; i < 4; i++) { //四种翻转
for (int j = 0; j < 4; j++) { //行
for (int k = 0; k < 4; k++) { //列
block[t][i].space[k][3 - j] = block[t][i - 1].space[j][k];
}
}
}
}
}
void drawblock(int shape, int form, int x, int y) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (block[shape][form].space[i][j] == 1) {
gotoxy((j + x) * 2, y + i);
//一个小方格占据两个横坐标,一个纵坐标
printf("■");
}
}
}
}
void deleteblock(int shape, int form, int x, int y) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (block[shape][form].space[i][j] == 1) {
gotoxy((j + x) * 2, y + i);
printf(" "); //需要打两个空格覆盖
}
}
}
}
int judgestop(int shape, int form, int x, int y) { //判断方块是否落到方块上 或者底部
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (block[shape][form].space[i][j] == 1)
{
if (face.data[y + i][x + j] == 1)
{
return 0;
}
}
}
}
return 1;
}
void Grade() {
for (int i = ROW - 2; i > 0; i--) { //从ROW-2开始,最底层是边界 最顶部检测到初始方块下落的位置
int num = 0; //记录每一行方块的个数
for (int j = 1; j <= COL - 2; j++) //从1开始,最左边是边界
{
num += face.data[i][j];
}
if (num == 0) break; //没有方块结束循环
if (num == COL - 2) { //判断是否消除
grade += 10;
color(7);
gotoxy(COL * 2 + 1, ROW / 2 - 1);
printf("当前得分:%d", grade);
for (int j = 1; j < COL - 1; j++) {
face.data[i][j] = 0;
gotoxy(j * 2, i);
printf(" ");//打印两个空格覆盖
}
for (int t = i; t > 1; t--) { //下移
int sum = 0;
for (int k = 1; k < COL - 1; k++) {
face.data[t][k] = face.data[t - 1][k];
face.color[t][k] = face.color[t - 1][k];
sum += face.data[t - 1][k];
if (face.data[t][k] == 1) {
gotoxy(k * 2, t);
color(face.color[t][k]);
printf("■");
}
else {
gotoxy(k * 2, t);
printf(" ");
}
}
if (sum == 0) {
i++;
break;
}
}
}
}
}
int judgeout() { //判断游戏结束
for (int i = 1; i < COL - 1; i++) { //i=1为顶层
if (face.data[1][i] == 1) {
initoutface();
return 0;
}
}return 1;
}
void initoutface() {
for (int j = 28; j >= 0; j--) {
for (int i = 0; i < 30; i++) {
color(2);
gotoxy(i * 2, j);
printf("■");
}Sleep(50);
}
for (int j = 28; j >= 0; j--)
{
for (int i = 0; i < 60; i++) {
color(2);
gotoxy(i * 2, j);
printf("■");
}Sleep(50);
}
for (int i = 0; i < 5; i++) {
gotoxy(3 * 2, i + 10);
printf(" "); Sleep(50);
}
for (int i = 0; i < 4; i++) {
gotoxy((3 + i) * 2, 10);
printf(" "); Sleep(50);
}
for (int i = 0; i < 3; i++) {
gotoxy((3 + i) * 2, 12);
printf(" "); Sleep(50);
}
for (int i = 0; i < 4; i++) {
gotoxy((3 + i) * 2, 14);
printf(" "); Sleep(50);
}
for (int i = 0; i < 5; i++) {
gotoxy(9 * 2, i + 10);
printf(" "); Sleep(50);
}
for (int i = 0; i < 4; i++) {
gotoxy((9 + i) * 2, i + 10);
printf(" "); Sleep(50);
}
for (int i = 0; i < 5; i++) {
gotoxy(13 * 2, 14 - i);
printf(" "); Sleep(50);
}
for (int i = 0; i < 5; i++) {
gotoxy(15 * 2, 10 + i);
printf(" "); Sleep(50);
}
for (int i = 0; i < 2; i++) {
gotoxy((16 + i) * 2, 10);
printf(" "); Sleep(50);
}
for (int i = 0; i < 3; i++) {
gotoxy(18 * 2, 11 + i);
printf(" "); Sleep(50);
}
for (int i = 0; i < 2; i++) {
gotoxy((17 - i) * 2, 14);
printf(" "); Sleep(50);
}
Sleep(500);
}
void startgame()
{
int shape = rand() % 7; //随机取方块形状 状态
int form = rand() % 4;
while (1)
{
int i = 0;
int x = COL / 2; //每次循环(x,y)初始化为顶部中间位置 初始下落位置
int y = 0;
int nextshape = rand() % 7; //获取下一个方块
int nextform = rand() % 4;
int t; //下落间隙时间设置在while循环外 ,保证每次t都是减少到0 才重新计时,保证正常的下落速度
if (flag == 2) t = 5000;
else t = 15000;
color(nextshape);
drawblock(nextshape, nextform, COL + 3, 2);
while (1) {
color(shape);
drawblock(shape, form, x, y); //每次循环后刷新方块位置
if (judgestop(shape, form, x, y) == 0) {
initoutface();
return;
}
if (t == 0)
{
if (flag == 2) t = 5000;
else t = 15000;
}
while (--t) {
if (_kbhit() != 0)break; //如果按键 ,则直接跳出循环执行 方向 操作,t剩余值
}
if (t == 0) { //t=0 方块需要往下移动一行
if (judgestop(shape, form, x, y + 1) == 0) { //如果下一行位置
for (int t = 0; t < 4; t++) {
for (int k = 0; k < 4; k++) {
if (block[shape][form].space[t][k] == 1) {
face.data[t + y][k + x] = 1;
if (flag != 4)
face.color[t + y][k + x] = shape;
else if (flag == 4)//隐身模式
{
face.color[t + y][k + x] = 99; color(99);
}
}
}
}
Grade(); //判断得分
if (judgeout() == 0) //判断结束游戏
return;
break; //跳出while循环,刷新下一个方块
}
else { //如果合法删除原来位置方块
deleteblock(shape, form, x, y);
y++;
}
}
else if (t > 0) { //由于程序执行很快,在敲击条件下,如果不合法可以不给赋值data=1,它会直接进下一循环不敲击中判断出来。
char c = _getch(); //获取 敲击键操作
switch (c) {
case DOWN:if (judgestop(shape, form, x, y + 1) == 1) { //如果不合法
deleteblock(shape, form, x, y);
y++;
}
break;
case LEFT:
if (judgestop(shape, form, x - 1, y) == 1) {
deleteblock(shape, form, x, y);
x--;
}
break;
case RIGHT:if (judgestop(shape, form, x + 1, y) == 1)
{
deleteblock(shape, form, x, y);
x++;
}
break;
case SPACE:if (judgestop(shape, (form + 1) % 4, x, y + 1) == 1) {
deleteblock(shape, form, x, y);
y++;
form = (form + 1) % 4; //翻转
}break;
case ENTER:if (flag == 3 && judgestop(shape, (form + 1) % 4, x, y + 1) == 1) {
deleteblock(shape, form, x, y);
y++;
shape = (shape + 1) % 7; //翻转
}break;
case 's':
case 'S':
system("pause>nul"); break;
case 'r':
case 'R': system("cls"); main(); //重新游戏
}
}
}
deleteblock(nextshape, nextform, COL + 3, 2); shape = nextshape; form = nextform;
}
return;
}