首页 / 操作系统 / Linux / C Windows控制台字符版本俄罗斯方块
C Windows控制台字符版本俄罗斯方块//一个可以工作在Windows控制台字符界面下的俄罗斯方块//工作在非图形模式,无需其他库依赖,单个C文件代码即可运行//支持最高纪录,并且对于纪录进行了加密//By wrule 2015年12月14日20:53:57//控制方式 WSAD 键对应旋转,下,左,右//需要注意的是在进行游戏之前需要按下 Ctrl + 空格 取消输入法,否则无法正确操作#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <conio.h>#include <time.h>#include <windows.h>#define SQUARE_BLANK 0#define SQUARE_TETRIS 1#define SQUARE_WALL 2//俄罗斯方块图案类型,不同旋转情况下的可能个体typedef struct pattern pattern;struct pattern {const int w;const int h;const int data[4][4];};//俄罗斯方块类型,可以完整描述一个俄罗斯方块的所有旋转typedef struct tetris tetris;struct tetris {const int len;const pattern * pattern_list;};//用于定位的俄罗斯方块类型,使用两个 id 作为索引typedef struct itetris itetris;struct itetris {int id1;int id2;};//可以完整描述俄罗斯方块当前状态以及操作的类型,为最终类型typedef struct ntetris ntetris;struct ntetris {int x1;int y1;itetris its1;int x2;int y2;itetris its2;};//以下为俄罗斯方块数据的前向声明const tetris tetris_list[];const size_t TETRIS_LIST_LEN;//历史最高分玩家姓名char hs_name[9] = "暂无";//历史最高分玩家得分int hs_score = 0;//当前得分int now_score = 0;//地图int map[20][10];//游戏初始节奏unsigned int rhythm = 1000;//Windows 控制台显示坐标定位void gotoxy(int x, int y) {COORD coord = { x, y };SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);}//错误抛出并且结束程序运行void error() {gotoxy(0, 0);printf("error");_getch();exit(EXIT_FAILURE);}//绘制基本方块void draw_square(int x, int y, int v) {gotoxy(x * 2, y);switch (v) {case SQUARE_BLANK:{ printf(" ");}break;case SQUARE_TETRIS:{ printf("□");}break;case SQUARE_WALL:{ printf("■");}break;default:{error();}break;}}//绘制界面框架void draw_frame() {int x, y;for (y = 0; y < 22; ++y) {for (x = 0; x < 12; ++x) {if (y == 0 ||y == 21 ||x == 0 ||x == 11) {draw_square(x, y, SQUARE_WALL);}}}for (y = 0; y < 6; ++y) {for (x = 13; x < 19; ++x) {if (y == 0 ||y == 5 ||x == 13 ||x == 18) {draw_square(x, y, SQUARE_WALL);}}}gotoxy(28, 8);printf("最高纪录");gotoxy(28, 14);printf("当前得分");gotoxy(28, 21);printf("by wrule");}//绘制俄罗斯方块void draw_tetris(const itetris * pits, int x, int y, int v) {int px;int py;int ox;int oy;const pattern * ptrn = &(tetris_list[pits->id1].pattern_list[pits->id2]);int w = ptrn->w;int h = ptrn->h;for (py = 0; py < h; ++py) {for (px = 0; px < w; ++px) {if (ptrn->data[py][px]) {ox = x + px;oy = y + py;if ((ox > -1 &&ox < 10 &&oy > -1 &&oy < 20)||(ox > 12 &&ox < 17 &&oy > -1 &&oy < 4)) {draw_square(ox + 1, oy + 1, v);}}}}}//在界面上绘制出下一个即将出现的俄罗斯方块void draw_next_tetris(const itetris * pits) {const pattern * ptrn = &(tetris_list[pits->id1].pattern_list[pits->id2]);int w = ptrn->w;int h = ptrn->h;int bx;int by;int px = (4 - w) / 2;int py = (4 - h) / 2;if (h % 2) {++py;}for (by = 1; by < 5; ++by) {for (bx = 14; bx < 18; ++bx) {draw_square(bx, by, SQUARE_BLANK);}}draw_tetris(pits, 13 + px, py, SQUARE_TETRIS);}//随机取得一个俄罗斯方块void get_rnd_tetris(itetris * pits) {pits->id1 = rand() % TETRIS_LIST_LEN;pits->id2 = rand() % tetris_list[pits->id1].len;}//应用针对于俄罗斯方块的操作void use_op(ntetris * pnts) {draw_tetris(&(pnts->its1),pnts->x1,pnts->y1,SQUARE_BLANK);pnts->x1 = pnts->x2;pnts->y1 = pnts->y2;pnts->its1 = pnts->its2;draw_tetris(&(pnts->its1),pnts->x1,pnts->y1,SQUARE_TETRIS);}//俄罗斯方块自然下落void general_fall(ntetris * pnts) {pnts->x2 = pnts->x1;pnts->y2 = pnts->y1 + 1;pnts->its2 = pnts->its1;}//用户手动左移俄罗斯方块void hand_left(ntetris * pnts) {pnts->x2 = pnts->x1 - 1;pnts->y2 = pnts->y1;pnts->its2 = pnts->its1;}//用户手动右移俄罗斯方块void hand_right(ntetris * pnts) {pnts->x2 = pnts->x1 + 1;pnts->y2 = pnts->y1;pnts->its2 = pnts->its1;}#define TURN_LEFT -1#define TURN_RIGHT 1//用户手动旋转俄罗斯方块void hand_turn(ntetris * pnts, int dir) {int px, py;const pattern * ptrn1;int w1, h1;const pattern * ptrn2;int w2, h2;int len = tetris_list[pnts->its1.id1].len;pnts->its2 = pnts->its1;pnts->its2.id2 += dir;if (pnts->its2.id2 >= len) {pnts->its2.id2 = 0;}else if (pnts->its2.id2 <= -1) {pnts->its2.id2 = len - 1;}ptrn1 = &(tetris_list[pnts->its1.id1].pattern_list[pnts->its1.id2]);ptrn2 = &(tetris_list[pnts->its2.id1].pattern_list[pnts->its2.id2]);w1 = ptrn1->w;h1 = ptrn1->h;w2 = ptrn2->w;h2 = ptrn2->h;px = (w1 - w2) / 2;py = (h1 - h2) / 2;pnts->x2 = pnts->x1 + px;pnts->y2 = pnts->y1 + py;}//向地图之中放置一个俄罗斯方块void put_tetris(ntetris * pnts, itetris * pits) {const pattern * ptrn = &(tetris_list[pits->id1].pattern_list[pits->id2]);int w = ptrn->w;int h = ptrn->h;pnts->x2 = (10 - w) / 2;pnts->y2 = -h;pnts->its2 = *pits;pnts->x1 = pnts->x2;pnts->y1 = pnts->y2;pnts->its1 = pnts->its2;use_op(pnts);}//初始化地图数据void init_map() {int x, y;for (y = 0; y < 20; ++y) {for (x = 0; x < 10; ++x) {map[y][x] = 0;}}}//判断针对于俄罗斯方块的操作是否非法int op_isilegal(ntetris * pnts) {int x = pnts->x2;int y = pnts->y2;const pattern * ptrn = &(tetris_list[pnts->its2.id1].pattern_list[pnts->its2.id2]);int w = ptrn->w;int h = ptrn->h;int px, py;int ox, oy;int ret = 0;for (py = 0; py < h; ++py) {for (px = 0; px < w; ++px) {if (ptrn->data[py][px]) {ox = x + px;oy = y + py;if (ox < 0 ||ox > 9 ||oy > 19) {ret = 1;goto pret;}else {if (oy > -1) {if (map[oy][ox]) {ret = 1;goto pret;}}}}}}pret:return ret;}//俄罗斯方块被放置之后写入地图数据void write_tetris(ntetris * pnts) {int x = pnts->x1;int y = pnts->y1;const pattern * ptrn = &(tetris_list[pnts->its1.id1].pattern_list[pnts->its1.id2]);int w = ptrn->w;int h = ptrn->h;int px, py;int ox, oy;for (py = 0; py < h; ++py) {for (px = 0; px < w; ++px) {if (ptrn->data[py][px]) {ox = x + px;oy = y + py;map[oy][ox] = 1;}}}}//绘制地图void draw_map() {int x, y;for (y = 0; y < 20; ++y) {for (x = 0; x < 10; ++x) {draw_square(x + 1,y + 1,map[y][x] ? SQUARE_TETRIS : SQUARE_BLANK);}}}//试图消行int try_clear() {int score = 0;int i;int x, y;int flag;int clear = 0;int list[20];int tmap[20][10];//记录应当被消除的列表for (y = 0; y < 20; ++y) {flag = 1;for (x = 0; x < 10; ++x) {if (0 == map[y][x]) {flag = 0;break;}}list[y] = flag;}//记录消除标志和消除得分for (i = 0; i < 20; ++i) {if (1 == list[i]) {score++;clear = 1;}}if (clear) {//显示消除动画for (i = 0; i < 3; ++i) {for (x = 0; x < 10; ++x) {for (y = 0; y < 20; ++y) {if (list[y]) {draw_square(x + 1,y + 1,(i % 2) ? SQUARE_TETRIS : SQUARE_BLANK);}}}Sleep(250);}//初始化备份地图for (y = 0; y < 20; ++y) {for (x = 0; x < 10; ++x) {tmap[y][x] = 0;}}//在备份地图上执行消除i = 19;for (y = 19; y > -1; --y) {if (0 == list[y]) {for (x = 0; x < 10; ++x) {tmap[i][x] = map[y][x];}--i;}}//把消除之后的数据回写入地图for (y = 0; y < 20; ++y) {for (x = 0; x < 10; ++x) {map[y][x] = tmap[y][x];}}//在界面上重绘地图draw_map();}rhythm -= score;if (rhythm < 50) {rhythm = 50;}return score;}//更新当前玩家分数void update_now_score() {gotoxy(30, 16);printf(" ");gotoxy(30, 16);printf("%d", now_score);}//更新历史玩家数据void update_hs_data() {int i;gotoxy(28, 10);printf(" ");gotoxy(28, 10);for (i = 0; i < 8 && hs_name[i]; ++i) {putchar(hs_name[i]);}gotoxy(30, 12);printf(" ");gotoxy(30, 12);printf("%d", hs_score);}#define NEXT_STEP 0#define WRITE_WIN 1#define GAME_OVER 2//俄罗斯方块自然下落之后的处理过程int after_fall(ntetris * pnts) {int pscore;//如果试图进行的操作不违法if (op_isilegal(pnts) == 0) {use_op(pnts);return NEXT_STEP;}else {//如果垒满,游戏结束if (pnts->y1 < 0) {return GAME_OVER;}else {//放置好俄罗斯方块,并且向地图写入数据write_tetris(pnts);//试图消行并且获得得分pscore = try_clear();//如果得分非零,则更新当前玩家得分if (pscore) {now_score += pscore;update_now_score();}return WRITE_WIN;}}}//BKDR Hash 函数unsigned int bkdr_hash(const char * str) {unsigned int seed = 131;unsigned int hash = 0;while (*str) {hash = hash * seed + (*str++);}return hash % 65536;}//读取历史记录数据 Hash 校验void read_hs_data() {size_t i;size_t len;unsigned char buf[100];unsigned int hash_num;char str[50];char * pstr = NULL;FILE * fp = fopen("tetris.txt", "rb");if (NULL != fp) {len = fread(buf, 1, 100, fp);for (i = 0; i < len; ++i) {buf[i] ^= 0xC6;}buf[i] = " ";hash_num = buf[0] + buf[1] * 256;if (bkdr_hash(&buf[2]) == hash_num) {sscanf(&buf[2], "%s", str);sscanf(str, "%d", &hs_score);pstr = &buf[strlen(str) + 3];for (i = 0; i < 8 && pstr[i]; ++i) {hs_name[i] = pstr[i];}hs_name[i] = " ";}else {strcpy(hs_name, "暂无");hs_score = 0;}fclose(fp);}else {strcpy(hs_name, "暂无");hs_score = 0;}}//写入历史记录数据 Hash 加密void write_hs_data(char name[], int score) {size_t len;size_t i;unsigned char buf[100];FILE * fp = fopen("tetris.txt", "wb");unsigned int hash_num;if (NULL != fp) {sprintf(&buf[2], "%d
", score);len = strlen(&buf[2]);for (i = 0; i < 8 && name[i]; ++i) {(&buf[2])[len + i] = name[i];}(&buf[2])[len + i] = " ";len = strlen(&buf[2]);hash_num = bkdr_hash(&buf[2]);buf[0] = hash_num % 256;buf[1] = hash_num / 256;for (i = 0; i < len + 2; ++i) {fputc(buf[i] ^ 0xC6, fp);}fclose(fp);}else {error();}}//游戏结束相关处理void game_over() {char name[100];system("cls");gotoxy(9, 2);printf("你的得分为: %d", now_score);if (now_score > hs_score) {gotoxy(9, 4);printf("恭喜你刷新了记录");gotoxy(9, 6);printf("请输入你的大名: ");scanf("%s", name);write_hs_data(name, now_score);gotoxy(9, 8);printf("记录成功刷新");}else {gotoxy(9, 4);printf("很遗憾你没有刷新记录");}_getch();}//游戏主程序int game_main() {itetris next_its;ntetris nts;unsigned int otime;unsigned int ntime;int c;int pause = 0;int aflag;//清空控制台屏幕system("cls");now_score = 0;//初始化地图数据init_map();//获得第一个随机俄罗斯方块get_rnd_tetris(&next_its);//绘制界面框架draw_frame();//读取历史记录read_hs_data();//更新历史玩家数据update_hs_data();//更新当前玩家得分update_now_score();gotoxy(28, 19);printf("P键暂停");while (1) {bk2://放置下一个俄罗斯方块进入当前场景put_tetris(&nts, &next_its);//获得下一个随机俄罗斯方块get_rnd_tetris(&next_its);//绘制下一个俄罗斯方块draw_next_tetris(&next_its);//Tick 定时器开始工作otime = GetTickCount();while (1) {if (0 == pause) {//Tick 定时器节奏触发俄罗斯方块自然下落if ((ntime = GetTickCount()) - otime > rhythm) {otime = ntime;general_fall(&nts);aflag = after_fall(&nts);if (WRITE_WIN == aflag) {break;}else if (GAME_OVER == aflag) {game_over();return 0;}}}//按键检测if (_kbhit()) {c = _getch();if (0 == pause) {switch (c) {//旋转俄罗斯方块case "w":{ hand_turn(&nts, TURN_RIGHT); //如果合法则应用操作,下同 if (op_isilegal(&nts) == 0) { use_op(&nts); }}break;//手动下落case "s":{ general_fall(&nts); aflag = after_fall(&nts); if (WRITE_WIN == aflag) { goto bk2; } else if (GAME_OVER == aflag) { game_over(); return 0; }}break;//左移俄罗斯方块case "a":{ hand_left(&nts); if (op_isilegal(&nts) == 0) { use_op(&nts); }}break;//右移俄罗斯方块case "d":{ hand_right(&nts); if (op_isilegal(&nts) == 0) { use_op(&nts); }}break;}}//游戏暂停功能if ("p" == c) {if (pause) {pause = 0;gotoxy(28, 19);printf("P键暂停");}else {pause = 1;gotoxy(28, 19);printf("P键继续");}}}}}return 0;}//主函数int main() {//清空控制台屏幕system("cls");//在控制台标题上显示游戏标题system("title tetris");//设定控制台窗口的宽高system("mode con cols=38 lines=22");//产生随机数种子srand((unsigned int)time(NULL));while (1) {//进入游戏主程序game_main();}return 0;}//以下 const pattern 类型为七种俄罗斯方块各自旋转图案const pattern pattern_list_line[] = {{1, 4,{1, 0, 0, 0,1, 0, 0, 0,1, 0, 0, 0,1, 0, 0, 0}},{4, 1,{1, 1, 1, 1,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0}}};const pattern pattern_list_l[] = {{2, 3,{1, 0, 0, 0,1, 0, 0, 0,1, 1, 0, 0,0, 0, 0, 0}},{3, 2,{1, 1, 1, 0,1, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0}},{2, 3,{1, 1, 0, 0,0, 1, 0, 0,0, 1, 0, 0,0, 0, 0, 0}},{3, 2,{0, 0, 1, 0,1, 1, 1, 0,0, 0, 0, 0,0, 0, 0, 0}}};const pattern pattern_list_rl[] = {{2, 3,{0, 1, 0, 0,0, 1, 0, 0,1, 1, 0, 0,0, 0, 0, 0}},{3, 2,{1, 0, 0, 0,1, 1, 1, 0,0, 0, 0, 0,0, 0, 0, 0}},{2, 3,{1, 1, 0, 0,1, 0, 0, 0,1, 0, 0, 0,0, 0, 0, 0}},{3, 2,{1, 1, 1, 0,0, 0, 1, 0,0, 0, 0, 0,0, 0, 0, 0}}};const pattern pattern_list_s[] = {{3, 2,{0, 1, 1, 0,1, 1, 0, 0,0, 0, 0, 0,0, 0, 0, 0}},{2, 3,{1, 0, 0, 0,1, 1, 0, 0,0, 1, 0, 0,0, 0, 0, 0}}};const pattern pattern_list_z[] = {{3, 2,{1, 1, 0, 0,0, 1, 1, 0,0, 0, 0, 0,0, 0, 0, 0}},{2, 3,{0, 1, 0, 0,1, 1, 0, 0,1, 0, 0, 0,0, 0, 0, 0}}};const pattern pattern_list_box[] = {{2, 2,{1, 1, 0, 0,1, 1, 0, 0,0, 0, 0, 0,0, 0, 0, 0}}};const pattern pattern_list_t[] = {{3, 2,{1, 1, 1, 0,0, 1, 0, 0,0, 0, 0, 0,0, 0, 0, 0}},{2, 3,{0, 1, 0, 0,1, 1, 0, 0,0, 1, 0, 0,0, 0, 0, 0}},{3, 2,{0, 1, 0, 0,1, 1, 1, 0,0, 0, 0, 0,0, 0, 0, 0}},{2, 3,{1, 0, 0, 0,1, 1, 0, 0,1, 0, 0, 0,0, 0, 0, 0}}};//俄罗斯方块数据表const tetris tetris_list[] = {{sizeof(pattern_list_line) / sizeof(pattern_list_line[0]),pattern_list_line},{sizeof(pattern_list_l) / sizeof(pattern_list_l[0]),pattern_list_l},{sizeof(pattern_list_rl) / sizeof(pattern_list_rl[0]),pattern_list_rl},{sizeof(pattern_list_s) / sizeof(pattern_list_s[0]),pattern_list_s},{sizeof(pattern_list_z) / sizeof(pattern_list_z[0]),pattern_list_z},{sizeof(pattern_list_box) / sizeof(pattern_list_box[0]),pattern_list_box},{sizeof(pattern_list_t) / sizeof(pattern_list_t[0]),pattern_list_t}};//俄罗斯方块数据表长度const size_t TETRIS_LIST_LEN = (sizeof(tetris_list) / sizeof(tetris_list[0]));本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-04/130457.htm