最近有点无聊正好想玩五子棋,那就实现一下这个游戏吧,网上的五子棋逻辑又长又复杂,我这个逻辑还是蛮简单的,展示如下(检测函数在最后)
这是一个简单的五子棋,今天就了解一下这个游戏的思路,使用的是QT框架,只要思路了解什么框架都能实现
1.首先画棋盘,主要是分横和竖两个方向,代码如下
void Widget::paintEvent(QPaintEvent *event) {// 创建一个 QPainter 对象,用于绘制图形,以当前的 Widget 为绘图设备QPainter huajia(this);// 使用循环绘制水平和垂直的线条,创建一个网格效果for (int a = 0; a < 21; a++) {// 绘制垂直线条,起点为 (a*40, 0),终点为 (a*40, 800)huajia.drawLine(QPoint(a * 40, 0), QPoint(a * 40, 800));// 绘制水平线条,起点为 (0, a*40),终点为 (800, a*40)huajia.drawLine(QPoint(0, a * 40), QPoint(800, a * 40));}// 调用基类的 paintEvent 函数来完成绘制操作return QWidget::paintEvent(event);
}
加上窗口的大小标题设置
Widget::Widget(QWidget *parent): QWidget(parent) // 构造函数初始化列表,传递父窗口指针, ui(new Ui::Widget) // 创建一个 Ui::Widget 对象,通常用于用户界面设计
{ui->setupUi(this); // 调用 Ui::Widget 对象的 setupUi 函数来设置用户界面// 启用鼠标跟踪,以便能够捕获鼠标移动事件this->setMouseTracking(true);// 设置窗口的固定大小为 800x800 像素this->setFixedSize(QSize(800, 800));// 设置窗口的标题为 "五子棋"this->setWindowTitle("五子棋");// 调用 chushihua() 函数来执行初始化操作chushihua();
}
使用循环进行画线,如下
2.创建棋子类,头文件为
#ifndef QIZIZHUANGTAI_H // 条件编译指令,防止头文件被重复包含
#define QIZIZHUANGTAI_H#include <QPoint> // 包含 QPoint 类的头文件
#include <QBrush> // 包含 QBrush 类的头文件class qizizhuangtai : public QPoint // 定义一个 qizizhuangtai 类,继承自 QPoint 类
{public:explicit qizizhuangtai(QPoint dian); // 构造函数声明int x; // 整型变量 xint y; // 整型变量 yint yanse = 0; // 整型变量 yanse,初始化为 0bool zhuangtai = false; // 布尔变量 zhuangtai,初始化为 falseQBrush huashua; // QBrush 对象 huashuavoid shua(int a); // 成员函数声明 shuavoid fangkai(); // 成员函数声明 fangkai
};#endif // QIZIZHUANGTAI_H // 结束条件编译指令,确保头文件完整性
cpp为
#include "qizizhuangtai.h" // 包含自定义头文件 "qizizhuangtai.h"
#include <QBrush> // 包含 QBrush 类的头文件qizizhuangtai::qizizhuangtai(QPoint dian)
{this->x = dian.x(); // 构造函数,设置类的 x 成员变量为传入 QPoint 对象的 x 坐标this->y = dian.y(); // 构造函数,设置类的 y 成员变量为传入 QPoint 对象的 y 坐标
}void qizizhuangtai::shua(int a)
{this->yanse = a; // 成员函数,设置类的 yanse 成员变量为传入的整数参数 a
}void qizizhuangtai::fangkai()
{this->zhuangtai = true; // 成员函数,将类的 zhuangtai 成员变量设置为 true
}
棋子类的属性为:棋子的x,y坐标,状态(比如一开始都为false,而绘画只绘画出为true的棋子)颜色 (区分两方不同的棋子)
3.将棋盘上所有的点都装进一个数组,就是chushihua()这个方法,具体实现如下
void Widget::chushihua()
{for (int a = 1; a < 20; a++) {for (int b = 1; b < 20; b++) {QPoint dian = QPoint(a * 40, b * 40); // 创建一个 QPoint 对象,坐标为 (a*40, b*40)qizizhuangtai qi(dian); // 创建一个 qizizhuangtai 对象 qi,传入 QPoint 对象 dianquan.append(qi); // 将 qizizhuangtai 对象 qi 添加到容器 quan 中}}
}
可能会有人问为什么要这样做,其实很简单,应为我们画棋子本事就是根据一个点,加上半径即可,半径已经选好了,那么主要是这个棋子圆形状的坐标,而鼠标事件正好可以捕获坐标,点到哪会将这个位置的棋子状态改变
4.根据鼠标选择要画哪里,如下
或者这种
这个是怎么实现的呢,其实本质是使用加减算出上下左右坐标的点,之后求出鼠标离这四个点的聚类最后求出最近的点画棋子,代码如下
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#define QIZIBANJING 15
#define LUOZIBIAOJI 6
#define GEZIDAXIAO 40
#include <QMouseEvent>
#include <QDebug>
#include "qizizhuangtai.h"
#include <QMessageBox>void Widget::mouseMoveEvent(QMouseEvent *event)
{int x = event->x() / 40; // 将鼠标事件的x坐标除以40,用于确定鼠标所在的列int y = event->y() / 40; // 将鼠标事件的y坐标除以40,用于确定鼠标所在的行int shubiaox = event->x(); // 获取鼠标事件的x坐标int shubiaoy = event->y(); // 获取鼠标事件的y坐标qDebug() << "x为:" << x << " " << "y为:" << y;QPoint zuoshang = QPoint(x * 40, y * 40); // 左上角的点坐标QPoint youshang = zuoshang + QPoint(40, 0); // 右上角的点坐标QPoint zuoxia = zuoshang + QPoint(0, 40); // 左下角的点坐标QPoint youxia = zuoshang + QPoint(40, 40); // 右下角的点坐标// 比较鼠标事件点与四个角的距离的平方int zs = (shubiaox - zuoshang.x()) * (shubiaox - zuoshang.x()) + (shubiaoy - zuoshang.y()) * (shubiaoy - zuoshang.y());int ys = (shubiaox - youshang.x()) * (shubiaox - youshang.x()) + (shubiaoy - youshang.y()) * (shubiaoy - youshang.y());int zx = (shubiaox - zuoxia.x()) * (shubiaox - zuoxia.x()) + (shubiaoy - zuoxia.y()) * (shubiaoy - zuoxia.y());int yx = (shubiaox - youxia.x()) * (shubiaox - youxia.x()) + (shubiaoy - youxia.y()) * (shubiaoy - youxia.y());qDebug() << "左上距离" << zs << " 右上距离" << ys << " 左下距离" << zx << " 右下距离" << yx;// 根据距离的平方选择一个标记点if (zs < 800) {this->biaojidianx = zuoshang.x();this->biaojidiany = zuoshang.y();}if (ys < 800) {this->biaojidianx = youshang.x();this->biaojidiany = youshang.y();}if (zx < 800) {this->biaojidianx = zuoxia.x();this->biaojidiany = zuoxia.y();}if (yx < 800) {this->biaojidianx = youxia.x();this->biaojidiany = youxia.y();}qDebug() << "标记点x:" << this->biaojidianx << "标记点y:" << this->biaojidiany;
}
这样大致就求出要画棋子的点了
5.落子,主要还是使用鼠标的松开事件,进行对状态的修改
void Widget::mouseReleaseEvent(QMouseEvent *event)
{// 遍历存储在 'quan' 向量中的棋子对象for (int a = 0; a < quan.size(); a++) {// 检查当前棋子的坐标是否与 'biaojidianx' 和 'biaojidiany' 匹配if (quan[a].x == biaojidianx && quan[a].y == biaojidiany) {// 对匹配的棋子执行 'shua' 操作,传入当前玩家的信息 'qishou'quan[a].shua(this->qishou);// 对匹配的棋子执行 'fangkai' 操作,可能是该操作的状态变化quan[a].fangkai();// 切换当前玩家。如果当前玩家是 'hei',则切换为 'bai',反之亦然if (this->qishou == hei) {this->qishou = bai;} else if (this->qishou == bai) {this->qishou = hei;}}}// 调用基类的鼠标释放事件处理函数,以确保正常的事件处理流程return QWidget::mouseReleaseEvent(event);
}
落子顺便改下颜色
落子进行绘画,代码如下
void Widget::paintEvent(QPaintEvent *event){// 创建一个画刷 'huashua',设置颜色为黑色QBrush huashua;huashua.setColor(Qt::black);huashua.setStyle(Qt::SolidPattern);// 设置绘图对象 'huajia' 的画刷为 'huashua'huajia.setBrush(huashua);// 绘制一个小矩形,可能用于标记某个点的位置huajia.drawRect(biaojidianx-6, biaojidiany-6, 12, 12);// 创建另一个画刷 'huashua2',设置颜色为红色QBrush huashua2;huashua2.setColor(Qt::red);huashua2.setStyle(Qt::SolidPattern);// 此处似乎有错误,应该设置 'huajia' 的画刷为 'huashua2' 而不是 'huashua'huajia.setBrush(huashua);// 绘制圆形for (int a = 0; a < quan.size(); a++) {if (quan[a].zhuangtai == true) {if (quan[a].yanse == 0) {// 设置画刷颜色为蓝色huashua.setColor(Qt::blue);huajia.setBrush(huashua);// 绘制蓝色圆形huajia.drawEllipse(quan[a].x-15, quan[a].y-15, 30, 30);// 调用 'jiance' 函数,可能用于检查圆形的位置jiance(quan[a].x, quan[a].y);}else {// 设置画刷颜色为红色huashua2.setColor(Qt::red);huajia.setBrush(huashua2);// 绘制红色圆形huajia.drawEllipse(quan[a].x-15, quan[a].y-15, 30, 30);// 调用 'jiance' 函数,可能用于检查圆形的位置jiance(quan[a].x, quan[a].y);}}}// 更新绘图,可能会触发重新绘制操作this->update();// 调用基类的绘图事件处理函数,以确保正常的事件处理流程return QWidget::paintEvent(event);
}
如下所示
检测逻辑如下
检测的话其实也蛮容易的,只需要双层for遍历所有的点和这个点的左右上下连续四个是否的颜色属性一样就可以了,大家可以自己试一下
这样的话就大致实现了五子棋的功能,到这里就写完了