最近有点无聊正好想玩五子棋,那就实现一下这个游戏吧,网上的五子棋逻辑又长又复杂,我这个逻辑还是蛮简单的,展示如下
这是一个简单的五子棋,今天就了解一下这个游戏的思路,使用的是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; // 整型变量 x
int y; // 整型变量 y
int yanse = 0; // 整型变量 yanse,初始化为 0
bool zhuangtai = false; // 布尔变量 zhuangtai,初始化为 false
QBrush huashua; // QBrush 对象 huashua
void shua(int a); // 成员函数声明 shua
void 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 对象 dian
quan.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遍历所有的点和这个点的左右上下连续四个是否的yanse属性一样就可以了,大家可以自己试一下,那五子棋到这里就写完了