C語(yǔ)言開(kāi)發(fā)簡(jiǎn)易版掃雷小游戲
前言:
想起來(lái)做這個(gè)是因?yàn)槟菚r(shí)候某天知道了原來(lái)黑框框里面的光標(biāo)是可以控制的,而且又經(jīng)常聽(tīng)人說(shuō)起這個(gè),就鍛煉一下好了。
之前就完成了那1.0的版本,現(xiàn)在想放上來(lái)分享卻發(fā)現(xiàn)有蠻多問(wèn)題的,而且最重要的是沒(méi)什么注釋【果然那時(shí)候太年輕】!現(xiàn)在看了也是被那時(shí)候的自己逗笑了,就修改了一些小bug,增加了算是詳盡而清楚的注釋?zhuān)?,MSDN上面對(duì)各種函數(shù)的解釋很詳細(xì)的【又鍛煉一下英語(yǔ)】,順便讓開(kāi)頭和結(jié)尾的展示“動(dòng)”了起來(lái),就當(dāng)作1.5的版本好了。
這個(gè)只是給出了一個(gè)實(shí)現(xiàn)的思路,其中肯定也有很多不合理的地方和可優(yōu)化之處,希望能供大家參考和交流。
過(guò)程:
期間也是遇到了蠻多困惑的。
1.最先的是怎么知道按了方向鍵,左查右找,說(shuō)法有好幾個(gè)版本呢,就想看能不能自己測(cè)試一下自己的好了,再查再找,好了,感謝寫(xiě)了測(cè)試方向鍵的人;
2.再比如說(shuō)怎么消除窗口中一行的緩沖,因?yàn)椴幌鸵恢痹谀模曈X(jué)效果不好,翻查了一下資料,就寫(xiě)了delLine()這個(gè)來(lái)做這個(gè)事情了;
3.設(shè)定顏色時(shí),在cmd里面help color知道了顏色的參數(shù),但是通過(guò)數(shù)字0-9來(lái)設(shè)定的太暗了,發(fā)現(xiàn)有更亮的,比如0A,在setColor()里面用它卻說(shuō)類(lèi)型不對(duì),于是上MSDN,發(fā)現(xiàn)還可以用宏,就想通過(guò)如'BACKGROUND_INTENSITY | BACKGROUND_RED '之類(lèi)來(lái)完成,就想怎么去代替那個(gè)宏,覺(jué)得每次寫(xiě)一長(zhǎng)串好麻煩。然后換了各種類(lèi)型的參數(shù)類(lèi)型和不定長(zhǎng)參數(shù)什么的,發(fā)現(xiàn)還是不行,后來(lái)一想,萬(wàn)一它支持?jǐn)?shù)字10呢,A不就是10么?!一測(cè),成了;
4.還有一些判斷狀態(tài)的順序,嗯啊,這些要先想好再下手,不然左改右改很麻煩呢;
5.別的困惑不怎么記得了。。。
代碼:
下面分別給出LittleMines【好弱的名字】,測(cè)試顏色,測(cè)試方向鍵的代碼。【反映說(shuō)有行號(hào)不好復(fù)制,那取消好了】
/********************************* * c語(yǔ)言命令行+方向鍵簡(jiǎn)易版掃雷 * Author:AnnsShadoW * Version:1.5 * Time:2015-11-29 ********************************/ /******************************** * 運(yùn)行環(huán)境:Windows10-64bit * 編譯環(huán)境:Codeblocks-13.12 ********************************/ //用到的都導(dǎo)進(jìn)去吧 #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <time.h> #include <windows.h> //定義各種判斷狀態(tài)的ASCII碼 //MINE是定義翻開(kāi)格子中的‘*'號(hào) #define MINE 42 #define ESC 27 #define ENTER 13 #define SPACE 32 #define UP 72 #define DOWN 80 #define LEFT 75 #define RIGHT 77 //定義類(lèi)型狀態(tài),方便后續(xù)判斷 #define bool int #define true 1 #define false 0 #define ROW 10 #define COLUMN 10 #define ALL_MINES 15 //當(dāng)前位置的結(jié)構(gòu)體 typedef struct currentPosition_struct { int x; int y; } currentPosition; //每一個(gè)小格的結(jié)構(gòu)體 typedef struct blockCondition_struct { //是否被覆蓋了 bool beCovered; //以它為中心周?chē)睦讛?shù) int minesNum; } blockCondition; //光標(biāo)的位置數(shù)組 currentPosition cursorPos[ROW][COLUMN]; //雷區(qū)地圖的數(shù)組 blockCondition minesMap[ROW][COLUMN]; //剩下的格子數(shù) int leftBlocksNum = ROW * COLUMN; //光標(biāo)在光標(biāo)位置、雷區(qū)地圖中的下標(biāo) int index_x = 0, index_y = 0; //設(shè)置窗口前后背景色 void setColor(unsigned short color); //開(kāi)頭的歡迎“動(dòng)畫(huà)” void welcomeToMyGame(); //游戲地圖初始化 void gameInitailize(); //以某格子為中心計(jì)算驚天雷數(shù)量 void countMines(); //獲取鍵盤(pán)的輸入 void keyBoardInput(); //指定光標(biāo)的位置 void setCurPos(int y, int x); //移動(dòng)光標(biāo)的位置 void moveCursor(int y, int x); //檢測(cè)每一步的結(jié)果 bool checkResult(int y, int x); //輸出游戲界面 void printMap(); //游戲退出后的“動(dòng)畫(huà)” void gameOver(char *str); //刪除窗口中一行的緩沖 void delLine(int y); int main() { setColor(10); system("cls"); welcomeToMyGame(); gameInitailize(); countMines(); printMap(); for(;;) { setCurPos(cursorPos[index_y][index_x].y, cursorPos[index_y][index_x].x); keyBoardInput(); } return EXIT_SUCCESS; } void setColor(unsigned short color) { HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); //對(duì)設(shè)置之后的輸出有效 SetConsoleTextAttribute(hCon, color); }; void welcomeToMyGame() { int i = 0; char introductions0[] = "LittleMines"; char introductions1[] = "--"; char introductions2[] = "Version 1.5"; char introductions3[] = "Author:AnnsShadow,thank you ╮( ̄▽?zhuān)?╭"; //控制臺(tái)窗口默認(rèn)大小是80*25,所以能達(dá)到最大的位置是[79,24] for(i = 0; i <= 5; ++i) { //每次輸出之前都清屏,就會(huì)有看起來(lái)是動(dòng)的效果 system("cls"); //縱坐標(biāo)不斷加,形成向下效果 setCurPos(i, (80 - strlen(introductions0)) / 2); printf("%s", introductions0); //緩沖一下,太快了看不到呢 Sleep(50); } //為了對(duì)稱(chēng),從邊邊78開(kāi)始到中間39好了 for(i = 78; i >= 39; --i) { //上面用了5行了,大于它吧 setCurPos(7, i); printf("%s", introductions1); setCurPos(7, 78 - i); printf("%s", introductions1); Sleep(40); } //從左邊一步步進(jìn)入屏幕中間 for(i = 0; i <= (80 - strlen(introductions2)) / 2; ++i) { //要?jiǎng)h除這一行緩沖的原因: //上一次循環(huán)的輸出會(huì)影響到下一次,如輸出VVVVVVVVVVersion1.0 //換成中文就不會(huì),中文要兩個(gè)字節(jié)才能顯示完整呀 delLine(9); //這里就會(huì)有閃閃發(fā)亮的效果哦 Sleep(10); setCurPos(9, i); printf("%s", introductions2); Sleep(50); } //從底部進(jìn)入 for(i = 24; i >= 12; --i) { setCurPos(i, (80 - strlen(introductions3)) / 2); printf("%s", introductions3); Sleep(20); //刪除上一次的緩沖,不加1的話最后一行就會(huì)殘留,其它都不見(jiàn)了 delLine(i + 1); Sleep(50); } Sleep(500); char help0[] = "動(dòng)?。骸r(╯▽╰)╭"; char help1[] = "點(diǎn)擊啊:Space / Enter (ΘェΘ)"; char help2[] = "不玩啦:Esc (>﹏<)"; char help3[] = "<<愿你玩的開(kāi)心 _(:з」∠)_>>"; setCurPos(14, (80 - strlen(help0)) / 2); setColor(14); printf("%s", help0); setCurPos(15, (80 - strlen(help1)) / 2); printf("%s", help1); setCurPos(16, (80 - strlen(help2)) / 2); printf("%s", help2); setCurPos(17, (80 - strlen(help3)) / 2); setColor(15); printf("%s", help3); getch(); } void gameInitailize() { int i = 0, j = 0; int allMines = ALL_MINES; //設(shè)置隨機(jī)值 srand((unsigned int)time(NULL)); //雷區(qū)地圖初始化 for(i = 0; i < ROW; ++i) { for(j = 0; j < COLUMN; ++j) { minesMap[i][j].beCovered = true; minesMap[i][j].minesNum = 0; } } //放置驚天雷! while(allMines) { i = rand() % ROW; j = rand() % COLUMN; if(minesMap[i][j].minesNum == 0) { //這個(gè)‘-1'就作為判斷驚天雷的依據(jù)了 minesMap[i][j].minesNum = -1; --allMines; } } //光標(biāo)位置初始化 for(i = 0; i < ROW; ++i) { for(j = 0; j < COLUMN; ++j) { cursorPos[i][j].x = j * 6 + 3; cursorPos[i][j].y = i * 2 + 1; } } } void countMines() { int i = 0, j = 0, m = 0, n = 0; //以格子為中心周?chē)睦讛?shù) int minesNum = 0; for(i = 0; i < ROW; ++i) { for(j = 0; j < COLUMN; ++j) { //遇到驚天雷就放棄統(tǒng)計(jì)吧 if(minesMap[i][j].minesNum == -1) continue; minesNum = 0; //九宮格嘛,那3次好了 for(m = -1; m <= 1; ++m) { //行溢出了沒(méi),不能算沒(méi)有的哦 if(i + m < 0 || i + m >= ROW) { continue; } for(n = -1; n <= 1; ++n) { //這次就是看列溢出了沒(méi) if(j + n < 0 || j + n >= COLUMN) { continue; } //周邊有驚天雷趕緊加起來(lái) if(minesMap[i + m][j + n].minesNum == -1) { ++minesNum; } } } minesMap[i][j].minesNum = minesNum; } } } void keyBoardInput() { bool lose; int key1 = getch(); /***************************** 測(cè)試之后才知道方向鍵兩個(gè)字節(jié) 第一個(gè)字節(jié)ASCII 0x00e0 224 第二個(gè)字節(jié)分別是: 上:0x0048 72 下:0x0050 80 左:0x012b 75 右:0x012d 77 *****************************/ if(key1 == 224) { int key2 = getch(); switch(key2) { case UP: moveCursor(index_y - 1, index_x); break; case DOWN: moveCursor(index_y + 1, index_x); break; case LEFT: moveCursor(index_y, index_x - 1); break; case RIGHT: moveCursor(index_y, index_x + 1); break; default: break; } } else { switch(key1) { case ENTER: case SPACE: lose = checkResult(index_y, index_x); system("cls"); printMap(); if(lose) { setColor(13); printf("| 誒喲,還差一點(diǎn)點(diǎn)哦! ╥﹏╥ |\n"); printf("| 按\"r\"重玩,Esc不玩啦。 |\n"); printf("[%c]-------------------------------------------------------[%c]\n", MINE, MINE); setColor(10); Sleep(1000); char key3 = getch(); if(key3 == 'r' || key3 == 'R') { //重來(lái),跟main中過(guò)程是一樣的 setColor(10); gameInitailize(); countMines(); printMap(); } } //剩余的格子比雷還要多,可以繼續(xù)玩 else if(leftBlocksNum > ALL_MINES) { setColor(13); printf("| 哎喲,挺不錯(cuò)哦~ ( ̄0  ̄) |\n"); printf("[%c]-------------------------------------------------------[%c]\n", MINE, MINE); setColor(10); } //來(lái)到這你已經(jīng)贏了 else { setColor(13); printf("| 喲,恭喜你贏了(/≧▽≦/) |\n"); printf("| 按\"r\"重玩,Esc就不玩啦。 |\n"); printf("[%c]-------------------------------------------------------[%c]\n", MINE, MINE); setColor(10); Sleep(1000); char key3 = getch(); if(key3 == 'r' || key3 == 'R') { setColor(10); gameInitailize(); countMines(); printMap(); } } break; case ESC: system("cls"); gameOver("\t\t\t啦啦啦~很逗很扯吧~最后感謝你的玩耍呀(≧Д≦)\n\n\n\n\n\n\n\n"); default: break; } } } void setCurPos(int y, int x) { //在窗口緩沖中定義每個(gè)位置的狀態(tài) COORD currentPosition; currentPosition.Y = y; currentPosition.X = x; //所以現(xiàn)在的位置是在{y,x} SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), currentPosition); } void moveCursor(int y, int x) { //限定能走的地方 if((x >= 0 && x < COLUMN) && (y >= 0 && y < ROW)) { setCurPos(cursorPos[y][x].y, cursorPos[y][x].x); index_x = x; index_y = y; } } bool checkResult(int y, int x) { int i = 0, j = 0; //檢測(cè)有沒(méi)有溢出地圖了 if(x < 0 || x >= COLUMN || y < 0 || y >= ROW) { return false; } //就是你了!被選中的格子! minesMap[y][x].beCovered = false; //被驚天雷炸了 if(minesMap[y][x].minesNum == -1) { minesMap[y][x].minesNum = 9; return true; } //如果沒(méi)有雷,就當(dāng)作空格吧 if(minesMap[y][x].minesNum > 0 && minesMap[y][x].minesNum < 9) { return false; } //九宮格,3x3咯 for(i = -1; i <= 1; ++i) { //檢查一下在這一行溢出了沒(méi)吧 if(y + i < 0 || y + i >= ROW) { continue; } for(j = -1; j <= 1; ++j) { //這次就到列了吧 if(x + j < 0 || x + j >= COLUMN) { continue; } //如果下一個(gè)是沒(méi)開(kāi)過(guò)的,就檢查它吧 if(minesMap[y + i][x + j].beCovered) { minesMap[y + i][x + j].beCovered = false; checkResult(y + i, x + j); } } } return false; } void printMap() { system("cls"); char help0[] = "←↑↓→"; char help1[] = "動(dòng)啊"; char help2[] = "Space / Enter"; char help3[] = "點(diǎn)擊啊"; char help4[] = "Esc 不玩啦"; //因?yàn)橐敵鎏崾?,所以地圖不能太大了,10x10就差不多了 setColor(14); setCurPos(4, 62); printf("%s", help0); setCurPos(6, 62); printf("%s", help1); setCurPos(9, 62); printf("%s", help2); setCurPos(11, 62); printf("%s", help3); setCurPos(14, 62); printf("%s", help4); setCurPos(0, 0); setColor(10); int i = 0, j = 0, k = 0; leftBlocksNum = 0; setColor(11); printf("[開(kāi)]--"); setColor(10); for(k = 1; k < COLUMN - 1; ++k) { printf("+-----"); } setColor(11); printf("+--[心]\n"); setColor(10); for(i = 0; i < ROW; ++i) { for(j = 0; j < COLUMN; ++j) { if(minesMap[i][j].beCovered) { ++leftBlocksNum; //這個(gè)輸出的就是格子被覆蓋的時(shí)候輸出的圖形,可以換成1-6試試 //1-4是正方形的4個(gè)角,5-6是雙豎線和雙橫線 printf("| %c ", 3); } else if(minesMap[i][j].minesNum == -1 || minesMap[i][j].minesNum == 9) { printf("| %c ", MINE); } else if(minesMap[i][j].minesNum == 0) { printf("| %c ", ' '); } else { printf("| %d ", minesMap[i][j].minesNum); } } printf("|\n"); if(i < ROW - 1) { for(k = 0; k < COLUMN; ++k) { printf("+-----"); } printf("+\n"); } } setColor(11); printf("[就]--"); setColor(10); for(k = 1; k < COLUMN - 1; ++k) { printf("+-----"); } setColor(11); printf("+--[好]\n"); setColor(10); } void gameOver(char *str) { setColor(12); system("cls"); setCurPos(10, 0); int i = 0; do { //逐字輸出 printf("%c", str[i]); Sleep(60); } while(str[i++]); setColor(15); system("pause"); //隨意終止程序并返回給OS,0是正常的 exit(0); } void delLine(int y) { HANDLE hOutput; //窗口緩存信息 CONSOLE_SCREEN_BUFFER_INFO sbi; DWORD len, nw; //用MSDN上的TCHAR類(lèi)型跪了,換成char就好 char fillchar = ' '; //定位光標(biāo) COORD startPosition = {0, y}; //獲取輸出句柄 hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //獲取窗口緩沖中的信息 GetConsoleScreenBufferInfo(hOutput, &sbi); //窗口緩沖的位置,這里取得X值 len = sbi.dwSize.X; //從特定的位置用特定的字符去填充窗口的緩沖特定次數(shù) //成功返回非0值,一般都成功,就不判斷了 FillConsoleOutputCharacter(hOutput, fillchar, len, startPosition, &nw); }
測(cè)試顏色:
#include <windows.h> #include <stdlib.h> #include <stdio.h> void setColor(unsigned short color) { HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); //對(duì)設(shè)置之后的輸出有效 SetConsoleTextAttribute(hCon, color); }; int main() { //測(cè)試顏色啊~~ for(int i = 0; i <= 255; i++) { setColor(i); printf("%d\n", i); system("pause"); } return 0; }
測(cè)試方向鍵:
#include <stdio.h> #include <stdlib.h> #include <windows.h> #include <conio.h> int main() { unsigned short int k; while(1) { _sleep(100); if(_kbhit()) { k = _getch(); if(0 == k) k = _getch() << 8; _cprintf("key:0x%04x pressed\r\n", k); } } system("pause"); return 0; }
運(yùn)行截圖:圖片不會(huì)動(dòng)啦,在自己機(jī)子跑起來(lái)就看得到動(dòng)的效果了~~~
后話:
雖然不是什么很厲害的事情,稍微懂點(diǎn)的都可以自己做出來(lái),不過(guò)在實(shí)踐的過(guò)程中還是收獲蠻多的,在這分享也算個(gè)小小的記錄吧,繼續(xù)加油~
- 基于C語(yǔ)言實(shí)現(xiàn)的掃雷游戲代碼
- C語(yǔ)言代碼實(shí)現(xiàn)掃雷游戲
- C語(yǔ)言快速實(shí)現(xiàn)掃雷小游戲
- C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單掃雷小游戲
- C語(yǔ)言 掃雷程序的實(shí)現(xiàn)
- C語(yǔ)言實(shí)現(xiàn)掃雷小游戲(適合初學(xué)者)
- C語(yǔ)言代碼實(shí)現(xiàn)簡(jiǎn)單掃雷小游戲
- C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單掃雷小程序
- C語(yǔ)言實(shí)現(xiàn)掃雷附完整代碼
- C語(yǔ)言輕松實(shí)現(xiàn)掃雷小游戲
相關(guān)文章
C++符號(hào)優(yōu)先級(jí)(詳細(xì)整理)
C++符號(hào)優(yōu)先級(jí),我詳細(xì)整理了一下。需要的朋友可以過(guò)來(lái)參考下。希望對(duì)大家有所幫助2013-10-10Matlab實(shí)現(xiàn)鼠標(biāo)光標(biāo)變成愛(ài)心和瞄準(zhǔn)鏡形狀
這篇文章主要為大家詳細(xì)介紹了如何利用Matlab實(shí)現(xiàn)將鼠標(biāo)光標(biāo)變成愛(ài)心和瞄準(zhǔn)鏡等形狀,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-08-08c語(yǔ)言版本二叉樹(shù)基本操作示例(先序 遞歸 非遞歸)
這篇文章主要介紹了實(shí)現(xiàn)二叉樹(shù)的創(chuàng)建(先序)、遞歸及非遞歸的先、中、后序遍歷2013-11-11C++之string類(lèi)對(duì)象的容量操作詳解
通過(guò)在網(wǎng)站上的資料搜集,得到了很多關(guān)于string類(lèi)對(duì)象的容量操作,通過(guò)對(duì)這些資料的整理和加入一些自己的代碼,希望能夠給你帶來(lái)幫助2021-08-08Qt下調(diào)用vlc庫(kù)實(shí)現(xiàn)RTSP拉流播放和截圖過(guò)程詳解
這篇文章主要為大家介紹了Qt下調(diào)用vlc庫(kù)實(shí)現(xiàn)RTSP拉流播放和截圖過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08C++實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器的相關(guān)資料,感興趣的朋友可以參考下2016-05-05