前言:

  想起来做这个是因为那时候某天知道了原来黑框框里面的光标是可以控制的,而且又经常听人说起这个,就锻炼一下好了。

  之前就完成了那1.0的版本,现在想放上来分享却发现有蛮多问题的,而且最重要的是没什么注释【果然那时候太年轻】!现在看了也是被那时候的自己逗笑了,就修改了一些小bug,增加了算是详尽而清楚的注释,嗯,MSDN上面对各种函数的解释很详细的【又锻炼一下英语】,顺便让开头和结尾的展示“动”了起来,就当作1.5的版本好了。

  这个只是给出了一个实现的思路,其中肯定也有很多不合理的地方和可优化之处,希望能供大家参考和交流。

过程:

  期间也是遇到了蛮多困惑的。

  1.最先的是怎么知道按了方向键,左查右找,说法有好几个版本呢,就想看能不能自己测试一下自己的好了,再查再找,好了,感谢写了测试方向键的人;

  2.再比如说怎么消除窗口中一行的缓冲,因为不消除就一直在哪,视觉效果不好,翻查了一下资料,就写了delLine()这个来做这个事情了;

  3.设定颜色时,在cmd里面help color知道了颜色的参数,但是通过数字0-9来设定的太暗了,发现有更亮的,比如0A,在setColor()里面用它却说类型不对,于是上MSDN,发现还可以用宏,就想通过如'BACKGROUND_INTENSITY  | BACKGROUND_RED '之类来完成,就想怎么去代替那个宏,觉得每次写一长串好麻烦。然后换了各种类型的参数类型和不定长参数什么的,发现还是不行,后来一想,万一它支持数字10呢,A不就是10么?!一测,成了;

  4.还有一些判断状态的顺序,嗯啊,这些要先想好再下手,不然左改右改很麻烦呢;

  5.别的困惑不怎么记得了。。。

代码:

  下面分别给出LittleMines【好弱的名字】,测试颜色,测试方向键的代码。【反映说有行号不好复制,那取消好了】

/*********************************
* c语言命令行+方向键简易版扫雷
* Author:AnnsShadoW
* Version:1.5
* Time:2015-11-29
********************************/ /********************************
* 运行环境:Windows10-64bit
* 编译环境:Codeblocks-13.12
********************************/ //用到的都导进去吧
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <windows.h> //定义各种判断状态的ASCII码
//MINE是定义翻开格子中的‘*’号
#define MINE 42
#define ESC 27
#define ENTER 13
#define SPACE 32
#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77 //定义类型状态,方便后续判断
#define bool int
#define true 1
#define false 0
#define ROW 10
#define COLUMN 10
#define ALL_MINES 15 //当前位置的结构体
typedef struct currentPosition_struct
{
int x;
int y;
} currentPosition; //每一个小格的结构体
typedef struct blockCondition_struct
{
//是否被覆盖了
bool beCovered;
//以它为中心周围的雷数
int minesNum;
} blockCondition; //光标的位置数组
currentPosition cursorPos[ROW][COLUMN];
//雷区地图的数组
blockCondition minesMap[ROW][COLUMN];
//剩下的格子数
int leftBlocksNum = ROW * COLUMN;
//光标在光标位置、雷区地图中的下标
int index_x = , index_y = ; //设置窗口前后背景色
void setColor(unsigned short color);
//开头的欢迎“动画”
void welcomeToMyGame();
//游戏地图初始化
void gameInitailize();
//以某格子为中心计算惊天雷数量
void countMines();
//获取键盘的输入
void keyBoardInput();
//指定光标的位置
void setCurPos(int y, int x);
//移动光标的位置
void moveCursor(int y, int x);
//检测每一步的结果
bool checkResult(int y, int x);
//输出游戏界面
void printMap();
//游戏退出后的“动画”
void gameOver(char *str);
//删除窗口中一行的缓冲
void delLine(int y); int main()
{
setColor();
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);
//对设置之后的输出有效
SetConsoleTextAttribute(hCon, color);
}; void welcomeToMyGame()
{
int i = ;
char introductions0[] = "LittleMines";
char introductions1[] = "--";
char introductions2[] = "Version 1.5";
char introductions3[] = "Author:AnnsShadow,thank you ╮( ̄▽ ̄)╭"; //控制台窗口默认大小是80*25,所以能达到最大的位置是[79,24]
for(i = ; i <= ; ++i)
{
//每次输出之前都清屏,就会有看起来是动的效果
system("cls");
//纵坐标不断加,形成向下效果
setCurPos(i, ( - strlen(introductions0)) / );
printf("%s", introductions0);
//缓冲一下,太快了看不到呢
Sleep();
} //为了对称,从边边78开始到中间39好了
for(i = ; i >= ; --i)
{
//上面用了5行了,大于它吧
setCurPos(, i);
printf("%s", introductions1);
setCurPos(, - i);
printf("%s", introductions1);
Sleep();
} //从左边一步步进入屏幕中间
for(i = ; i <= ( - strlen(introductions2)) / ; ++i)
{
//要删除这一行缓冲的原因:
//上一次循环的输出会影响到下一次,如输出VVVVVVVVVVersion1.0
//换成中文就不会,中文要两个字节才能显示完整呀
delLine();
//这里就会有闪闪发亮的效果哦
Sleep();
setCurPos(, i);
printf("%s", introductions2);
Sleep();
} //从底部进入
for(i = ; i >= ; --i)
{
setCurPos(i, ( - strlen(introductions3)) / );
printf("%s", introductions3);
Sleep();
//删除上一次的缓冲,不加1的话最后一行就会残留,其它都不见了
delLine(i + );
Sleep();
} Sleep();
char help0[] = "动啊:←↑↓→╮(╯▽╰)╭";
char help1[] = "点击啊:Space / Enter (ΘェΘ)";
char help2[] = "不玩啦:Esc (>﹏<)";
char help3[] = "<<愿你玩的开心 _(:з」∠)_>>";
setCurPos(, ( - strlen(help0)) / );
setColor();
printf("%s", help0);
setCurPos(, ( - strlen(help1)) / );
printf("%s", help1);
setCurPos(, ( - strlen(help2)) / );
printf("%s", help2);
setCurPos(, ( - strlen(help3)) / );
setColor();
printf("%s", help3);
getch();
} void gameInitailize()
{
int i = , j = ;
int allMines = ALL_MINES;
//设置随机值
srand((unsigned int)time(NULL)); //雷区地图初始化
for(i = ; i < ROW; ++i)
{
for(j = ; j < COLUMN; ++j)
{
minesMap[i][j].beCovered = true;
minesMap[i][j].minesNum = ;
}
} //放置惊天雷!
while(allMines)
{
i = rand() % ROW;
j = rand() % COLUMN; if(minesMap[i][j].minesNum == )
{
//这个‘-1’就作为判断惊天雷的依据了
minesMap[i][j].minesNum = -;
--allMines;
}
} //光标位置初始化
for(i = ; i < ROW; ++i)
{
for(j = ; j < COLUMN; ++j)
{
cursorPos[i][j].x = j * + ;
cursorPos[i][j].y = i * + ;
}
}
} void countMines()
{
int i = , j = , m = , n = ;
//以格子为中心周围的雷数
int minesNum = ; for(i = ; i < ROW; ++i)
{
for(j = ; j < COLUMN; ++j)
{
//遇到惊天雷就放弃统计吧
if(minesMap[i][j].minesNum == -)
continue;
minesNum = ;
//九宫格嘛,那3次好了
for(m = -; m <= ; ++m)
{
//行溢出了没,不能算没有的哦
if(i + m < || i + m >= ROW)
{
continue;
} for(n = -; n <= ; ++n)
{
//这次就是看列溢出了没
if(j + n < || j + n >= COLUMN)
{
continue;
}
//周边有惊天雷赶紧加起来
if(minesMap[i + m][j + n].minesNum == -)
{
++minesNum;
}
}
}
minesMap[i][j].minesNum = minesNum;
}
}
} void keyBoardInput()
{
bool lose;
int key1 = getch(); /*****************************
测试之后才知道方向键两个字节
第一个字节ASCII 0x00e0 224
第二个字节分别是:
上:0x0048 72
下:0x0050 80
左:0x012b 75
右:0x012d 77
*****************************/ if(key1 == )
{
int key2 = getch(); switch(key2)
{
case UP:
moveCursor(index_y - , index_x);
break; case DOWN:
moveCursor(index_y + , index_x);
break; case LEFT:
moveCursor(index_y, index_x - );
break; case RIGHT:
moveCursor(index_y, index_x + );
break; default:
break;
}
}
else
{
switch(key1)
{
case ENTER:
case SPACE:
lose = checkResult(index_y, index_x);
system("cls");
printMap(); if(lose)
{
setColor();
printf("| 诶哟,还差一点点哦! ╥﹏╥ |\n");
printf("| 按\"r\"重玩,Esc不玩啦。 |\n");
printf("[%c]-------------------------------------------------------[%c]\n", MINE, MINE);
setColor();
Sleep();
char key3 = getch(); if(key3 == 'r' || key3 == 'R')
{
//重来,跟main中过程是一样的
setColor();
gameInitailize();
countMines();
printMap();
}
}
//剩余的格子比雷还要多,可以继续玩
else if(leftBlocksNum > ALL_MINES)
{
setColor();
printf("| 哎哟,挺不错哦~ ( ̄0  ̄) |\n");
printf("[%c]-------------------------------------------------------[%c]\n", MINE, MINE);
setColor();
}
//来到这你已经赢了
else
{
setColor();
printf("| 哟,恭喜你赢了(/≧▽≦/) |\n");
printf("| 按\"r\"重玩,Esc就不玩啦。 |\n");
printf("[%c]-------------------------------------------------------[%c]\n", MINE, MINE);
setColor();
Sleep();
char key3 = getch(); if(key3 == 'r' || key3 == 'R')
{
setColor();
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)
{
//在窗口缓冲中定义每个位置的状态
COORD currentPosition;
currentPosition.Y = y;
currentPosition.X = x;
//所以现在的位置是在{y,x}
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), currentPosition);
} void moveCursor(int y, int x)
{
//限定能走的地方
if((x >= && x < COLUMN) && (y >= && 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 = , j = ; //检测有没有溢出地图了
if(x < || x >= COLUMN || y < || y >= ROW)
{
return false;
} //就是你了!被选中的格子!
minesMap[y][x].beCovered = false; //被惊天雷炸了
if(minesMap[y][x].minesNum == -)
{
minesMap[y][x].minesNum = ;
return true;
} //如果没有雷,就当作空格吧
if(minesMap[y][x].minesNum > && minesMap[y][x].minesNum < )
{
return false;
} //九宫格,3x3咯
for(i = -; i <= ; ++i)
{
//检查一下在这一行溢出了没吧
if(y + i < || y + i >= ROW)
{
continue;
} for(j = -; j <= ; ++j)
{
//这次就到列了吧
if(x + j < || x + j >= COLUMN)
{
continue;
}
//如果下一个是没开过的,就检查它吧
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[] = "动啊";
char help2[] = "Space / Enter";
char help3[] = "点击啊";
char help4[] = "Esc 不玩啦";
//因为要输出提示,所以地图不能太大了,10x10就差不多了
setColor();
setCurPos(, );
printf("%s", help0);
setCurPos(, );
printf("%s", help1);
setCurPos(, );
printf("%s", help2);
setCurPos(, );
printf("%s", help3);
setCurPos(, );
printf("%s", help4);
setCurPos(, );
setColor(); int i = , j = , k = ;
leftBlocksNum = ;
setColor();
printf("[开]--");
setColor(); for(k = ; k < COLUMN - ; ++k)
{
printf("+-----");
}
setColor();
printf("+--[心]\n");
setColor(); for(i = ; i < ROW; ++i)
{
for(j = ; j < COLUMN; ++j)
{
if(minesMap[i][j].beCovered)
{
++leftBlocksNum;
//这个输出的就是格子被覆盖的时候输出的图形,可以换成1-6试试
//1-4是正方形的4个角,5-6是双竖线和双横线
printf("| %c ", );
}
else if(minesMap[i][j].minesNum == - || minesMap[i][j].minesNum == )
{
printf("| %c ", MINE);
}
else if(minesMap[i][j].minesNum == )
{
printf("| %c ", ' ');
}
else
{
printf("| %d ", minesMap[i][j].minesNum);
}
} printf("|\n"); if(i < ROW - )
{
for(k = ; k < COLUMN; ++k)
{
printf("+-----");
} printf("+\n");
}
}
setColor();
printf("[就]--");
setColor(); for(k = ; k < COLUMN - ; ++k)
{
printf("+-----");
}
setColor();
printf("+--[好]\n");
setColor();
} void gameOver(char *str)
{
setColor();
system("cls");
setCurPos(, );
int i = ; do
{
//逐字输出
printf("%c", str[i]);
Sleep();
}
while(str[i++]);
setColor();
system("pause");
//随意终止程序并返回给OS,0是正常的
exit();
} void delLine(int y)
{
HANDLE hOutput;
//窗口缓存信息
CONSOLE_SCREEN_BUFFER_INFO sbi;
DWORD len, nw;
//用MSDN上的TCHAR类型跪了,换成char就好
char fillchar = ' ';
//定位光标
COORD startPosition = {, y};
//获取输出句柄
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//获取窗口缓冲中的信息
GetConsoleScreenBufferInfo(hOutput, &sbi);
//窗口缓冲的位置,这里取得X值
len = sbi.dwSize.X;
//从特定的位置用特定的字符去填充窗口的缓冲特定次数
//成功返回非0值,一般都成功,就不判断了
FillConsoleOutputCharacter(hOutput, fillchar, len, startPosition, &nw);
}

测试颜色:

 #include <windows.h>
#include <stdlib.h>
#include <stdio.h> void setColor(unsigned short color)
{
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
//对设置之后的输出有效
SetConsoleTextAttribute(hCon, color);
}; int main()
{
//测试颜色啊~~
for(int i = ; i <= ; i++)
{
setColor(i);
printf("%d\n", i);
system("pause");
}
return ;
}

测试方向键:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h> int main()
{
unsigned short int k; while()
{
_sleep(); if(_kbhit())
{
k = _getch(); if( == k)
k = _getch() << ; _cprintf("key:0x%04x pressed\r\n", k);
}
}
system("pause");
return ;
}

运行截图:图片不会啦,在自己机子跑起来就看得到的效果了~~~

后话:

  虽然不是什么很厉害的事情,稍微懂点的都可以自己做出来,不过在实践的过程中还是收获蛮多的,在这分享也算个小小的记录吧,继续加油~

  

C+命令行+方向键=简易版扫雷的更多相关文章

  1. 命令行:增强版 | Linux 中国

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/F8qG7f9YD02Pe/article/details/83542677 以下就是我如今使用的用于 ...

  2. SVN 命令行 精编版

    1.将文件checkout到本地目录 svn checkout path(path是服务器上的目录) 例如:svn checkout https://svn.sinaapp.com/beckhom 简 ...

  3. [批处理]简易命令行RAR

    这个BAT是为了病毒满满的信管实验室而专门定制的,在这机房上了两年,跟病毒也玩了两年了,也都脸熟的不行不行的了,来来回回就那几个病毒不是autorun.inf就是Desktop__.ini要么就是ga ...

  4. C++扫雷小游戏(基于CMD命令行)

    这个小游戏是笔者在大一C语言课程设计的时候写的,基于命令行,为了显得漂亮一些,特别加上了彩色特效~~~ 注意:Win10系统须将命令行调为旧版命令行,否则有可能会显示乱码! 代码示例: #includ ...

  5. GitBook制作电子书详细教程(命令行版)

    GitBook 是一款基于 Node.js 开发的开源的工具,可以通过命令行的方式创建电子书项目,再使用 MarkDown 编写电子书内容,然后生成 PDF.ePub.mobi 格式的电子书,或生成一 ...

  6. 命令行查看linux发行版版本信息

    有时候安装完自己的linux发行版系统(如ubuntu.centos.redhat.opensuse.--)时,把版本信息忽略了,又不想重启电脑,此时我们可以通过命令行方式来查看: 1.cat /et ...

  7. Python 实现有道翻译命令行版

    一.个人需求 由于一直用Linux系统,对于词典的支持特别不好,对于我这英语渣渣的人来说,当看英文文档就一直卡壳,之前用惯了有道词典,感觉很不错,虽然有网页版的但是对于全站英文的网页来说并不支持.索性 ...

  8. Shell终端收听音乐--网易云音乐命令行版

    Musicbox:网易云音乐命令行版本 高品质网易云音乐命令行版本,简洁优雅,丝般顺滑,基于Python编写. 这款命令行的客户端使用 Python 构建,以 mpg123 作为播放后端: Vim 式 ...

  9. c语言15行实现简易cat命令

    刚刚和舍友打赌.舍友说PY20行能做xlsx文件分析整理,C20行屁都干不了.我说简单的cat还是能做的嘛.他说不信.我说不处理非文件的参数的话10行能做啊. 下面直接贴代码吧: #include & ...

随机推荐

  1. Scalaz(39)- Free :a real monadic program

    一直感觉FP比较虚,可能太多学术性的东西,不知道如何把这些由数学理论在背后支持的一套全新数据类型和数据结构在现实开发中加以使用.直到Free Monad,才真正感觉能用FP方式进行编程了.在前面我们已 ...

  2. InfluxDB学习之InfluxDB的HTTP API写入操作

    HTTP API也有两种操作:写入和查询,本文就先给大家介绍一下 InfluxDB的HTTP API的写入操作方式.     在InfluxDB学习的上一篇文章:InfluxDB学习之InfluxDB ...

  3. 把Java生成的RSA公钥、私钥转换成.NET使用的XML格式

    import java.security.KeyFactory; import java.security.interfaces.RSAPrivateCrtKey; import java.secur ...

  4. java多线程之hashmap concurrenthashmap的状态同步

    最近在高并发的系统中发现,concurrenthashmap除了大家熟知的避免循环期间发生ConcurrentModificationException异常外,还有重要的一点是Retrievals r ...

  5. .Net中的并行编程-6.常用优化策略

                本文是.Net中的并行编程第六篇,今天就介绍一些我在实际项目中的一些常用优化策略.      一.避免线程之间共享数据 避免线程之间共享数据主要是因为锁的问题,无论什么粒度的锁 ...

  6. pagePiling.js - 创建漂亮的全屏滚动效果

    全屏滚动效果是最近非常流行的网页设计形式,带给用户良好的视觉和交互体验.pagePiling.js 这款 jQuery 插件可以帮助前端开发人员轻松实现这种效果.支持所有的主流浏览器,包括IE8+,支 ...

  7. 一步一步教你如何解锁被盗的iPhone 6S

    即使你的iPhone6S设置了六位数的密码,甚至还设置了touch ID,但我要告诉你的是:你的手机仍然能被犯罪分子解锁. 事件背景 三天前,一位苹果用户的iPhone6S被偷了.随后,小偷重置了该用 ...

  8. Atitit.在线充值功能的设计

    Atitit.在线充值功能的设计 1. 流程1 2. Js sdk api   增加订单1 3. Java api 返回servlet处理1 3.1. 返回网址的本地host测试2 1. 流程 本地增 ...

  9. Thoughts on an Article from Science 'A network framework of cultural history'

    Apparently, this is an excellent interdisciplinary study. This paper drawn on a simple but large dat ...

  10. Windows 上的 Jetty 小工具

    做项目经常遇到需要开发Java应用,我喜欢用Jetty进行开发.部署,主要是由于Jetty的轻量级. Jetty 项目主页:http://www.eclipse.org/jetty/, 最新版9.30 ...