Qt实现简单五子棋小游戏

 更新时间:2020年08月28日 13:35:34   作者:Tjmies  
这篇文章主要为大家详细介绍了Qt实现简单五子棋小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

C++代码简单实现五子棋功能,主要是分为窗口绘图的显示,横、纵、斜三个方面计算的功能代码实现,即能连续出现5个相同棋子就为赢。在这里就简单讲解一下这三个方面的功能实现(主要是通过QT实现)。

下图为游戏主窗口页面:

第一步:窗口绘图的实现(QPaintEvent绘图事件 和 QMouseEvent鼠标事件)

①鼠标事件(这里我的是mouseDoubleClickEvent()双击事件)

void GamePage::mouseDoubleClickEvent(QMouseEvent *event)//鼠标双击事件
{
 m_dx = event->x();
 m_dy = event->y();
 //避免乱点时存入坐标 需添加:标志符--》game状态 坐标的界限(点)
 if(m_dx < POINT_X_MAX && m_dy < POINT_Y_MAX && m_bRunState == true)
 {
 //如果点在交叉点周围则设置点在交叉点上(判断点位置)
 QPointF newPoint(gainPointPosition(QPointF(m_dx,m_dy)));
 
 if(!m_VectorRedPoint.contains(newPoint) &&    
  !m_VectorBlackPoint.contains(newPoint))//判断点是否已经存在
 {
  if(m_iFlagWho == 0)//红棋
  {
  m_VectorRedPoint.append(newPoint);
  m_iFlagWho = 1;
  }
  else//黑棋
  {
  m_VectorBlackPoint.append(newPoint);
  m_iFlagWho = 0;
  }
 }
 
 }
}

在这里窗口网格图是通过直接绘画以及鼠标双击选择坐标来存储棋子和绘画棋子,因此对点进行了一个设置位置函数以便处于两线之间的交接处,代码如下:

QPointF GamePage::gainPointPosition(QPointF srcPoint)//返回一个处于格子两线交接处的坐标点
{
 QPointF tmp;
 for(int i = 0;i < 12;i++)
 {
 if(srcPoint.x() >= 50*i && srcPoint.x() <= (50*i+25))//X判断
 {
  tmp.setX(50*i);//如果处于50*i ~ 50*i+25)之间则设置点坐标点为50*i
 }
 else if (srcPoint.x() >= (50*i + 25) && srcPoint.x() <= 50*(i+1))
 {
  tmp.setX(50*(i+1));//如果处于50*i+25 ~ 50*(i+1)之间则设置点坐标点为50*(i+1)
 }
 if(srcPoint.y() >= 50*i && srcPoint.y() <= (50*i+25))//Y判断
 {
  tmp.setY(50*i);//同上
 }
 else if (srcPoint.y() >= (50*i + 25) && srcPoint.y() <= 50*(i+1))
 {
  tmp.setY(50*(i+1));//同上
 }
 
 }
 return tmp;
}

②绘图事件( 主要是网格图、黑棋、红棋的绘画 )

棋子坐标的存储主要是通过QVector容器来实现,并对容器进行迭代循环绘图,实现代码如下:

void GamePage::paintEvent(QPaintEvent *event)//绘画事件
{
 QPainter *pater = new QPainter(this);
 pater->begin(this);
 //网格图
 pater->setPen(Qt::black);
 for(int i = 0;i <= 12;i++)
 {
 pater->drawLine(0,50*i,600,50*i);
 pater->drawLine(50*i,0,50*i,600);
 }
 
 //红色棋绘画
 QVector<QPointF>::iterator iter;
 for(iter = m_VectorRedPoint.begin();iter != m_VectorRedPoint.end();iter++)
 {
 pater->setBrush(QBrush(Qt::red, Qt::SolidPattern));
 pater->setPen(Qt::red);
 pater->drawEllipse(*iter,15,15);
 }
 //黑色棋绘画
 QVector<QPointF>::iterator iter1;
 for(iter1 = m_VectorBlackPoint.begin();iter1 != m_VectorBlackPoint.end();iter1++)
 {
 pater->setBrush(QBrush(Qt::black, Qt::SolidPattern));
 pater->setPen(Qt::black);
 pater->drawEllipse(*iter1,15,15);
 }
 pater->end();
 update();
 
}

第二步:输赢的计算

上图列出了计算的关系规律,下面就用代码分别实现三个不同方向的计算:

①横向

bool GamePage::checkXPointF(QVector<QPointF> vector) //检查X轴方向的
{
 int num_L= 1;
 int num_R = 1;
 QVector<QPointF>::iterator iter;
 QVector<QPointF>::iterator itertmp;
 for(iter = vector.begin();iter != vector.end();iter++)
 {
  QPointF tmp = *iter;
  for(int k = 1;k < 5;k++)//左方向的查找
  {
  for(itertmp = vector.begin();itertmp != vector.end();itertmp++)
  {
 
   qDebug()<<*itertmp<<"X compare"<<tmp;
   if((*itertmp).x() - tmp.x() == k*50)
   {
   num_L ++;
   }
  }
  //qDebug()<<"count:"<<num;
  if(num_L == k+1)//寻找过程中找到几个点相连
  {
   if(num_L == 5)
   {
   return true;
   }
  }
  else
  {
   break;
  }
  }
 
  for(int k = 1;k < 5;k++)//右方向的查找
  {
  for(itertmp = vector.begin();itertmp != vector.end();itertmp++)
  {
   qDebug()<<*itertmp<<"X compare"<<tmp;
   if((*itertmp).x() - tmp.x() == -k*50)
   {
   num_R ++;
   }
  }
  //qDebug()<<"count:"<<num;
  if(num_R == k+1)//寻找过程中找到几个点相连
  {
  if(num_R == 5)
  {
   return true;
  }
  }
  else
  {
  break;
  }
 }
 if(num_R + num_L == 5+1)//5+1 因为左右方向都是从1开始计算 重复了原点tmp坐标
 {
  return true;
 }
 else
 {
  num_R = 1;
  num_L = 1;
 
 }
 }
 return false;
}

②纵向(与横向同理)

bool GamePage::checkYPointF(QVector<QPointF> vector)
{
 qDebug()<<"enter Y***************";
 int num_U = 1;
  int num_D = 1;
 QVector<QPointF>::iterator iter;
 QVector<QPointF>::iterator itertmp;
 for(iter = vector.begin();iter != vector.end();iter++)
 {
  QPointF tmp = *iter;
  for(int k = 1;k < 5;k++)//上
  {
  for(itertmp = vector.begin();itertmp != vector.end();itertmp++)
  {
 
   qDebug()<<*itertmp<<"Y compare"<<tmp;
   if((*itertmp).y() - tmp.y() == k*50)
   {
   num_U ++;
   }
  }
  qDebug()<<"num_U:"<<num_U;
  if(num_U == k+1)//寻找过程中找到几个点相连
  {
   if(num_U == 5)
   {
   return true;
   }
  }else{break;}
  }
  for(int k = 1;k < 5;k++)//下
  {
  for(itertmp = vector.begin();itertmp != vector.end();itertmp++)
  {
   qDebug()<<*itertmp<<"Y compare"<<tmp;
   if((*itertmp).y() - tmp.y() == -k*50)
   {
   num_D ++;
   }
 
  }
  qDebug()<<"num_D:"<<num_D;
  if(num_D == k+1)//寻找过程中找到几个点相连
  {
  if(num_D == 5)
  {
   return true;
  }
  }else{break;}
 }
 if(num_D + num_U == 5 + 1)//减去一个
 {
  return true;
 }
 else
 {
  num_D = 1;
  num_U= 1;
 }
 }
 
 return false;
}

③斜向(从上图可知,以坐标系为例,分为四个象限的计算和计数来判断是否达到要求)

int GamePage::findSeriesPointF(bool flag, QPointF tmp, QVector<QPointF> vector)
{
 bool flag_iter = false;
 int forward_count = 1;//一象限的数量
 int reverse_count = 1;
 int forward_count2 = 1;
 int reverse_count2 = 1;
 QVector<QPointF>::iterator iter= vector.begin();
 
 while(iter != vector.end())
 {
  qDebug()<<*iter<<"compare"<<tmp;
  switch(forward_count)//一象限
  {
  case 1:
   if((*iter).x() - tmp.x() == 50 && (*iter).y() - tmp.y() == -50)
   {
   forward_count ++;
 
   flag_iter = true;
   }
   break;
  case 2:
   if((*iter).x() - tmp.x() == 50*forward_count && (*iter).y() - tmp.y() == -50*forward_count)
   {
   forward_count++;
   flag_iter = true;
   }
   break;
  case 3:
   if((*iter).x() - tmp.x() == 50*forward_count && (*iter).y() - tmp.y() == -50*forward_count)
   {
   forward_count++;
   flag_iter = true;
   }
   break;
  case 4:
   if((*iter).x() - tmp.x() == 50*forward_count && (*iter).y() - tmp.y() == -50*forward_count)
   {
   forward_count++;
   flag_iter = true;
   }
   break;
 
  }
  switch(reverse_count)//三象限
  {
  case 1:
   if((*iter).x() - tmp.x() == -50 && (*iter).y() - tmp.y() == 50)
   {
   reverse_count=2;
   flag_iter = true;
   }
   break;
  case 2:
   if((*iter).x() - tmp.x() == -50*reverse_count && (*iter).y() - tmp.y() == 50*reverse_count)
   {
   reverse_count++;
   flag_iter = true;
   }
   break;
  case 3:
   if((*iter).x() - tmp.x() == -50*reverse_count && (*iter).y() - tmp.y() == 50*reverse_count)
   {
   reverse_count++;
   flag_iter = true;
   }
   break;
  case 4:
   if((*iter).x() - tmp.x() == -50*reverse_count && (*iter).y() - tmp.y() == 50*reverse_count)
   {
   reverse_count++;
   flag_iter = true;
   }
   break;
 
  }
  qDebug()<<forward_count<<"+"<<reverse_count;
  if(forward_count + reverse_count == 6)//未加上点本身
  {
  return 5;
 
  }
 
  switch(forward_count2)//2象限
  {
  case 1:
   if((*iter).x() - tmp.x() == -50 && (*iter).y() - tmp.y() == -50)
   {
   forward_count2++;
   flag_iter = true;
   }
   break;
  case 2:
   if((*iter).x() - tmp.x() == -50*forward_count2 && (*iter).y() - tmp.y() == -50*forward_count2)
   {
   forward_count2++;
   flag_iter = true;
   }
   break;
  case 3:
   if((*iter).x() - tmp.x() == -50*forward_count2 && (*iter).y() - tmp.y() == -50*forward_count2)
   {
   forward_count2++;
   flag_iter = true;
   }
   break;
  case 4:
   if((*iter).x() - tmp.x() == -50*forward_count2 && (*iter).y() - tmp.y() == -50*forward_count2)
   {
   forward_count2++;
   flag_iter = true;
   }
   break;
  }
 
  switch(reverse_count2)//4象限
  {
  case 1:
   if((*iter).x() - tmp.x() == 50 && (*iter).y() - tmp.y() == 50)
   {
   reverse_count2++;
   flag_iter = true;
   }
   break;
  case 2:
   if((*iter).x() - tmp.x() == 50*reverse_count2 && (*iter).y() - tmp.y() == 50*reverse_count2)
   {
   reverse_count2++;
   flag_iter = true;
   }
   break;
  case 3:
   if((*iter).x() - tmp.x() == 50*reverse_count2 && (*iter).y() - tmp.y() == 50*reverse_count2)
   {
   reverse_count2++;
   flag_iter = true;
   }
   break;
  case 4:
   if((*iter).x() - tmp.x() == 50*reverse_count2 && (*iter).y() - tmp.y() == 50*reverse_count2)
   {
   reverse_count2++;
   flag_iter = true;
   }
   break;
  }
  qDebug()<<forward_count2<<"+"<<reverse_count2;
  if(forward_count2 + reverse_count2 == 6)//未加上点本身
  {
  return 5;
  }
  if(flag_iter)
  {
   iter = vector.begin();//目的是返回首个点,重头存货在后 不错过
   flag_iter = false;
  }
  else {
   iter++;
  }
 
 }
 
 return 0;
}

以上横、纵、斜三个方向的运算都是通过最简单的算法是实现,易于理解。

④定时器实现红黑旗的定时检查功能

void GamePage::slotCheckWhetherWin()//定时器检查是否输赢功能
{
 m_pVerVector.clear();
 m_pVerVectorB.clear();
 m_pHerVector.clear();
 m_pHerVectorB.clear();
 
 QVector<QPointF>::iterator iterRed;
 for(iterRed = m_VectorRedPoint.begin();iterRed != m_VectorRedPoint.end();iterRed++)
 {
  qDebug()<<*iterRed;
 }
 QVector<QPointF> tmpRed = m_VectorRedPoint;
 //红棋判断
 if(m_VectorRedPoint.size() >= 5)
 {
  for(iterRed = m_VectorRedPoint.begin();iterRed != m_VectorRedPoint.end();iterRed++)
  {
 
  QPointF tmp = *iterRed;//获取第一个点
  qDebug()<<"tmp:"<<tmp;
  QVector<QPointF>::iterator itertmp;
  for(itertmp = tmpRed.begin();itertmp != tmpRed.end();itertmp++)
  {
   qDebug()<<"tmpRed:"<<*itertmp;
   //横向连续5个点
   if((*itertmp).y() - tmp.y() >= -0.000001 && (*itertmp).y() - tmp.y() <= 0.000001)//先判断y是同一坐标
   {
    m_pHerVector.append(*itertmp);
   }
   //纵向连续5个点
   if((*itertmp).x() - tmp.x() >= -0.000001 && (*itertmp).x() - tmp.x() <= 0.000001)//先判断y是同一坐标
   {
    m_pVerVector.append(*itertmp);
   }
 
  }
  //对容器进行操作
  if(checkXPointF(m_pHerVector) || checkYPointF(m_pVerVector))
  {
   QMessageBox::warning(nullptr,"warning","红方XY赢了!");
   m_ptimer->stop();
   return;
  }
  else
  {
   m_pHerVector.clear();//清空
   m_pVerVector.clear();//清空
   count = 0;
  }
 
  //其他都是斜向
  if(findSeriesPointF(true,tmp,m_VectorRedPoint) == 5)
  {
   QMessageBox::warning(nullptr,"warning","红方斜线赢了!");
   m_ptimer->stop();
   return;
  }
  }
 
 }
 //黑棋判断
 QVector<QPointF>::iterator iterBlack;
 QVector<QPointF> tmpBlack = m_VectorBlackPoint;
 if(m_VectorBlackPoint.size() >= 5)
 {
  for(iterBlack = m_VectorBlackPoint.begin();iterBlack != m_VectorBlackPoint.end();iterBlack++)
  {
 
  QPointF tmp = *iterBlack;//获取第一个点
  qDebug()<<"tmp:"<<tmp;
  QVector<QPointF>::iterator itertmp;
  for(itertmp = tmpBlack.begin();itertmp != tmpBlack.end();itertmp++)//正向
  {
   qDebug()<<"tmpRed:"<<*itertmp;
   //横向连续5个点
   if((*itertmp).y() - tmp.y() >= -0.000001 && (*itertmp).y() - tmp.y() <= 0.000001)//先判断y是同一坐标
   {
    m_pHerVectorB.append(*itertmp);
   }
   //纵向连续5个点
   if((*itertmp).x() - tmp.x() >= -0.000001 && (*itertmp).x() - tmp.x() <= 0.000001)//先判断y是同一坐标
   {
    m_pVerVectorB.append(*itertmp);
   }
 
  }
  //对容器进行操作
  if(checkXPointF(m_pHerVectorB) || checkYPointF(m_pVerVectorB))
  {
   QMessageBox::warning(nullptr,"warning","黑方XY赢了!");
   m_ptimer->stop();
   return;
  }
  else
  {
   m_pHerVectorB.clear();//清空
   m_pVerVectorB.clear();//清空
   count = 0;
  }
 
  //其他都是斜向
  if(findSeriesPointF(true,tmp,m_VectorBlackPoint) == 5)
  {
   QMessageBox::warning(nullptr,"warning","黑方斜线赢了!");
   m_ptimer->stop();
   return;
  }
  }
 
 }
 
}

以上就是实现简单的五子棋功能,初步实现一些简单的计算功能,能正常运行小游戏,没花太多时间进行检查,可能会存在一些bug,还请见谅 ,希望对初学者有所帮助。

更多有趣的经典小游戏实现专题,分享给大家:

C++经典小游戏汇总

python经典小游戏汇总

python俄罗斯方块游戏集合

JavaScript经典游戏 玩不停

javascript经典小游戏汇总

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • C语言实现扫雷游戏(可以自动展开)

    C语言实现扫雷游戏(可以自动展开)

    这篇文章主要为大家详细介绍了C语言实现扫雷游戏,可以自动展开,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • C语言实现简单的扫雷功能

    C语言实现简单的扫雷功能

    这篇文章主要为大家详细介绍了C语言实现简单的扫雷功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • C++对象的浅复制和深复制详解及简单实例

    C++对象的浅复制和深复制详解及简单实例

    这篇文章主要介绍了C++对象的浅复制和深复制详解及简单实例的相关资料,需要的朋友可以参考下
    2017-04-04
  • C/C++实现快速排序算法的两种方式实例

    C/C++实现快速排序算法的两种方式实例

    快速排序是一种采用分治思想,在实践中通常运行较快一种排序算法,这篇文章主要给大家介绍了关于C/C++实现快速排序的两种方式的相关资料,文中给出了详细的示例代码,需要的朋友可以参考下
    2021-08-08
  • VisualStudio2022 cmake配置opencv开发环境

    VisualStudio2022 cmake配置opencv开发环境

    本文主要介绍了VisualStudio2022 cmake配置opencv开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • C++编程异常处理中try和throw以及catch语句的用法

    C++编程异常处理中try和throw以及catch语句的用法

    这篇文章主要介绍了C++编程异常处理中try和throw以及catch语句的用法,包括对Catch块的计算方式的介绍,需要的朋友可以参考下
    2016-01-01
  • 基于Qt OpenCV的图像灰度化像素操作详解

    基于Qt OpenCV的图像灰度化像素操作详解

    这篇文章主要为大家详细介绍了基于Qt+OpenCV的图像灰度化像素操作:最大值法、平均法、加权平均值法,感兴趣的小伙伴可以了解一下
    2022-07-07
  • Qt实现Slider滑块条组件的示例代码

    Qt实现Slider滑块条组件的示例代码

    在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,本文主要介绍了Qt实现Slider滑块条组件的示例代码,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • C++中的memset用法详解

    C++中的memset用法详解

    memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值,本文给大家介绍C++中的memset用法,感兴趣的朋友跟随小编一起看看吧
    2023-02-02
  • C/C++ 多线程的学习心得总结

    C/C++ 多线程的学习心得总结

    本篇文章是对C/C++中多线程的学习心得总结进行了详细的分析介绍,需要的朋友参考下
    2013-05-05

最新评论