五子棋GUI_C++

五子棋游戏人机对战 C/C++ (图形界面)

准备:找好游戏图片、音乐等基本素材。另外,代码中的图片尺寸数据根据各自的实际素材调整。
注:本人的素材均不做盈利用,不涉及版权。
tools文件是从相关网站学习课程上获得的,此处对相关内容不做明示。可以告知的是tools做的工作是使PNG图片背景透明。
tools.h
在这里插入图片描述

主函数:

#define _CRT_SECURE_NO_WARNINGS 1
/*
* 设计一个人机交互程序——五子棋 也可参考设计军棋、跳棋、围棋、象棋
*/
#include "ChessData.h"

int main() { //_wuziqi2
	board_Init();

	//主要功能区
	while (1) { //~while(true) C/C++最好用while(1)
		MOUSEMSG msg = GetMouseMsg(); //获取光标信息 如果报错4996,项目名右键->属性->c/c++->sdl检查选否
		if (msg.uMsg == WM_LBUTTONDOWN && clickBoard(msg)) { //鼠标左键点击落子 且是有效点击
			man_Go();
			if (checkOver()) {
				board_Init();
				continue;
			}

			AI_Go();
			if (checkOver()) {
				board_Init();
				continue;
			}
		}
	}

	closegraph();

	system("pause");
	return 0;
}

“ChessData.h”文件内容:(需要graphics.h)
说明:AI走子算法是关键。

#pragma once
#include <stdlib.h>
#include <windows.h>
#include "tools.h" //外部写的头文件
#include <mmsystem.h> //音乐播放
#pragma comment (lib, "winmm.lib") //表示接入winmm.lib静态链接库,和在工程设置中写上链入winmm.lib的效果相同

const float BLOCK_SIZE = 50.4; //const float BLOCK_SIZE = 50.4; //25.2
const int BOARD_GRADE_SIZE = 13;
const int POS_OFFSET = BLOCK_SIZE * 0.4; //位置模糊,一定要小于0.5
const int margin_x = 32;
const int margin_y = 31;

typedef enum {
	CHESS_WHITE = -1,
	CHESS_BLACK = 1
} chess_kind_t;

struct ChessData {
	//0:空白 1:黑子 -1:白子
	int chessMap[BOARD_GRADE_SIZE][BOARD_GRADE_SIZE]; //使用宏,好
	//各个落子点评分
	int scoreMap[BOARD_GRADE_SIZE][BOARD_GRADE_SIZE];
	//轮流下棋,true黑方,flase白方
	bool playerFlag;
};

typedef struct point {
	int row;
	int col;
}point_t;

void board_Init(); //棋盘初始化
void initChessData(struct ChessData* data);

bool clickBoard(MOUSEMSG msg); //判断有效点击
void chessDown(int row, int col, chess_kind_t kind);
void updateGameMap(ChessData* data, int row, int col);

bool checkWin(ChessData* game, int row, int col); //row和col表示当前落子
bool checkOver();
void man_Go();
void caculateScore(ChessData* data);
point_t action_AI(ChessData* data); //机器执行下棋
void AI_Go();

“ChessData.cpp”文件内容:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h> //NULL
#include <math.h> //sqrt
#include <string.h>
#include <memory.h> //memset //string.h
#include <conio.h> //getch
#include <time.h>
#include "ChessData.h"

//加载图片到内存,提高效率
IMAGE chessBlackImg; //黑白子需要不断输出
IMAGE chessWhiteImg;
int clickPosRow, clickPosCol; //表示有效点击的实际位置(行列)
struct ChessData game;

void initChessData(struct ChessData* data) {
	if (!data) { //data == NULL
		return;
	}

	memset(data->chessMap, 0, sizeof(data->chessMap)); //比双重for更高级
	memset(data->scoreMap, 0, sizeof(data->scoreMap));
	data->playerFlag = true;
}
void board_Init() {
	initgraph(673, 672); //根据图片尺寸确定
	loadimage(0, "res/棋盘.jpg"); //加载报错时,项目名右键->属性->高级->字符集选择多字节字符集
	//添加对战开始背景音
	mciSendString("play res/start.wav", 0, 0, 0);  //可以添加背景音乐,mciSendString("play 背景音乐 repeat", 0, 0, 0);

	loadimage(&chessBlackImg, "res/black.png", BLOCK_SIZE, BLOCK_SIZE, true); //图片太大时true等比缩放
	loadimage(&chessWhiteImg, "res/white.png", BLOCK_SIZE, BLOCK_SIZE, true);

	initChessData(&game);
}

bool clickBoard(MOUSEMSG msg) {
	//上下边界 x32 y31,定义在ChessData.h中
	int col = (msg.x - margin_x) / BLOCK_SIZE;
	int row = (msg.y - margin_y) / BLOCK_SIZE;

	int leftTopPosX = margin_x + col * BLOCK_SIZE;
	int leftTopPosY = margin_y + row * BLOCK_SIZE;

	int selectPos = false;
	do {
		//左上角
		int len = sqrt((msg.x - leftTopPosX) * (msg.x - leftTopPosX) +
			(msg.y - leftTopPosY) * (msg.y - leftTopPosY));
		if (len < POS_OFFSET) {
			clickPosRow = row;
			clickPosCol = col;
			selectPos = true;
			break;
		}
		//右上角 leftTopPosX+BLOCK_SIZE leftTopPosY,其余同理
		len = sqrt((msg.x - (leftTopPosX + BLOCK_SIZE)) * (msg.x - (leftTopPosX + BLOCK_SIZE)) +
			(msg.y - leftTopPosY) * (msg.y - leftTopPosY));
		if (len < POS_OFFSET) {
			clickPosRow = row;
			clickPosCol = col + 1;
			selectPos = true;
			break;
		}
		//右下角
		len = sqrt((msg.x - (leftTopPosX + BLOCK_SIZE)) * (msg.x - (leftTopPosX + BLOCK_SIZE)) +
			(msg.y - (leftTopPosY + BLOCK_SIZE)) * (msg.y - (leftTopPosY + BLOCK_SIZE)));
		if (len < POS_OFFSET) {
			clickPosRow = row + 1;
			clickPosCol = col + 1;
			selectPos = true;
			break;
		}
		//左下角
		len = sqrt((msg.x - leftTopPosX) * (msg.x - leftTopPosX) +
			(msg.y - (leftTopPosY + BLOCK_SIZE)) * (msg.y - (leftTopPosY + BLOCK_SIZE)));
		if (len < POS_OFFSET) {
			clickPosRow = row + 1;
			clickPosCol = col;
			selectPos = true;
			break;
		}
	} while (0); //只循环一次,关键作用是break避免使用return
	return selectPos;
}
void chessDown(int row, int col, chess_kind_t kind) {
	mciSendString("play res/down7.WAV", 0, 0, 0);

	int x = margin_x + clickPosCol * BLOCK_SIZE - 0.5 * BLOCK_SIZE;
	int y = margin_y + clickPosRow * BLOCK_SIZE - 0.5 * BLOCK_SIZE;

	if (kind == CHESS_BLACK) {
		//putimage(msg.x, msg.y, &chessBlackImg); //在鼠标点击处放图片,从左上角开始铺图
		drawPNG(x, y, &chessBlackImg); //tools.h中的函数
		//还需要模糊算法处理摆正位置,模块化,clickBoard
	}
	else {
		drawPNG(x, y, &chessWhiteImg);
	}
}
void updateGameMap(ChessData* data, int row, int col) {
	if (!data) {
		return;
	}

	if (data->playerFlag) {
		data->chessMap[row][col] = 1;
	}
	else {
		data->chessMap[row][col] = -1;
	}

	data->playerFlag = !data->playerFlag; //换手
}

bool checkWin(ChessData* game, int row, int col) {
	//8个方向,本质是4个方向只要有5连就算赢
	int i;

	//水平
	for (i = 0; i < 5; i++) {
		//左、右各匹配
		if (col - i >= 0 &&
			col - i + 4 < BOARD_GRADE_SIZE &&
			game->chessMap[row][col - i] == game->chessMap[row][col - i + 1] &&
			game->chessMap[row][col - i] == game->chessMap[row][col - i + 2] &&
			game->chessMap[row][col - i] == game->chessMap[row][col - i + 3] &&
			game->chessMap[row][col - i] == game->chessMap[row][col - i + 4] ) 
		{
			return true;
		}
	}
	//竖直
	for (i = 0; i < 5; i++) {
		if (row - i >= 0 &&
			row - i + 4 < BOARD_GRADE_SIZE &&
			game->chessMap[row - i][col] == game->chessMap[row - i + 1][col] &&
			game->chessMap[row - i][col] == game->chessMap[row - i + 2][col] &&
			game->chessMap[row - i][col] == game->chessMap[row - i + 3][col] &&
			game->chessMap[row - i][col] == game->chessMap[row - i + 4][col] )
		{
			return true;
		}
	}
	// '/'
	for (i = 0; i < 5; i++) {
		if (row + i < BOARD_GRADE_SIZE &&
			row + i - 4 >= 0 &&
			col - i >= 0 &&
			col - i + 4 < BOARD_GRADE_SIZE &&
			//第[row+i]行,第[col-i]列的棋子,与右上方连续4子相同
			game->chessMap[row + i][col - i] == game->chessMap[row + i - 1][col - i + 1] &&
			game->chessMap[row + i][col - i] == game->chessMap[row + i - 2][col - i + 2] &&
			game->chessMap[row + i][col - i] == game->chessMap[row + i - 3][col - i + 3] &&
			game->chessMap[row + i][col - i] == game->chessMap[row + i - 4][col - i + 4] )
		{
			return true;
		}
	}
	// '\'
	for (i = 0; i < 5; i++) {
		if (row - i >= 0 &&
			row - i + 4 < BOARD_GRADE_SIZE &&
			col - i >= 0 &&
			col - i + 4 < BOARD_GRADE_SIZE &&
			//第[row-i]行,第[col-i]列的棋子,与右下方连续4子相同
			game->chessMap[row - i][col - i] == game->chessMap[row - i + 1][col - i + 1] &&
			game->chessMap[row - i][col - i] == game->chessMap[row - i + 2][col - i + 2] &&
			game->chessMap[row - i][col - i] == game->chessMap[row - i + 3][col - i + 3] &&
			game->chessMap[row - i][col - i] == game->chessMap[row - i + 4][col - i + 4] )
		{
			return true;
		}
	}

	return false;
}
bool checkOver() {
	if (checkWin(&game, clickPosRow, clickPosCol)) {
		//Sleep(1500);
		if (game.playerFlag == false) {
			mciSendString("play res/不错.mp3", 0, 0, 0);
			loadimage(0, "res/胜利.jpg");
		}
		else {
			mciSendString("play res/失败.mp3", 0, 0, 0);
			loadimage(0, "res/失败.jpg");
		}

		getch();
		return true;
	}

	return false;
}

void man_Go() {
	chessDown(clickPosRow, clickPosCol, CHESS_BLACK);
	updateGameMap(&game, clickPosRow, clickPosCol);
}

//*************************系统评分函数,是关键*******************************
/*
*      黑子      白子(连1/普通~~~5)
* 连2   10        10
* 死3   30        25
* 活3   40        50
* 死4   60        55
* 活4   200       300
* 连5   20000     30000
*/
void caculateScore(ChessData* data) {
	if (!data) {
		return;
	}

	int row, col, i, k;
	//统计玩家或电脑连子个数
	int man_Num = 0; //玩家连子个数
	int AI_Num = 0; //AI连子个数
	int emptyNum = 0; //空白位个数

	//清空评分数组
	memset(data->scoreMap, 0, sizeof(data->scoreMap));

	for (row = 0; row < BOARD_GRADE_SIZE; row++) {
		for (col = 0; col < BOARD_GRADE_SIZE; col++) {
			//空白点就算
			if (row >= 0 && col >= 0 && data->chessMap[row][col] == 0) {
				//遍历周围4个方向,考虑正反
				int direction[4][2] = { {1,0}, {1,1}, {0,1}, {-1,1} }; //
				for (k = 0; k < 4; k++) {
					int x = direction[k][0];
					int y = direction[k][1]; //

					//重置
					man_Num = 0;
					AI_Num = 0;
					emptyNum = 0;

					//黑方正向计数
					for (i = 1; i <= 4; i++) {
						if (row + i * y >= 0 &&
							row + i * y < BOARD_GRADE_SIZE &&
							col + i * x >= 0 &&
							col + i * x < BOARD_GRADE_SIZE &&
							data->chessMap[row + i * y][col + i * x] == 1)
						{
							man_Num++;
						}
						else if (row + i * y >= 0 &&
							row + i * y < BOARD_GRADE_SIZE &&
							col + i * x >= 0 &&
							col + i * x < BOARD_GRADE_SIZE &&
							data->chessMap[row + i * y][col + i * x] == 0)
						{
							emptyNum++;
							break; //遇到空白位,停止搜索
						}
						else { //出边界或者遇到白棋,停止搜索
							break;
						}
					}
					//黑反
					for (i = 1; i <= 4; i++) {
						if (row - i * y >= 0 &&
							row - i * y < BOARD_GRADE_SIZE &&
							col - i * x >= 0 &&
							col - i * x < BOARD_GRADE_SIZE &&
							data->chessMap[row - i * y][col - i * x] == 1)
						{
							man_Num++;
						}
						else if (row - i * y >= 0 &&
							row - i * y < BOARD_GRADE_SIZE &&
							col - i * x >= 0 &&
							col - i * x < BOARD_GRADE_SIZE &&
							data->chessMap[row - i * y][col - i * x] == 0)
						{
							emptyNum++;
							break; //遇到空白位,停止搜索
						}
						else { //出边界或者遇到白棋,停止搜索
							break;
						}
					}
					//黑,数->分 //elseif else
					if (man_Num == 1) { //杀2
						data->scoreMap[row][col] += 10;
					}
					else if (man_Num == 2) { //杀3
						if (emptyNum == 1) { //死3
							data->scoreMap[row][col] += 30;
						}
						else if (emptyNum == 2) { //活3
							data->scoreMap[row][col] += 40;
						}
					}
					else if (man_Num == 3) { //杀4
						if (emptyNum == 1) {
							data->scoreMap[row][col] += 60;
						}
						else if (emptyNum == 2) {
							data->scoreMap[row][col] += 200;
						}
					}
					else if (man_Num == 4) { //杀5
						data->scoreMap[row][col] += 20000;
					}

					//进行一次清空
					emptyNum = 0;

					//对白棋评分
					for (i = 1; i <= 4; i++) { //正
						if (row + i * y > 0 &&
							row + i * y < BOARD_GRADE_SIZE &&
							col + i * x > 0 &&
							col + i * x < BOARD_GRADE_SIZE &&
							data->chessMap[row + i * y][col + i * x] == -1)
						{
							AI_Num++;
						}
						else if (row + i * y > 0 &&
							row + i * y < BOARD_GRADE_SIZE &&
							col + i * x > 0 &&
							col + i * x < BOARD_GRADE_SIZE &&
							data->chessMap[row + i * y][col + i * x] == 0)
						{
							emptyNum++;
							break;
						}
						else {
							break;
						}
					}
					for (i = 1; i <= 4; i++) { //反
						if (row - i * y > 0 &&
							row - i * y < BOARD_GRADE_SIZE &&
							col - i * x > 0 &&
							col - i * x < BOARD_GRADE_SIZE &&
							data->chessMap[row - i * y][col - i * x] == -1)
						{
							AI_Num++;
						}
						else if (row - i * y > 0 &&
							row - i * y < BOARD_GRADE_SIZE &&
							col - i * x > 0 &&
							col - i * x < BOARD_GRADE_SIZE &&
							data->chessMap[row - i * y][col - i * x] == 0) //空白位
						{
							emptyNum++;
							break;
						}
						else { //出边界
							break;
						}
					}
					if (AI_Num == 0) { //普通下子
						data->scoreMap[row][col] += 5;
					}
					else if (AI_Num == 1) { //活2
						data->scoreMap[row][col] += 10;
					}
					else if (AI_Num == 2) {
						if (emptyNum == 1) {
							data->scoreMap[row][col] += 25;
						}
						else if (emptyNum == 2) {
							data->scoreMap[row][col] += 50;
						}
					}
					else if (AI_Num == 3) {
						if (emptyNum == 1) {
							data->scoreMap[row][col] += 55;
						}
						else if (emptyNum == 2) {
							data->scoreMap[row][col] += 300;
						}
					}
					else if (AI_Num >= 4) {
						data->scoreMap[row][col] += 30000;
					}
				}
			}
		}
	}
}

point_t action_AI(ChessData* data) {
	caculateScore(data);

	int maxScore = 0;
	//std::vector<std::pair<int, int>>maxPoints;
	point_t maxPoints[BOARD_GRADE_SIZE * BOARD_GRADE_SIZE] = { 0, };
	int k = 0;

	for (int row = 0; row < BOARD_GRADE_SIZE; row++) { //面向对象语言
		for (int col = 0; col < BOARD_GRADE_SIZE; col++) {
			//前提是坐标为空
			if (data->chessMap[row][col] == 0) {
				if (data->scoreMap[row][col] > maxScore) {
					//maxPoints.clear();
					memset(maxPoints, 0, sizeof(maxPoints));
					k = 0;
					maxScore = data->scoreMap[row][col];
					//maxPoints.push_back(std::make_pair(row, col));
					maxPoints[k].row = row;
					maxPoints[k].col = col;
					k++;
				}
				else if (data->scoreMap[row][col] == maxScore) {
					//maxPoints.push_back(std::make_pair(row, col));
					maxPoints[k].row = row;
					maxPoints[k].col = col;
					k++;
				}
			}
		}
	}

	srand((unsigned)time(0));
	int index = rand() % k;
	return maxPoints[index];
}
void AI_Go() {
	point_t point = action_AI(&game);
	clickPosRow = point.row;
	clickPosCol = point.col;

	//Sleep(1000);
	chessDown(clickPosRow, clickPosCol, CHESS_WHITE);
	updateGameMap(&game, clickPosRow, clickPosCol);
}

附结果图:
在这里插入图片描述
补充:已发现的漏洞有黑子可覆盖白子,需要在玩家走子函数中添加判断条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值