Qt實現(xiàn)簡單五子棋小游戲
C++代碼簡單實現(xiàn)五子棋功能,主要是分為窗口繪圖的顯示,橫、縱、斜三個方面計算的功能代碼實現(xiàn),即能連續(xù)出現(xiàn)5個相同棋子就為贏。在這里就簡單講解一下這三個方面的功能實現(xiàn)(主要是通過QT實現(xiàn))。
下圖為游戲主窗口頁面:

第一步:窗口繪圖的實現(xiàn)(QPaintEvent繪圖事件 和 QMouseEvent鼠標事件)
①鼠標事件(這里我的是mouseDoubleClickEvent()雙擊事件)
void GamePage::mouseDoubleClickEvent(QMouseEvent *event)//鼠標雙擊事件
{
m_dx = event->x();
m_dy = event->y();
//避免亂點時存入坐標 需添加:標志符--》game狀態(tài) 坐標的界限(點)
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容器來實現(xiàn),并對容器進行迭代循環(huán)繪圖,實現(xiàn)代碼如下:
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();
}
第二步:輸贏的計算

上圖列出了計算的關系規(guī)律,下面就用代碼分別實現(xiàn)三個不同方向的計算:
①橫向
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;
}
以上橫、縱、斜三個方向的運算都是通過最簡單的算法是實現(xiàn),易于理解。
④定時器實現(xiàn)紅黑旗的定時檢查功能
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;
//橫向連續(xù)5個點
if((*itertmp).y() - tmp.y() >= -0.000001 && (*itertmp).y() - tmp.y() <= 0.000001)//先判斷y是同一坐標
{
m_pHerVector.append(*itertmp);
}
//縱向連續(xù)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;
//橫向連續(xù)5個點
if((*itertmp).y() - tmp.y() >= -0.000001 && (*itertmp).y() - tmp.y() <= 0.000001)//先判斷y是同一坐標
{
m_pHerVectorB.append(*itertmp);
}
//縱向連續(xù)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;
}
}
}
}
以上就是實現(xiàn)簡單的五子棋功能,初步實現(xiàn)一些簡單的計算功能,能正常運行小游戲,沒花太多時間進行檢查,可能會存在一些bug,還請見諒 ,希望對初學者有所幫助。
更多有趣的經典小游戲實現(xiàn)專題,分享給大家:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
VisualStudio2022 cmake配置opencv開發(fā)環(huán)境
本文主要介紹了VisualStudio2022 cmake配置opencv開發(fā)環(huán)境,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-08-08
C++編程異常處理中try和throw以及catch語句的用法
這篇文章主要介紹了C++編程異常處理中try和throw以及catch語句的用法,包括對Catch塊的計算方式的介紹,需要的朋友可以參考下2016-01-01

