程序界面设计部分代码如下:
基于Qt自带的窗口进行棋盘的界面设计,这里在原有的界面进行棋盘,棋子以及落点标示点的绘制,通过for循环以及自带的#include <QPainter> //画家头文件 中 painter.drawLine()函数进行横竖线的绘制,其余同理利用brush.setColor()以及painter.drawEllipse()椭圆绘制棋子部分。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setMouseTracking(true);//使用鼠标
// 设置窗口大小
setFixedSize
(MARGIN*2+BLOCK_SIZE*BOARD_GRAD_SIZE
,MARGIN*2+BLOCK_SIZE*BOARD_GRAD_SIZE);
initGame();
}
MainWindow::~MainWindow()
{
delete ui;
}
//绘制棋盘
painter.setRenderHint(QPainter::Antialiasing,true);// 抗锯齿
for (int i=0;i<BOARD_GRAD_SIZE+1;i++)
{
//从左到右第i+1 条 竖线
painter.drawLine(MARGIN+BLOCK_SIZE*i,MARGIN ,
MARGIN+BLOCK_SIZE*i,size().height()-MARGIN);
// 从上到下第i+1 条 竖线
painter.drawLine(MARGIN,MARGIN+BLOCK_SIZE*i ,
size().width()-MARGIN,MARGIN+BLOCK_SIZE*i);
}
//绘制选中点
QBrush brush;
brush.setStyle(Qt::SolidPattern);
//绘制落子标记
if(clickPosRow>0&&clickPosRow<BOARD_GRAD_SIZE&&
clickPosCol>0&&clickPosCol<BOARD_GRAD_SIZE&&
game->gameMapVec[clickPosRow][clickPosCol]==0)
{
if(game->playerFlag)
brush.setColor(Qt::black);
else
brush.setColor(Qt::white);
painter.setBrush(brush);
//落子前的标示位置
painter.drawRect(MARGIN+BLOCK_SIZE*clickPosCol-MARK_SIZE/2,MARGIN+BLOCK_SIZE*clickPosRow-MARK_SIZE/2,MARK_SIZE,MARK_SIZE);//?
}
//绘制棋子
for(int i=0;i<BOARD_GRAD_SIZE;i++)
for(int j=0;j<BOARD_GRAD_SIZE;j++)
{
if(game->gameMapVec[i][j]==1)
{
brush.setColor(Qt::black);
painter.setBrush(brush);
//落点判断有误
painter.drawEllipse(MARGIN+BLOCK_SIZE*j-CHESS_RADIUS/2,MARGIN+BLOCK_SIZE*i-CHESS_RADIUS/2,CHESS_RADIUS,CHESS_RADIUS);
}
else if(game->gameMapVec[i][j]==-1)
{
brush.setColor(Qt::white);
painter.setBrush(brush);
painter.drawEllipse(MARGIN+BLOCK_SIZE*j-CHESS_RADIUS/2,MARGIN+BLOCK_SIZE*i-CHESS_RADIUS/2,CHESS_RADIUS,CHESS_RADIUS);
}
}
4.3鼠标位置确定的实现
通过编写mouseMoveEvent()进行实现,利用int x=event->x();
int y=event->y();确定标记跟踪同时设置边缘不能落子以及四个角中优先获取左上角的点以便进行落子的确定,用if以及勾股平方和确定距离的方式确定与目标点的大概的距离来进行判断,距离最近的则标示出来供参考,并且只能显现一个标示点出来,确定完标示点后进行重置更新进行下一个点位的判断。
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
//通过鼠标确定标记跟踪
int x=event->x();
int y=event->y();
//边缘不能落子
if(x>=MARGIN+BLOCK_SIZE/2&&
x<size().width()-MARGIN-BLOCK_SIZE/2&&
y>=MARGIN+BLOCK_SIZE/2&&
y<size().height()-MARGIN-BLOCK_SIZE/2)
{
//获取最近左上角的点
int col=(x-MARGIN)/BLOCK_SIZE;
int row=(y-MARGIN)/BLOCK_SIZE;
int leftTopPosX=MARGIN+BLOCK_SIZE*col;
int leftTopPosY=MARGIN+BLOCK_SIZE*row;
clickPosRow= -1;
clickPosCol= -1;
int len=0;
selectPos=false;
//确定误差在范围内的点,只可能确定一个出来
len=sqrt((x-leftTopPosX)*(x-leftTopPosX)+(y-leftTopPosY)*(y-leftTopPosY));
if(len<POS_OFFSET)
{
clickPosRow=row;
clickPosCol=col;
if(game->gameMapVec[clickPosRow][clickPosCol]==0)
{
selectPos=true;
}
}
len=sqrt((x-leftTopPosX-BLOCK_SIZE)*(x-leftTopPosX-BLOCK_SIZE)+(y-leftTopPosY)*(y-leftTopPosY));
if(len<POS_OFFSET)
{
clickPosRow=row;
clickPosCol=col+1;
if(game->gameMapVec[clickPosRow][clickPosCol]==0)
{
selectPos=true;
}
}
len=sqrt((x-leftTopPosX)*(x-leftTopPosX)+(y-leftTopPosY-BLOCK_SIZE)*(y-leftTopPosY-BLOCK_SIZE));
if(len<POS_OFFSET)
{
clickPosRow=row+1;
clickPosCol=col;
if(game->gameMapVec[clickPosRow][clickPosCol]==0)
{
selectPos=true;
}
}
len=sqrt((x-leftTopPosX-BLOCK_SIZE)*(x-leftTopPosX-BLOCK_SIZE)+(y-leftTopPosY-BLOCK_SIZE)*(y-leftTopPosY-BLOCK_SIZE));
if(len<POS_OFFSET)
{
clickPosRow=row+1;
clickPosCol=col;
if(game->gameMapVec[clickPosRow][clickPosCol]==0)
{
selectPos=true;
}
}
}
update();
}
4.4连成五子的判断部分
用于判断各个方向是否连成五子,这里用二维数组的方式通过for循环进行指定方向的连五子判断,每下一步棋后就会进行如下的判断函数,函数内会依次进行水平方向,竖直方向,左上左下,右上右下方向的判断,如果其中一个符合则返回ture进行胜负判定,游戏结束。
//判断连5子
bool Game_Model::isWin(int row, int col)
{
//水平方向判断是否有五个子
for(int i=0;i<5;i++)
{
if(col-i>0&&
col-i+4<BOARD_GRAD_SIZE&&
gameMapVec[row][col-i]==gameMapVec[row][col-i+1]&&
gameMapVec[row][col-i]==gameMapVec[row][col-i+2]&&
gameMapVec[row][col-i]==gameMapVec[row][col-i+3]&&
gameMapVec[row][col-i]==gameMapVec[row][col-i+4])
return true;
}
//竖直方向
for(int i=0;i<5;i++)
{
if(row-i>0&&
row-i+4<BOARD_GRAD_SIZE&&
gameMapVec[row-i][col]==gameMapVec[row-i+1][col]&&
gameMapVec[row-i][col]==gameMapVec[row-i+2][col]&&
gameMapVec[row-i][col]==gameMapVec[row-i+3][col]&&
gameMapVec[row-i][col]==gameMapVec[row-i+4][col])
return true;
}
//斜方向/
for(int i=0;i<5;i++)
{
if(row+i<BOARD_GRAD_SIZE&&
row+i-4>0&&
col-i>0&&
col-i+4<BOARD_GRAD_SIZE&&
gameMapVec[row+i][col-i]==gameMapVec[row+i-1][col-i+1]&&
gameMapVec[row+i][col-i]==gameMapVec[row+i-2][col-i+2]&&
gameMapVec[row+i][col-i]==gameMapVec[row+i-3][col-i+3]&&
gameMapVec[row+i][col-i]==gameMapVec[row+i-4][col-i+4]
)
return true;
}
//斜方向\,
for(int i=0;i<5;i++)
{
if(
row-i>0&&
row-i+4<BOARD_GRAD_SIZE&&
col-i>0&&
col-i+4<BOARD_GRAD_SIZE&&
gameMapVec[row-i][col-i]==gameMapVec[row-i+1][col-i+1]&&
gameMapVec[row-i][col-i]==gameMapVec[row-i+2][col-i+2]&&
gameMapVec[row-i][col-i]==gameMapVec[row-i+3][col-i+3]&&
gameMapVec[row-i][col-i]==gameMapVec[row-i+4][col-i+4]
)
return true;
}
return false;
}
此外我们还需要一个判断输赢的程序代码部分通过对上面的bool Game_Model::isWin()函数进行反馈,同时还要判断鼠标点击部分是否是在棋盘范围内,同时我们还规定棋盘边缘不能落子,边缘不能落子的代码在鼠标位置标示那一部分有提到。
//判断输赢
if(clickPosRow>0&&clickPosRow<BOARD_GRAD_SIZE&&
clickPosCol>0&&clickPosCol<BOARD_GRAD_SIZE&&
(game->gameMapVec[clickPosRow][clickPosCol]==1||
game->gameMapVec[clickPosRow][clickPosCol]==-1))
{
if(game->isWin(clickPosRow,clickPosCol)&&game->gameStatus==PLAYING)
{
game->gameStatus=WIN;
//QSound::play(":sound/win.wav");
QString str;
if(game->gameMapVec[clickPosRow][clickPosCol]==1)
str="黑棋";
else if(game->gameMapVec[clickPosRow][clickPosCol]==-1)
str="白棋";
QMessageBox::StandardButton btnValue=QMessageBox::information(this,"五子棋决战",str+"胜利!");
//重置游戏状态,否则容易死循环
if(btnValue==QMessageBox::Ok)
{
game->startGame(game_type);
game->gameStatus=PLAYING;
}
}
}
}
最后,定义一个函数用于实现人来操作下一步棋的设置,代码如下。
void MainWindow::chessOneByPerson()
{
if(clickPosRow!=-1&&clickPosCol!=-1&&game->gameMapVec[clickPosRow][clickPosCol]==0)
{
game->actionByPerson(clickPosRow,clickPosCol);
update();
}
}
void MainWindow::paintEvent(QPaintEvent *event)
{#include "Game_Model.h"
#include <time.h>
#include <stdlib.h>
void Game_Model::startGame(GameType type)
{
gameType=type;
//初始棋盘
gameMapVec.clear();
for(int i=0;i<BOARD_GRAD_SIZE;i++)
{
std::vector<int>lineBoard;
for(int j=0;j<BOARD_GRAD_SIZE;j++)
lineBoard.push_back(0);
gameMapVec.push_back(lineBoard);
}
//AI模式,初始化评分数组
if(gameType==AI)
{
scoreMapVec.clear();
for(int i=0;i<BOARD_GRAD_SIZE;i++)
{
std::vector<int>lineScores;
for(int j=0;j<BOARD_GRAD_SIZE;j++)
lineScores.push_back(0);
scoreMapVec.push_back(lineScores);
}
}
//轮到黑方下棋为true
playerFlag=true;
}
void Game_Model::actionByPerson(int row, int col)
{
updateGameMap(row,col);
}
void Game_Model::updateGameMap(int row, int col)
{
if(playerFlag)
gameMapVec[row][col]=1;
else
gameMapVec[row][col]=-1;
//换手
playerFlag=!playerFlag;
}
//判断连5子
bool Game_Model::isWin(int row, int col)
{
//水平方向判断是否有五个子
for(int i=0;i<5;i++)
{
if(col-i>0&&
col-i+4<BOARD_GRAD_SIZE&&
gameMapVec[row][col-i]==gameMapVec[row][col-i+1]&&
gameMapVec[row][col-i]==gameMapVec[row][col-i+2]&&
gameMapVec[row][col-i]==gameMapVec[row][col-i+3]&&
gameMapVec[row][col-i]==gameMapVec[row][col-i+4])
return true;
}
//竖直方向
for(int i=0;i<5;i++)
{
if(row-i>0&&
row-i+4<BOARD_GRAD_SIZE&&
gameMapVec[row-i][col]==gameMapVec[row-i+1][col]&&
gameMapVec[row-i][col]==gameMapVec[row-i+2][col]&&
gameMapVec[row-i][col]==gameMapVec[row-i+3][col]&&
gameMapVec[row-i][col]==gameMapVec[row-i+4][col])
return true;
}
//斜方向/
for(int i=0;i<5;i++)
{
if(row+i<BOARD_GRAD_SIZE&&
row+i-4>0&&
col-i>0&&
col-i+4<BOARD_GRAD_SIZE&&
gameMapVec[row+i][col-i]==gameMapVec[row+i-1][col-i+1]&&
gameMapVec[row+i][col-i]==gameMapVec[row+i-2][col-i+2]&&
gameMapVec[row+i][col-i]==gameMapVec[row+i-3][col-i+3]&&
gameMapVec[row+i][col-i]==gameMapVec[row+i-4][col-i+4]
)
return true;
}
//斜方向\,
for(int i=0;i<5;i++)
{
if(
row-i>0&&
row-i+4<BOARD_GRAD_SIZE&&
col-i>0&&
col-i+4<BOARD_GRAD_SIZE&&
gameMapVec[row-i][col-i]==gameMapVec[row-i+1][col-i+1]&&
gameMapVec[row-i][col-i]==gameMapVec[row-i+2][col-i+2]&&
gameMapVec[row-i][col-i]==gameMapVec[row-i+3][col-i+3]&&
gameMapVec[row-i][col-i]==gameMapVec[row-i+4][col-i+4]
)
return true;
}
return false;
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPainter> //画家头文件
#include <QMouseEvent>
#include <math.h>
#include <QMessageBox>
#include <QTimer>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setMouseTracking(true);//使用鼠标
// 设置窗口大小
setFixedSize
(MARGIN*2+BLOCK_SIZE*BOARD_GRAD_SIZE
,MARGIN*2+BLOCK_SIZE*BOARD_GRAD_SIZE);
initGame();
}
MainWindow::~MainWindow()
{
delete ui;
}
QPainter painter(this);
//绘制棋盘
painter.setRenderHint(QPainter::Antialiasing,true);// 抗锯齿
for (int i=0;i<BOARD_GRAD_SIZE+1;i++)
{
//从左到右第i+1 条 竖线
painter.drawLine(MARGIN+BLOCK_SIZE*i,MARGIN ,
MARGIN+BLOCK_SIZE*i,size().height()-MARGIN);
// 从上到下第i+1 条 竖线
painter.drawLine(MARGIN,MARGIN+BLOCK_SIZE*i ,
size().width()-MARGIN,MARGIN+BLOCK_SIZE*i);
}
//绘制选中点
QBrush brush;
brush.setStyle(Qt::SolidPattern);
//绘制落子标记
if(clickPosRow>0&&clickPosRow<BOARD_GRAD_SIZE&&
clickPosCol>0&&clickPosCol<BOARD_GRAD_SIZE&&
game->gameMapVec[clickPosRow][clickPosCol]==0)
{
if(game->playerFlag)
brush.setColor(Qt::black);
else
brush.setColor(Qt::white);
painter.setBrush(brush);
//落子前的标示位置
painter.drawRect(MARGIN+BLOCK_SIZE*clickPosCol-MARK_SIZE/2,MARGIN+BLOCK_SIZE*clickPosRow-MARK_SIZE/2,MARK_SIZE,MARK_SIZE);//?
}
//绘制棋子
for(int i=0;i<BOARD_GRAD_SIZE;i++)
for(int j=0;j<BOARD_GRAD_SIZE;j++)
{
if(game->gameMapVec[i][j]==1)
{
brush.setColor(Qt::black);
painter.setBrush(brush);
//落点判断有误
painter.drawEllipse(MARGIN+BLOCK_SIZE*j-CHESS_RADIUS/2,MARGIN+BLOCK_SIZE*i-CHESS_RADIUS/2,CHESS_RADIUS,CHESS_RADIUS);
}
else if(game->gameMapVec[i][j]==-1)
{
brush.setColor(Qt::white);
painter.setBrush(brush);
painter.drawEllipse(MARGIN+BLOCK_SIZE*j-CHESS_RADIUS/2,MARGIN+BLOCK_SIZE*i-CHESS_RADIUS/2,CHESS_RADIUS,CHESS_RADIUS);
}
}
//判断输赢
if(clickPosRow>0&&clickPosRow<BOARD_GRAD_SIZE&&
clickPosCol>0&&clickPosCol<BOARD_GRAD_SIZE&&
(game->gameMapVec[clickPosRow][clickPosCol]==1||
game->gameMapVec[clickPosRow][clickPosCol]==-1))
{
if(game->isWin(clickPosRow,clickPosCol)&&game->gameStatus==PLAYING)
{
game->gameStatus=WIN;
//QSound::play(":sound/win.wav");
QString str;
if(game->gameMapVec[clickPosRow][clickPosCol]==1)
str="黑棋";
else if(game->gameMapVec[clickPosRow][clickPosCol]==-1)
str="白棋";
QMessageBox::StandardButton btnValue=QMessageBox::information(this,"五子棋决战",str+"胜利!");
//重置游戏状态,否则容易死循环
if(btnValue==QMessageBox::Ok)
{
game->startGame(game_type);
game->gameStatus=PLAYING;
}
}
}
}
void MainWindow:: initGame()
{
//初始化游戏类型
game=new Game_Model;
initAIGame();
}
void MainWindow::initAIGame()//初始化AI游戏
{
game_type=AI;//游戏类型设成AI模式
game->gameStatus=PLAYING;//开玩状态
//在数据模型中进行初始化功能
game->startGame(game_type);//启动游戏
update();
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
//通过鼠标确定标记跟踪
int x=event->x();
int y=event->y();
//边缘不能落子
if(x>=MARGIN+BLOCK_SIZE/2&&
x<size().width()-MARGIN-BLOCK_SIZE/2&&
y>=MARGIN+BLOCK_SIZE/2&&
y<size().height()-MARGIN-BLOCK_SIZE/2)
{
//获取最近左上角的点
int col=(x-MARGIN)/BLOCK_SIZE;
int row=(y-MARGIN)/BLOCK_SIZE;
int leftTopPosX=MARGIN+BLOCK_SIZE*col;
int leftTopPosY=MARGIN+BLOCK_SIZE*row;
clickPosRow= -1;
clickPosCol= -1;
int len=0;
selectPos=false;
//确定误差在范围内的点,只可能确定一个出来
len=sqrt((x-leftTopPosX)*(x-leftTopPosX)+(y-leftTopPosY)*(y-leftTopPosY));
if(len<POS_OFFSET)
{
clickPosRow=row;
clickPosCol=col;
if(game->gameMapVec[clickPosRow][clickPosCol]==0)
{
selectPos=true;
}
}
len=sqrt((x-leftTopPosX-BLOCK_SIZE)*(x-leftTopPosX-BLOCK_SIZE)+(y-leftTopPosY)*(y-leftTopPosY));
if(len<POS_OFFSET)
{
clickPosRow=row;
clickPosCol=col+1;
if(game->gameMapVec[clickPosRow][clickPosCol]==0)
{
selectPos=true;
}
}
len=sqrt((x-leftTopPosX)*(x-leftTopPosX)+(y-leftTopPosY-BLOCK_SIZE)*(y-leftTopPosY-BLOCK_SIZE));
if(len<POS_OFFSET)
{
clickPosRow=row+1;
clickPosCol=col;
if(game->gameMapVec[clickPosRow][clickPosCol]==0)
{
selectPos=true;
}
}
len=sqrt((x-leftTopPosX-BLOCK_SIZE)*(x-leftTopPosX-BLOCK_SIZE)+(y-leftTopPosY-BLOCK_SIZE)*(y-leftTopPosY-BLOCK_SIZE));
if(len<POS_OFFSET)
{
clickPosRow=row+1;
clickPosCol=col;
if(game->gameMapVec[clickPosRow][clickPosCol]==0)
{
selectPos=true;
}
}
}
update();
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)//鼠标松开确定落子
{
if(selectPos==false)
{
return;
}
else
{//落子前,先标记在设置false
selectPos=false;
}
chessOneByPerson();
if(game_type==AI)//人机模式
{
//AI下棋
//QTimer::singleShot(AI_THINK_TIME,this,SLOT(chessOneByAI()));
}
}
void MainWindow::chessOneByPerson()
{
if(clickPosRow!=-1&&clickPosCol!=-1&&game->gameMapVec[clickPosRow][clickPosCol]==0)
{
game->actionByPerson(clickPosRow,clickPosCol);
update();
}
}
网盘链接:复制到浏览器下载 提取码:5bz5