C+命令行+方向键=简易版扫雷
前言:
想起来做这个是因为那时候某天知道了原来黑框框里面的光标是可以控制的,而且又经常听人说起这个,就锻炼一下好了。
之前就完成了那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+命令行+方向键=简易版扫雷的更多相关文章
- 命令行:增强版 | Linux 中国
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/F8qG7f9YD02Pe/article/details/83542677 以下就是我如今使用的用于 ...
- SVN 命令行 精编版
1.将文件checkout到本地目录 svn checkout path(path是服务器上的目录) 例如:svn checkout https://svn.sinaapp.com/beckhom 简 ...
- [批处理]简易命令行RAR
这个BAT是为了病毒满满的信管实验室而专门定制的,在这机房上了两年,跟病毒也玩了两年了,也都脸熟的不行不行的了,来来回回就那几个病毒不是autorun.inf就是Desktop__.ini要么就是ga ...
- C++扫雷小游戏(基于CMD命令行)
这个小游戏是笔者在大一C语言课程设计的时候写的,基于命令行,为了显得漂亮一些,特别加上了彩色特效~~~ 注意:Win10系统须将命令行调为旧版命令行,否则有可能会显示乱码! 代码示例: #includ ...
- GitBook制作电子书详细教程(命令行版)
GitBook 是一款基于 Node.js 开发的开源的工具,可以通过命令行的方式创建电子书项目,再使用 MarkDown 编写电子书内容,然后生成 PDF.ePub.mobi 格式的电子书,或生成一 ...
- 命令行查看linux发行版版本信息
有时候安装完自己的linux发行版系统(如ubuntu.centos.redhat.opensuse.--)时,把版本信息忽略了,又不想重启电脑,此时我们可以通过命令行方式来查看: 1.cat /et ...
- Python 实现有道翻译命令行版
一.个人需求 由于一直用Linux系统,对于词典的支持特别不好,对于我这英语渣渣的人来说,当看英文文档就一直卡壳,之前用惯了有道词典,感觉很不错,虽然有网页版的但是对于全站英文的网页来说并不支持.索性 ...
- Shell终端收听音乐--网易云音乐命令行版
Musicbox:网易云音乐命令行版本 高品质网易云音乐命令行版本,简洁优雅,丝般顺滑,基于Python编写. 这款命令行的客户端使用 Python 构建,以 mpg123 作为播放后端: Vim 式 ...
- c语言15行实现简易cat命令
刚刚和舍友打赌.舍友说PY20行能做xlsx文件分析整理,C20行屁都干不了.我说简单的cat还是能做的嘛.他说不信.我说不处理非文件的参数的话10行能做啊. 下面直接贴代码吧: #include & ...
随机推荐
- Gym 101102D---Rectangles(单调栈)
题目链接 http://codeforces.com/gym/101102/problem/D problem description Given an R×C grid with each cel ...
- Java基础复习笔记系列 七 IO操作
Java基础复习笔记系列之 IO操作 我们说的出入,都是站在程序的角度来说的.FileInputStream是读入数据.?????? 1.流是什么东西? 这章的理解的关键是:形象思维.一个管道插入了一 ...
- Web Service代理类生成工具
本文原文连接:http://www.cnblogs.com/dengxinglin/p/3334158.html 之前一篇文章写 Web Service服务代理类生成及编译 , 通过命令行的方式可以直 ...
- android 网络通讯
//get方式请求网络数据 String urlPath="http://192.168.98.112:8080/CloudMusicPlayer/fragment1_1_lv2/json/ ...
- #8.11.16总结#CSS常用样式总结(二)
border 边框 简写:border:1px solid #000; 等效于:border-width:1px;border-style:solid;border-color:#000; 顺序:b ...
- Tomcat部署记事
1.导入证书到jdk里 keytool -import -alias 证书名称 -file 证书地址 -keystore 导入位置 例:keytool -import -alias co3 -file ...
- linux命令学习使用记录
1.文件批量重命名:把所有.xml文件重命名.txt,第一个参数为文件名中字符串,第二个参数为替换后文件名,第三个为当前目录文件列表 rename .xml .txt *.xml 2.解压不显示过程: ...
- Android--ListView下拉刷新
整理了下以前写的小项目,ListView的下拉刷新,虽然小但还是想纪念下..适合新手看,大神略过... 效果图: 代码: 实体类 package com.example.listviewre ...
- Quartz2D复习(二) --- 手势解锁
这次支付宝手机客户端升级,把手势解锁那个功能去掉了,引起很多人的抱怨,觉得少了手势解锁的保护,个人信息容易泄漏了... 那么手势解锁功能是怎么是实现的呢,这里使用Quart2D来简单模拟一下, 先看下 ...
- 使用imeOptions
Android的软键盘右下角有Action按钮,如下图的“上一步” 在EditText中有 android:imeOptions选项,它包括完成按钮“actionDone”,发送按钮“actionSe ...