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,还请见谅 ,希望对初学者有所帮助。
更多有趣的经典小游戏实现专题,分享给大家:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
VisualStudio2022 cmake配置opencv开发环境
本文主要介绍了VisualStudio2022 cmake配置opencv开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2023-08-08C++编程异常处理中try和throw以及catch语句的用法
这篇文章主要介绍了C++编程异常处理中try和throw以及catch语句的用法,包括对Catch块的计算方式的介绍,需要的朋友可以参考下2016-01-01
最新评论