失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Qt C++实现五子棋人机对战与本地双人对战

Qt C++实现五子棋人机对战与本地双人对战

时间:2023-12-26 18:30:12

相关推荐

Qt C++实现五子棋人机对战与本地双人对战

1.基本介绍

软件基于Qt5.6.1,利用C++语言进行编写。

五子棋有两种游戏模式:人机对战和双人对战。

在进入游戏时玩家选择游戏模式。 选择人机对战玩家会执黑棋、电脑执白棋进行对决。选择双人对战会由玩家双方各自操作鼠标进行对战。

2.详细设计

2.1 模式选择界面

进入游戏首先呈现的是模式选择界面。模式选择界面逻辑较为简单,不需要多余装饰,故可以使用QMessageBox类来呈现,根据用户点击的按钮进入到对应的游戏模式,并弹出游戏界面。

2.2 游戏界面

五子棋为15*15一共225个点,14*14格。棋盘结构较为简单,可利用画家类QPainter自行绘制出棋盘,棋子同理。落子位置利用对mouseMoveEvent(QMouseEvent* event)重写来实现,根据鼠标移动来捕获落子标记点。

2.3 电脑算法

电脑算法采用对落子位置计分来实现,电脑对不同位置的子有不同的计分方式,最终选出最优计分,将棋子落在得分最高处。电脑算法以守为主,守的分数>攻的分数。

3.主要编码实现

3.1 模式选择界面的编码实现:

//初始化

void MainWindow::initGame(){

game = new GameModel();

QMessageBox msgBox;

msgBox.setWindowTitle("五子棋对战");

msgBox.setText("请选择对战模式");

QPushButton *Button1 = msgBox.addButton(tr("人机对战"), QMessageBox::ActionRole);

QPushButton *Button2 = msgBox.addButton(tr("本地玩家对战"), QMessageBox::ActionRole);

msgBox.setStandardButtons(QMessageBox::Close);

msgBox.exec();

if( msgBox.clickedButton() == Button1){

game_type = AI;

}else if (msgBox.clickedButton() == Button2) {

game_type = MAN;

}else{exit(0);}

game->gameStatus = PLAYING;

game->startGame(game_type);

update();

}

3.2 游戏界面的编码实现:

void MainWindow::paintEvent(QPaintEvent* event){

QPainter painter(this);

painter.setRenderHint(QPainter::Antialiasing); //设置抗锯齿

//画棋盘

for(int i=1;i<BOARD_GRAD_SIZE;i++){

painter.drawLine(MARGIN+BLOCK_SIZE*i,MARGIN+BLOCK_SIZE,

MARGIN+BLOCK_SIZE*i,this->height()-MARGIN-BLOCK_SIZE);

painter.drawLine(MARGIN+BLOCK_SIZE,MARGIN+BLOCK_SIZE*i,

this->width()-MARGIN-BLOCK_SIZE,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,MARGIN+BLOCK_SIZE*i-CHESS_RADIUS,CHESS_RADIUS*2,CHESS_RADIUS*2);

}else if(game->gameMapVec[i][j]==-1){

brush.setColor(Qt::white);

painter.setBrush(brush);

painter.drawEllipse(MARGIN+BLOCK_SIZE*j-CHESS_RADIUS,MARGIN+BLOCK_SIZE*i-CHESS_RADIUS,CHESS_RADIUS*2,CHESS_RADIUS*2);

}

}

}

//判断输赢

if(clickPosCol>0 && clickPosCol<BOARD_GRAD_SIZE &&

clickPosRow>0 && clickPosRow<BOARD_GRAD_SIZE &&

(game->gameMapVec[clickPosRow][clickPosCol]==1||game->gameMapVec[clickPosRow][clickPosCol]==-1)){ //代碼解析:game->gameMapVec[clickPosRow][clickPosCol]==1||game->gameMapVec[clickPosRow][clickPosCol]==-1,防止因為5個0(空白)相連也被判勝利

if(game->isWin(clickPosRow,clickPosCol) && game->gameStatus == PLAYING){

game->gameStatus = WIN;

QString str;

str = game->gameMapVec[clickPosRow][clickPosCol]==1?"黑棋":"白棋";

QMessageBox::StandardButton btnValue = QMessageBox::information(this,"五子棋嬴家",str+"勝利");

if(btnValue == QMessageBox::Ok){

game->startGame(game_type);

game->gameStatus = PLAYING;

}

}

}

}

棋盘,落子标记,黑子,白子如上图所示。

3. 电脑算法的编码实现

void GameModel::calculateScore(){

//统计玩家及电脑连成的子

int personNum = 0; //玩家连成子的个数

int botNum = 0; //AI连成子的个数

int emptyNum = 0; //各方向空白位的个数

//清空评分数组

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);

}

//计分

/*计分个人理解:

*遍历每一个格子,判断哪些是空白的点(即为0的点),以该点为中心,判断周围的八个点向外延伸的四格里,

*有多少个是黑子、白子、空白,以此作为依据来评分。电脑算法是以守为主,所以守的分数>攻的分数

*/

for(int row=0;row<BOARD_GRAD_SIZE;row++){

for(int col=0;col<BOARD_GRAD_SIZE;col++){

//空白点才算

if(row>0 && col>0 && gameMapVec[row][col]==0){

//遍历周围8个方向

for(int y=-1;y<=1;y++){

for(int x=-1;x<=1;x++){

//重置

personNum = 0;

botNum = 0;

emptyNum = 0;

//原坐标不算

if(!(y==0 && x==0)){

//每个方向延伸4个子

//对玩家的黑子评分(正反两个方向)

for(int i=1;i<=4;i++){

if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&

col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&

gameMapVec[row+i*y][col+i*x]==1){ //真人玩家的子

personNum++;

}else if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&

col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&

gameMapVec[row+i*y][col+i*x]==0){ //空白位

emptyNum++;

break;

}else{ //出边界,或有白子

break;

}

}

for(int i=1;i<=4;i++){

if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&

col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&

gameMapVec[row-i*y][col-i*x]==1){ //真人玩家的子

personNum++;

}else if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&

col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&

gameMapVec[row-i*y][col-i*x]==0){ //空白位

emptyNum++;

break;

}else{ //出边界,或有白子

break;

}

}

if(personNum == 1){ //守玩家的第2子

scoreMapVec[row][col]+=10;

}else if(personNum == 2){ //守玩家的第3子

if(emptyNum == 1)

scoreMapVec[row][col]+=30;

else if(emptyNum == 2)

scoreMapVec[row][col]+=40;

}else if(personNum == 3){ //守玩家的第4子

//量变空位不一样,优先级不一样

if(emptyNum == 1)

scoreMapVec[row][col]+=60;

else if(emptyNum == 2)

scoreMapVec[row][col]+=110;

}else if(personNum == 4){ //守玩家的第5子

scoreMapVec[row][col]+=10100;

}

//进行一次清空

emptyNum = 0;

//对AI白子评分

for(int i=1;i<=4;i++){

if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&

col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&

gameMapVec[row+i*y][col+i*x]==-1){ //AI的子

botNum++;

}else if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&

col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&

gameMapVec[row+i*y][col+i*x]==0){ //空白位

emptyNum++;

break;

}else{ //出边界

break;

}

}

for(int i=1;i<=4;i++){

if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&

col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&

gameMapVec[row-i*y][col-i*x]==-1){ //AI的子

botNum++;

}else if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&

col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&

gameMapVec[row-i*y][col-i*x]==0){ //空白位

emptyNum++;

break;

}else{ //出边界

break;

}

}

if(botNum == 0){

scoreMapVec[row][col]+=5; //攻1

}else if(botNum == 1){

scoreMapVec[row][col]+=10; //攻2

}else if(botNum == 2){ //攻3

if(emptyNum == 1)

scoreMapVec[row][col]+=25;

else if(emptyNum == 2)

scoreMapVec[row][col]+=50;

}else if(botNum == 3){ //攻4

if(emptyNum == 1)

scoreMapVec[row][col]+=55;

else if(emptyNum == 2)

scoreMapVec[row][col]+=100;

}else if(botNum >= 4){ //攻5

scoreMapVec[row][col]+=20000;

}

}

}

}

}

}

}

}

//AI执行下棋

void GameModel::actionByAI(int &clickRow,int &clickCol){

//计算评分

calculateScore();

//从评分中找出最大分数的位置

int maxScore = 0;

std::vector<std::pair<int,int>> maxPoints;

for(int row = 1;row<BOARD_GRAD_SIZE;row++){

for(int col = 1;col<BOARD_GRAD_SIZE;col++){

//前提是这个坐标是空的

if(gameMapVec[row][col] == 0){

if(scoreMapVec[row][col]>maxScore){//找最大数和坐标

maxPoints.clear();

maxScore = scoreMapVec[row][col];

maxPoints.push_back(std::make_pair(row,col));

}else if(scoreMapVec[row][col] == maxScore){

//如果有多个最大值就将他们存储起来,在后面的代码随机抽1个

maxPoints.push_back(std::make_pair(row,col));

}

}

}

}

//随机落子,如果有多个点的话

srand((unsigned)time(0));

int index = rand()%maxPoints.size();

std::pair<int,int> pointPair = maxPoints.at(index);

clickRow = pointPair.first;

clickCol = pointPair.second;

updateGameMap(clickRow,clickCol);

}

4. 游戏效果

游戏能够实现两种对战模式,并正确判断输赢。

如果觉得《Qt C++实现五子棋人机对战与本地双人对战》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。