在学C++的时候,在做课设的时候做了一个五子棋课设,当时选这个就是觉得挺好玩的,想理解一下游戏是如何实现的,然后就入了这个坑。。。这个我没有用图形化界面来实现,完全是使用字符来实现。然后没有用图形化界面的最大的问题就是闪屏。。。因为普通的实现一般是用到清屏命令,每一次刷新输出的时候都要时间,所以就会闪屏,看着这一闪一闪的屏幕的确令有强迫症的我看到很难受,然后在全部完成五子棋的功能之后,整整花了两三天时间去解决这个问题,最终还是解决了。废话不说那么多了,直接开始吧哈哈哈。
功能介绍:
(1)开始界面和界面大小的控制 在游戏运行开始的时候,出现一个初始化界面 ,介绍游戏名字、游戏背景选择项(使用system()来改变控制台背景颜色使用sleep()来实现提示语的先后出现 ,使用字符串格式化函数sprintf()函数来实现控制台窗口的大小)。
(2)下棋操作 先通过键盘的上下左右箭头和WASD的字母移动下棋的位置,按空格键进行下棋(通过getch()函数和switch语句实现)。
(3)人人对战功能 玩家可通过先后移动棋子坐标位置进行下棋,进而进行对战游戏,直至分出胜负,在游戏期间玩家可以按n来实现重新游戏,按Esc退出游戏,可以在下方看到该是哪方下棋(通过坐标值的变化然后重新打印一张棋盘来实现光标的移动,这里用到双缓存技术来解决system(“cls”)的闪屏问题)。
(4)判断玩家棋子位置的合法性 玩家通过键盘的输入确定棋子位置坐标,系统会人性化判断棋子的合法性。若棋子不合法,玩家需要再次输入棋子位置坐标;若棋子合法,未决出胜负则继续由另一玩家确定棋子坐标。
(5)胜负判断功能 游戏系统程序可以通过棋盘上棋子的横竖方向上相同玩家棋子数量的关系,判断玩家棋子是否已构成五连子。若棋子构成五连子,则可选择结束游戏或者再来一局;若棋子没有构成五连子,则玩家可继续游戏。
(6)结束界面 玩家分出胜负之后,出现结束界面,玩家可选择是否继续玩游戏。若玩家选择继续游戏,则会重新出现空白棋盘;若玩家选择不继续游戏,则退出游戏界面。
(7)悔棋功能实现(新增) 实现原理:利用栈,在下子即按下空格键的时候,将当前子的坐标压栈,我是用了两个栈,一个记录X坐标,一个记录Y坐标,即X.push(g_cursorX); Y.push(g_cursorY);然后在按下悔棋键的时候,将栈顶的X,Y坐标赋值为0,即g_checkboard[X.top()][Y.top()] = 0;代码修改见文末。

五子棋基本的实现并不难,认真的看代码的话都会看的懂,最难的部分就是解决闪屏问题那部分,我当时也尝试了很多弯路,想要真正了解原理对于初学者并不容易,因为涉及到了比较底层的东西,但是不了解并影响实现,基本就是一个模板,要改的地方就是输出内容那个地方。我当初就是经过不断找资料不断地尝试,然后就意外的成功了,也算是比较幸运吧。所以叫我解释如何实现双缓冲我也说不清楚,上一个我参考过得链接吧。希望对你们有帮助。
https://blog.csdn.net/z1832729975/article/details/88382018
完整代码(很多地方都有注释了,所以我也不多说了直接看代码吧,有不懂或者改进的建议欢迎留言):
tips:这个程序在旧版控制台输出更加完美,因为旧版和新版的符号格式有些不一样,一般没有设置过的话都是新版的控制台,要设置的话也很简单,在输出框上方白色的地方右键,然后选择属性,选项的界面中勾上使用旧版控制台退出重新运行即可。

#include<iostream>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
using namespace std;#define CHECKBROADSIZE 25           //棋盘的大小class Rungame  //运行游戏:绘制棋盘
{private:int g_checkboard[CHECKBROADSIZE][CHECKBROADSIZE];char data[CHECKBROADSIZE][CHECKBROADSIZE];         //存放并输出棋盘组成符号int g_currentGamer;                                    //当前玩家下子  1代表黑子,2代表白子int g_cursorX, g_cursorY;                            // 坐标
public:void DrawCheckborad();   //绘制棋盘void Init();          //棋盘初始化int RunGame();int Put();              //下子成功  成功返回1 失败返回0int Check();            //判断输赢void buffer();            //双缓冲实现函数void blackground();     //背景颜色设置函数};int zhixiang_hOutput = 0;  //通过指针轮流指向两个缓冲区,实现双缓冲
HANDLE hOutput, hOutBuf;   //控制台屏幕缓冲区句柄
HANDLE *houtpoint;
COORD coord = { 6, 6 };    //起始点坐标
DWORD bytes = 0;          //DWORD 双字即为4个字节,每个字节是8位,共32位,变量类型的内存占位int main()
{Rungame chessboard;Rungame rungame;chessboard.blackground();   chessboard.buffer();SetConsoleTitle(L"五子棋人人对战");          //设置控制台的标题//进入游戏while (1){rungame.RunGame();                                //运行函数}return 0;
}
void Rungame::blackground()      //设置背景函数
{//设置控制台窗口大小char stCmd[32];sprintf(stCmd, "mode con cols=%d lines=%d", CHECKBROADSIZE + 35, CHECKBROADSIZE + 8);  //格式化控制台窗口函数system(stCmd);puts("*****************welcome to wuziqi world!******************");Sleep(500);puts("please press  'w' to turn to the white background...");Sleep(400);puts("please press  'g' to turn to the green background...");Sleep(300);puts("please press  'b' to turn to the blue background...");Sleep(200);puts("please press  'r' to turn to the red background...");Sleep(100);puts("please press  any other key to turn to the black background.");//控制控制台的颜色输出 如8代表输出灰色背景 0为黑色字体switch (getch()){case'g':system("color 0A");break;case'w':system("color F0");break;case 'b':system("color F9");break;case 'r':system("color 0C");break;}
}
int Rungame::RunGame()
{Init();                //初始化int nInput = 0;       //键盘输入值int nWinner = 0;    //赢家while (1){//system("cls");                //清屏   导致头上冒出一串东西DrawCheckborad();              //绘制棋盘switch (getch()){case 32: //space{// 1.下子 能不能下if (Put()){//2.判断输赢,每下一颗子判断一次输赢nWinner = Check();//更换玩家g_currentGamer = 3 - g_currentGamer;if (nWinner == 1){if (MessageBox(NULL, L"'■'方胜,是否再来一局?", L"五子棋", MB_YESNO) == IDYES){RunGame();      //重新开始游戏}}else if (nWinner == 2){if (MessageBox(NULL, L"'○'方胜,是否再来一局?", L"五子棋", MB_YESNO) == IDYES)RunGame();      //重新开始游戏}}}break;case 75: //向左case 'a':g_cursorY--;if (g_cursorY < 0)     //到最左的时候从最右边出来g_cursorY = 0;break;case 77: //向右case'd':g_cursorY++;if (g_cursorY > CHECKBROADSIZE - 1)g_cursorY = CHECKBROADSIZE - 1;break;case 72://向上case 'w':g_cursorX--;if (g_cursorX < 0)g_cursorX = 0;break;case 80://向下case 's':g_cursorX++;if (g_cursorX > CHECKBROADSIZE - 1)g_cursorX = CHECKBROADSIZE - 1;break;case 'n':                    //按下n重新游戏//提示信息if (MessageBox(NULL, L"是否要重新开始游戏?", L"五子棋", MB_YESNO) == IDYES){RunGame();}break;case 27://提示信息if (MessageBox(NULL, L"是否要退出游戏?", L"五子棋", MB_YESNO) == IDYES){exit(0);}break;}}return 0;
}
void Rungame::Init()
{Rungame rungame;memset(g_checkboard, 0, sizeof(g_checkboard));     //清空二维数组为0在下子的时候判断g_currentGamer = 1;   //黑子先下g_cursorX = CHECKBROADSIZE / 2;                     //开始游戏的时候光标在棋盘中间g_cursorY = CHECKBROADSIZE / 2;
}
void Rungame::DrawCheckborad()
{//输出提示信息char Tips[] = "please press 'w' 'a' 's' 'd' to control the cursor... ";char Tips1[] = "press 'n' to have a new game... ";char Tips2[] = "press 'space' to play the chess... ";char Tips3[] = "press 'Esc' to exit the game... ";char score[50];for (int i = 0; i < CHECKBROADSIZE; i++)   //控制行{for (int j = 0; j < CHECKBROADSIZE; j++) //控制列{if (i == g_cursorX && j == g_cursorY)data[i][j] = 206;   //╬else if (g_checkboard[i][j] == 1)   //黑子data[i][j] = 219;    //■else if (g_checkboard[i][j] == 2)   //白子data[i][j] = 9;      //○else if (i == 0 && j == 0)data[i][j] = 218;   //┏else if (i == 0 && j == CHECKBROADSIZE - 1)data[i][j] = 191;    //┓else if (i == CHECKBROADSIZE - 1 && j == 0)data[i][j] = 192;    //┗else if (i == CHECKBROADSIZE - 1 && j == CHECKBROADSIZE - 1)data[i][j] = 217;   //┛else if (i == 0)data[i][j] = 194; //┳else if (i == CHECKBROADSIZE - 1)data[i][j] = 193;    //┻else if (j == 0)data[i][j] = 195; //┣else if (j == CHECKBROADSIZE - 1)data[i][j] = 180;    //┫elsedata[i][j] = 197;   //┼}cout << endl;}//实现双缓冲//用指针实现指向两个缓冲区zhixiang_hOutput = !zhixiang_hOutput;if (!zhixiang_hOutput){houtpoint = &hOutput;}else{houtpoint = &hOutBuf;}coord.Y = 1;  //起始坐标//以下分别输出三个TipsWriteConsoleOutputCharacterA(*houtpoint, Tips, strlen(Tips), coord, &bytes);    // 在指定位置处插入指定数量的字符coord.Y++;              //换一行显示WriteConsoleOutputCharacterA(*houtpoint, Tips2, strlen(Tips2), coord, &bytes);coord.Y++;WriteConsoleOutputCharacterA(*houtpoint, Tips1, strlen(Tips1), coord, &bytes);coord.Y++;WriteConsoleOutputCharacterA(*houtpoint, Tips3, strlen(Tips3), coord, &bytes);coord.Y++;for (int i = 0; i < CHECKBROADSIZE; i++)          //绘制棋盘的符号{coord.Y++;WriteConsoleOutputCharacterA(*houtpoint, (char *)data[i], CHECKBROADSIZE, coord, &bytes);}//显示到哪方下sprintf(score, "current player: %d  Tips: 1:square  2:circle ", g_currentGamer);coord.Y++;     //换一行WriteConsoleOutputCharacterA(*houtpoint, score, strlen(score), coord, &bytes);SetConsoleActiveScreenBuffer(*houtpoint);
}
void Rungame::buffer()
{//实现双缓冲SetConsoleOutputCP(437);//改成标准英文输出标志,这样可以使用扩展ASCII码表,以方便后面游戏界面的制作//创建新的控制台缓冲区hOutBuf = CreateConsoleScreenBuffer(GENERIC_WRITE,  //定义进程可以往缓冲区写数据FILE_SHARE_WRITE, //定义缓冲区可共享写权限NULL,CONSOLE_TEXTMODE_BUFFER,NULL);hOutput = CreateConsoleScreenBuffer(GENERIC_WRITE,        //定义进程可以往缓冲区写数据FILE_SHARE_WRITE,     //定义缓冲区可共享写权限NULL,CONSOLE_TEXTMODE_BUFFER,NULL);//隐藏两个缓冲区的光标CONSOLE_CURSOR_INFO cci;cci.bVisible = 0;cci.dwSize = 1;SetConsoleCursorInfo(hOutput, &cci);SetConsoleCursorInfo(hOutBuf, &cci);}
int Rungame::Put()              //下子成功  成功返回1 失败返回0
{//怎么才是下子成功,而且改下那颗子if (g_checkboard[g_cursorX][g_cursorY] == 0)    //下子成功{g_checkboard[g_cursorX][g_cursorY] = g_currentGamer;return 1;}elsereturn 0;
}
int Rungame::Check()       //判断输赢
{int nHorizontal = 1;   //水平方向int nVertical = 1;     //垂直方向int nPostiveDirection = 1;  //正斜向int nSkewDirection = 1;   //反斜向//水平  向右检查//g_cursorY 列的变化for (int i = 1; i < 5; i++){if (g_cursorY + i < CHECKBROADSIZE && g_checkboard[g_cursorX][g_cursorY + i] == g_currentGamer)nHorizontal++;elsebreak;}//水平 向左检查for (int i = 1; i < 5; i++){if (g_cursorY - i > 0 && g_checkboard[g_cursorX][g_cursorY - i] == g_currentGamer)nHorizontal++;elsebreak;}if (nHorizontal >= 5){g_cursorY += 1;return g_currentGamer;}//垂直 向下检查for (int i = 1; i < 5; i++){if (g_cursorX + i < CHECKBROADSIZE && g_checkboard[g_cursorX + i][g_cursorY] == g_currentGamer)nVertical++;elsebreak;}// 向上检查for (int i = 1; i < 5; i++){if (g_cursorX - i > 0 && g_checkboard[g_cursorX - i][g_cursorY] == g_currentGamer)nVertical++;elsebreak;}if (nVertical >= 5){g_cursorY += 1;return g_currentGamer;}//正斜向上 检查for (int i = 1; i < 5; i++){if (g_cursorX - i > 0 && g_cursorY + i < CHECKBROADSIZE && g_checkboard[g_cursorX - i][g_cursorY + i] == g_currentGamer)nPostiveDirection++;elsebreak;}//正斜向下检查for (int i = 1; i < 5; i++){if (g_cursorY - i > 0 && g_cursorX + i < CHECKBROADSIZE && g_checkboard[g_cursorX + i][g_cursorY - i] == g_currentGamer)nPostiveDirection++;elsebreak;}if (nPostiveDirection >= 5){g_cursorY += 1;return g_currentGamer;}//反斜向下检查for (int i = 1; i < 5; i++){if (g_cursorX + i < CHECKBROADSIZE && g_cursorY + i < CHECKBROADSIZE && g_checkboard[g_cursorX + i][g_cursorY + i] == g_currentGamer)nSkewDirection++;elsebreak;}//正斜向上检查for (int i = 1; i < 5; i++){if (g_cursorX - i > 0 && g_cursorY - i > 0 && g_checkboard[g_cursorX - i][g_cursorY - i] == g_currentGamer)nSkewDirection++;elsebreak;}if (nSkewDirection >= 5){g_cursorY += 1;return g_currentGamer;}return 0;
}

效果图(放不了视频,就放张图片吧):

悔棋功能实现要修改的地方(在源代码中修改这些地方即可)
1.加入头文件 #include<stack>
2.在Rungame类中添加记录X,Y坐标的栈变量和记录悔棋次数的变量

3.修改RunGame()函数
a.每一次下棋都将坐标压栈,下一次棋就重置Retract_Time,这样在每一次下棋之后都可以悔一次棋

b.添加悔棋键(r)
也可以自己定义悔棋次数,修改if中的Retract_Time的限制条件即可

效果:


五子棋-完美解决闪屏问题版-新增悔棋功能(C++实现)相关推荐

  1. win7下玩完美玩魔兽争霸3的方法,完美解决闪屏和黑边

    在Win7下玩魔兽是没法全屏的,有时候修改分辨率还会闪屏,用以下方法可解决. 比如你电脑里魔兽默认的是1024×768 32位,你在游戏中先选好,然后退出魔兽. 进入注册表,运行里输入regedit, ...

  2. C# CreateParams的使用(解决闪屏问题)

    C# CreateParams的使用(解决闪屏问题) 参考文章: (1)C# CreateParams的使用(解决闪屏问题) (2)https://www.cnblogs.com/Alex1994/p ...

  3. QT for android 比较完美解决 全屏问题

    QT for android 比较完美解决 全屏问题 参考文章: (1)QT for android 比较完美解决 全屏问题 (2)https://www.cnblogs.com/wuyaSama/p ...

  4. vue 刷新界面 解决闪屏和iOS失效问题

    vue 刷新界面 解决闪屏和iOS失效问题 vue中: 1 this.$router.go(0).这种方法虽然代码很少,只有一行,但是体验很差.页面会一瞬间的白屏,体验不是很好,关键是 在ios系统中 ...

  5. GPU硬件加速相关问题,解决闪屏

    在Android开发中,部分机型可能会出现带透明度背景使用alpha动画的时候,出现闪屏问题,这个问题只要在当前的Activity配置中添加 android:hardwareAccelerated=& ...

  6. #完美解决 闪讯客户NetKeeper---Sorry,this application cannot under a Virtual Machine

    完美解决 因为win10自带虚拟软件Hyper-V关闭不完全导致的无法打开闪讯客户端Netkeeper 适用于 Windows 10 系统(非虚拟机环境)下的 Net Keeper 误报虚拟机问题: ...

  7. IOS解决闪屏无效LuanchImage启动动画设置加载广告闪屏图

    IOS闪屏动画LuanchImage设置无效 设置Launch Image无效 其它方法 转载请标明出处: https://dujinyang.blog.csdn.net/article/detail ...

  8. static 控件设置成透明后无法正常更新数据_win10又发布10月更新,提升视频体验,解决闪屏和打印问题...

    2020年10月20日,微软windows10系统再次发布了10累积更新KB4580386补丁,对1909版和1903版两版本win10系统同步推送更新补丁,本次win10的2004版未同步进行补丁更 ...

  9. android qt 对比_QT for android 比较完美解决 全屏问题

    项目用到QT qml,需要在android下面全屏显示,折腾了一晚上,搞定,分享下,希望能帮助他人. 该解决方案一定环境下不完美,比如台电P70上就只是隐藏了通知区,没有隐藏虚拟按键区 想来广大奔赴安 ...

最新文章

  1. SpringBoot中定义全局异常处理器
  2. 1066. [SCOI2007]蜥蜴【最大流】
  3. 安卓学习笔记08:常用布局 - 线性布局
  4. 建一个 5G 基站,到底要花多少钱?
  5. h5首页加载慢_Webview加载H5优化小记
  6. if else 嵌套 来源微信公众号
  7. 2022年第十二届MathorCup高校数学建模挑战赛
  8. java.net.UnknownHostException 异常处理
  9. 计算机三级要英语词汇,大学英语三级常考词汇
  10. Kotlin入门(27)文件读写操作
  11. 游戏引擎jvm频繁young gc问题分析
  12. 2022面试必刷461道大厂架构面试真题汇总+面经+简历模板
  13. 解决bmp图片文件宽度不是4的倍数
  14. JavaScript学习笔记03【基础——对象(RegExp、Global)】
  15. python怎样删除某一行_python删除某一行
  16. 计算机cpu的字母,电脑处理器后面的字母你认识几个?不认识跟我来学学(intel篇)...
  17. R语言气泡图 bubble chart
  18. C++::namespace
  19. 【什么是服务网格?】
  20. 功能点:3秒倒计时自动跳转指定页面

热门文章

  1. 应用调试(三)oops
  2. Xamarin.Forms学习之路——黑猫时钟App
  3. 生活记录:记录2020暑假前半部分时光
  4. input文本框--去首尾空格
  5. [论文笔记]Self-Attention Generative Adversarial Networks
  6. 不容错过, Paintinglite轻量级Sqlite3框架
  7. getPhoneNumber:fail Error: 用户绑定的手机需要进行验证,请在客户端完成短信验证
  8. 在线制作h5——上帝的礼物
  9. CTFhub备份文件下载
  10. 联通在线信息科技有限公司社招!