歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C Windows控制台字符版本俄羅斯方塊

C Windows控制台字符版本俄羅斯方塊

日期:2017/3/1 9:16:42   编辑:Linux編程

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] = '\0';

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] = '\0';

}

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\n\0", score);

len = strlen(&buf[2]);

for (i = 0; i < 8 && name[i]; ++i) {

(&buf[2])[len + i] = name[i];

}

(&buf[2])[len + i] = '\0';

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]));

Copyright © Linux教程網 All Rights Reserved