当前位置: 首页 > article >正文

C++ Easyx 三子棋

目录

思路

框架​编辑

读取操作

数据操作

绘制画面

游戏的数据结构

用二维数组来模拟棋盘格

        赢的情况

        平局情况

Code

代码细节部分

  (1)初始化棋盘格

  (2) 初始化棋子类型​编辑

        事件处理部分 

             落子

框架内代码的完善

      数据处理框架代码的完善

检查是否赢了 (函数)

         绘制图形框架代码的完善

绘制棋盘网格(函数)

绘制棋子 (函数)

绘制提示信息  (函数)

DBUG

优化

代码托管

三子棋/Test.cpp · 孙鹏宇/孙鹏宇的第一个仓库 - 码云 - 开源中国 (gitee.com)


思路

我们遵循先框架,后思路得的思路

框架

首先是读取操作:

读取操作

读取鼠标单击之后的信息

接下来是数据操作:

数据操作

我们只需要对游戏胜负进行判断

胜的清空:一条线三颗棋子都一样

平的情况:棋格全部填满还未分出胜负。

游戏结束时使用弹出告诉玩家游戏结果,然后退出主循环。

绘制画面

我们使用line函数绘制一个3*3的棋盘格:

x玩家时使用line函数对角线画两个线,轮到O玩家落子时时使用cirlce函数画个圆:

游戏的数据结构

用二维数组来模拟棋盘格

棋盘内容为字符串,初始化为‘-'

赢的情况

我们对赢得情况进行穷举:

一共有八种

平局情况

如果9个网格均被棋子填满却没有获胜的一方,那么就是平局:

Code

写代码同样遵循先框架再细节的原则:

我们先把上面的内容转化为代码:

框架:

#include<easyx.h>
#include<iostream>
using namespace std;
	//处理数据
	// 
	//检测玩家是否赢了
bool Checkwin(char str)
{

}
//检测是否平局
bool Checkavg(char str)
{

}

//绘制图像
// 
//绘制棋盘格
void DrawBorad()
{

}
//绘制棋子
void DrawPiece()
{

}
//绘制提示信息
void DrawTipText()
{

}
int main()
{
	initgraph(60,600);//绘制窗口

	bool flag = true;//退出主循环的标识列
	ExMessage msg;//存储消息
	BeginBatchDraw();//渲染缓冲区
	//主循环
	while (flag)
	{
		//读取操作
		while(peekmessage(&msg))//读取消息
		{
			//读取到的细节如何处理稍息再说
		}
		cleardevice();//清屏
       
        //重新绘图
		DrawBorad();
		DrawPiece();
		DrawTipText();

		FlushBatchDraw();//刷新缓冲区
	}
	EndBatchDraw();//刷新缓冲区
	return 0;
}

代码细节部分

如果 x玩家赢了,我们可以用MessageBox()函数弹出了一个框,显示x玩家赢了:

代码如下:

 同样的,O玩家获胜的情况和平局的情况也写一下:

设置两个全局

(1)初始化棋盘格

(2) 初始化棋子类型

事件处理部分 

用msg来表示鼠标的位置:

 怎么把鼠标的位置映射到数组下标呢?我们绘制的棋盘格的大小为600x600,分成三等份之后每个格子的大小为200x200:

 所以我们求横坐标可以有这样一个公式:代码为:

落子

(1)首先判断是否可以落子

  (2)  落完子之后要切换下次落子的棋子类型

框架内代码的完善

把之前只写了框架没写实现的函数补全:

数据处理框架代码的完善

检查是否赢了 (函数)

按照我们之前列的八种赢的情况写:

Checkavg()函数

用两个for循环来遍历棋盘格中每一个元素,如果还有空格没有落子就返回false代表没有平局,最后如果没有返回false说明平局了,返回true:

绘制图形框架代码的完善

绘制棋盘网格(函数)

棋盘格的网格其实就是四条线:

我们可以通过图形绘制相关函数->line()函数来绘制:

首先棋盘格总大小是600x600,每个小格子是200x200:

绘制棋子 (函数)

(1)绘制棋子,首先用二维数组遍历一下棋盘,如果要落的棋子是'O',那就在棋格中间画圆。

(2)棋盘中心的求法:一个小格子长宽200x200,中心坐标为左上角坐标+100:

(3)画圆的方法:

 (4)当棋子为'x'时就按小格子对角线画两条线

 (5)对角线点求法:如下图(6)画对角线的方法:

(7)如果不是'O'或者',那就什么都不用做。

(8)代码:

绘制提示信息  (函数)

适用settextcolor()函数将提示文本设高亮:

outtextxy()函数用来在窗口指定位置输出提示信息:

 code:

DBUG

这样三子棋基本功能就做好了,但是有bug:

(1)闪退

(2)闪退过程中可以看见我们画的棋盘,发现棋盘网格线条错位:

原因:

(1)绘制棋盘时手误

 (2)闪退的原因是我们在判断是否平局用的else,没设条件,此刻没输值就直接平局了(只要不是'O',‘x’就直接平局了),这显示不是我们要的,我们想要的是棋盘满了还没赢才平局,因此我们应该引用checkagv()函数。

修改:

修改完之后运行:

发现有两个错误

(1)鼠标左键不用点击,就可以落子

(2)落子位置和我们鼠标落点位置不配置

鼠标左键不用点击,就可以落子的原因:

应该选择ExMessage的WM_LBUTTONDOWN表实列,我选成第一个了:

 落子位置不配置的原因:

(1)切换棋子类型应该包含在可以落子的前提下,如果不能落子也就没必要切换棋子类型了:

修改之后:

(2)

二维数组是按照横纵坐标系的:

 但是我们的窗口确是纵横坐标系:

 错误:

修改:

交互功能正常,但是渲染功能有问题,最后一颗棋子不会显示:

 原因:

我们把重新绘图放在判断胜负的后面,当玩家赢了的时候会执行flag=fale,此时会执行重新绘图。

当再次循环时,flag因为false,所以退出循环。也就是绘图只执行了一次,一闪而过。

 我们把绘图放到胜负判断之前,这样即便是不再进入循环,也是最后一次落子的下一次绘图不会显示,而最后一次落子的绘图会显示在当前窗口。

 正常运行:

优化

当我们的程序跑起来之后,查看任务管理器,发现我们的程序消耗内存空间特别大;

这是因为计算机在执行while循环时特别快,我们编写的主循环在顷刻间已经被执行了成千上万次。

因此,为了不避免的销毁,我们可以使用sleep()函数使循环休眠几毫秒。

我们可以在主循环开头写一个开始数获取GetTickCount(),主循环结束位置写一个结尾数获取GetTickCount()函数。 

通过计算 二者落差  可以得到  该主循环实际运行所需要的 毫秒数,简称实需数。

如果我们想在60帧率下刷新,那么就让1000/60=16,16为我们的期望值。

如果   实需数  < 期望值,说明不用休眠。

否则,实需数-期望值= 休眠数。

代码托管

三子棋/Test.cpp · 孙鹏宇/孙鹏宇的第一个仓库 - 码云 - 开源中国 (gitee.com)


http://www.kler.cn/a/155727.html

相关文章:

  • javascript 函数【知识点整理】
  • 腾讯云nginx SSL证书配置
  • 使用electron-egg把vue项目在linux Ubuntu环境下打包并安装运行
  • 【MATLAB代码】二维平面上的TDOA,使用加权最小二乘法,不限制锚点数量,代码可复制粘贴
  • 「IDE」集成开发环境专栏目录大纲
  • AI 大模型如何赋能电商行业,引领变革
  • PostgreSQL中常用的几种连接池总结及更新
  • 阻止事件默认行为
  • MySQL之存储引擎
  • Java开发实战(一):Java环境安装
  • MapperStruct的高级用法
  • 阿里微服务质量保障系列:性能监控最佳实践
  • 命令模式-C++实现
  • 超硬核解析Mybatis动态代理原理!只有接口没实现也能跑?
  • Python WebSocket 客户端教程
  • maven如何用命令看配置文件位置
  • 如何绕过某讯手游保护系统并从内存中获取Unity3D引擎的Dll文件
  • Debian12配置ssh服务器
  • 用Java写一个王者荣耀游戏
  • Django rest froamwork-序列化关系
  • python 交互模式和命令行模式的问题
  • 【C++】类和对象——explicit关键字,友元和内部类
  • Linux(12):磁盘配额(Quota)与进阶文件系统管理
  • Linux系统中进程间通信(Inter-Process Communication, IPC)
  • 医院电子病历编辑器源码(支持云端SaaS服务)
  • 关于清空ant.design 中表单内容的方法