C++五子棋(七)——main函数以及项目总结

main函数

  • main.cpp 代码如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    int main(void){
    init();

    while(1){
    //一直检测鼠标点击
    MOUSEMSG msg == GetMouseMsg();
    if(msg.uMsg == WM_LBUTTONDOWN){
    manGo();
    if(checkOver()){
    init();
    continue;
    }

    AI_GO();
    if(checkOver()){
    init();
    continue;
    }

    }

    }

    closegraph();
    return 0;

    }

项目总结

  • 学习了c语言模块化开发
  • 设计了判断鼠标点击的算法
  • 掌握了AI走棋的写法
  • ……

不足之处

  • 代码缺乏优化,vector没有充分使用
  • 玩家不能选择棋子颜色
  • 无法进行玩家对战
  • AI算法效率不够高

进阶

  • 继续学习数据结构与算法对AI进行优化
  • 尝试建立服务器实现网络对战等

C++五子棋(六)——游戏结束

规则原理

如图

1 2 3 4 5

判断游戏结束

  • chessData.h
1
2
//row,col	表示当前落子
bool checkWin(ChessData* game, int row, int col);
  • 横、竖、斜(斜有两种)共四种情况,每种情况根据当前落子往后遍历5个子,有一种符合就胜利
  • chessData.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
bool checkWin(ChessData* game, int row, int col){
//横
for(int i = 0; i < 5; i++){
if(col - i >= 0 &&
col - i + 4 < BOARD_GRAD_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(int i = 0; i < 5; i++){
if(row - i >= 0 &&
row - i + 4 < BOARD_GRAD_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(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 &&
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(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 &&
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;

}

调用接口

  • main.cpp
1
#include <stdio.h>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
bool checkOver(){
if(checkWin(&game, clickPosRow, clickPosCol)){
Sleep(1500);
if(game.playFlag = false){
//黑棋胜利,此时标记已经转为白棋落子
mciSendString("play res/不错.mp3", 0, 0, 0);
loadimage(0, "res/胜利.jpg");
score += 100; //更新分数
}else{
mciSendString("play res/失败.mp3", 0, 0, 0);
loadimage(0, "res/失败.jpg");
score -= 100; //同理
}


//用于显示分数
char scoreText[64];
sprintf(scoreText, "当前分数:%d", score);
outtextxy(310, 800, scoreText);

//记录分数
FILE* fp = fopen("score.data", "wb");
fwrite(&score, sizeof(score), 1, fp);
fclose(fp);


getch();
return true;

}

return false;

}

显示分数

  • main.cpp
1
2
#define INIT_SCORE 1000
int score; //全局变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void initScore(){
//分数字体设置
settextcolor(WHITE); //color
settextstyle(50, 0, "微软雅黑"); //style

FILE *fp = fopen("score.data", "rb");
if(fp == NULL){
score = INIT_SCORE;
}else{
fread(&score, sizeof(score), 1, fp);
}
if (fp)fclose(fp);

}
  • 然后在main.cpp文件的 init() 函数定义中 继续 添加代码
1
initScore();	//这一行添加到init()函数定义中

结束

到这里五子棋的全部功能已经实现了,但是你会发现程序无法运行。这是理所当然的,因为我们的main函数还没有写,在下一篇文章(也就是本项目的最后一章)我们将完善main函数并做该项目的总结。

C++五子棋(五)——实现AI落子

AI思考落子点

在之前我们已经实现计算权值了,现在要想让AI落子,应根据之前的计算结果使棋子落在分值最大点上。当然可能会出现多个分值相同的最大点,这时在其中随机取一个点落下即可。

  • chessData.h
1
2
3
4
5
6
7
typedef struct point{
int row;
int col;
} point_t;

//机器下棋
point_t actionAI(ChessData* data);
  • chessData.cpp
1
2
3
#include <time.h>
#include <stdlib.h>
#include <vector>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
point_t actionAI(ChessData* data){
//计算评分
calcScore(data);

//找出最大分数位置
int maxScore = 0;
std::vector<std::pair<int, int>> maxPoints;

int k = 0;

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

//若该坐标为空
if(data->chessMap[row][col] == 0){
//找出最大数和坐标
if(data->scoreMap[row][col] > maxScore){
maxScore.clear();
k = 0;
maxScore.push_back(std::make_pair(row, col));
k++;

}else if(data->scoreMap[row][col] == maxScore){
maxPoints.push_back(std::make_pair(row, col));
k++;
}
}

}
}

//如果有多个点随机落子
srand((unsigned)time(0));
int index = rend() % k;
return maxPoints[index];

}

实现AI落子

  • main.cpp
1
2
3
4
5
6
7
8
9
10
void AI_GO(){
point_t point = actionAI(&game);
clickPosRow = point.row;
clickPosCol = point.col;

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

}

C++五子棋(四)——走棋原理及权值计算

原理

计算

  • 计算每个落子点的“权值”,找到权值最大的落子点
  • 对于每个空白点,分别计算周围的八个方向
  • 不妨以该空白点作为参照原点,以水平向右作为X轴正方向,以竖直向下Y轴正方向建立平面直角坐标系
  • 因为在计算某个方向时,正向和反向需同时考虑,实际上只需要四个方向,即向量(1,0)的方向向量(1,1)方向向量(0,1)方向向量(-1,1)方向,图示如下(灵魂画图,请勿吐槽 滑稽)
灵魂画图

走棋原理

连2 活3 死3 活4 死4 连5

产生效果

  • 黑棋走这个点
产生效果 评分
连2 10
死3 30
活3 40
死4 60
活4 200
连5 20000

  • 如果白棋(AI)走这个点
产生效果 评分
连1 5
连2 10
死3 25
活3 50
死4 55
活4 300
连5 30000

权值计算

  • chessData.h
1
void calcScore(ChessData* data);
  • chessData.cpp
1
#include <string>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
void calcScore(ChessData* data){
if(!data) return;

//统计玩家或AI连子
int personNum = 0; //玩家
int botNum = 0; //AI
int emptyNum = 0; //各方向空白位数


//清空评分数组
memset(data->scoreMap, 0, sizeof(data->scoreMap));
for (int row = 0; row < BOARD_GRAD_SIZE; row++){
for(int col = 0; col < BOARD_GRAD_SIZE; col++){
//空白点计算
if(row >= 0 && col >= 0 && data->chessMap[row][col] == 0){
//遍历四个方向,然后分别计算正反四个方向
int directs[4][2] = {{1,0}, {1,1}, {0,1}, {-1,1}};

for(int k = 0; k < 4; k++){
int x = directs[k][0];
int y = directs[k][1];

//重置
personNum = 0;
botNum = 0;
emptyNum = 0;

//对黑棋评分(正向)
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 &&
data->chessMap[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 && data->chessMap[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 &&
data->chessMap[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 &&
data->chessMap[row -i * y][col - i * x] == 0){
emptyNum++;
break;
}else{
break;
}
}

if(personNum == 1){
data->scoreMap[row][col] += 10;
}else if(personNum == 2){
if(emptyNum == 1){
//死3
data->scoreMap[row][col] += 30;
}else if(emptyNum == 2){
//活3
data->scoreMap[row][col] += 40;
}
}else if(personNum == 3){
if(empty == 1){
//死4
data->scoreMap[row][col] += 60;
}else if (emptyNum == 2){
//活4
data->scoreMap[row][col] += 200;
}
}else if(personNum == 4){
data->scoreMap[row][col] += 20000;
}
emptyNum = 0; //清空

//对白棋评分(正向)
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_GARD_SIZE &&
data->chessMap[row + i * y][col + i * x == -1]){
botNum++;
}else if(row + i * y >0 && row + i * y < BOARD_GRAD_SIZE &&
col + i * x > 0 && col + i * x < BOARD_GRAD_SIZE &&
data->chessMap[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 &&
data->chessMap[row - i * y][col -i * x] == -1){
botNum++;
}else if (row - i * y >0 && row - i * y < BOARD_GRAD_SIZE &&
col - i * x > 0 && col - i * x < BOARD_GRAD_SIZE &&
data->chessMap[row - i * y][col - i * x] == 0){
emptyNum++;
break;
}else{
break; //出边界
}
}

if(botNum == 0){
//连1
data->scoreMap[row][col] += 5;
}else if(botNum == 1){
//活2
data->scoreMap[row][col] += 10;
}else if(botNum == 2){
if(emptyNum == 1){
//死3
data->scoreMap[row][col] += 25;
}else if(emptyNum == 2){
//活3
data->scoreMap[row][col] += 50;
}
}else if(botNum == 3){
if(emptyNum == 1){
//死4
data->scoreMap[row][col] += 55;
}else if(botNum == 2){
//活4
data->scoreMap[row][col] += 300;
}
}else if(botNum >= 4){
//活5
data->scoreMap[row][col] += 30000;
}

}

}
}
}
}

C++五子棋(三)——判断鼠标有效点击

分析

在鼠标左键点击时,我们不能让新棋子在已有棋子的位置落下,同时我们还要让棋子在规定位置落下——棋盘线的交点处。

功能实现

创建数据类型

  • 创建头文件chessData.h和源文件chessData.cpp
  • chessData.cppmain.cpp分别引用头文件chessData.h
1
#include "chessData.h"
  • 将之前在main.cpp中写的棋盘数据剪贴chessData.h
1
2
const float BLOCKSIZE = 67.4;
const int BLOCK_GRAD_SIZE = 13;
  • chessData.h中定义常量 POS_OFFSET,即鼠标有效点击距离上限
1
const int POS_OFFSET = BLOCKSIZE * 0.4;
  • chessData.h定义结构体 ChessData
1
2
3
4
5
6
7
8
9
struct ChessData{
//储存当前游戏棋盘的情况,空白为0,黑棋为1,白棋为-1
int chessMap[BLOCK_GRAD_SIZE][BLOCK_GRAD_SIZE];
//储存各点的评分情况,用于之后的AI走棋
int scoreMap[BLOCK_GRAD_SIZE][BLOCK_GRAD_SIZE];

bool playFlag; //表示下棋放,true黑棋,false白棋(AI)

};
  • main.cpp中添加全局变量game
1
ChessData game;

初始化数据类型

  • chessData.h中添加函数声明
1
void initChessData(ChessData*);
  • chessData.cpp
1
2
3
4
5
6
7
void initChessData(ChessData *data){
if(!data) return;
memset(data->chessMap,0,sizeof(data->chessMap));
memset(data->scoreMap,0,sizeof(data->scoreMap));
data->playFlag = true;

}

判断有效点击

算法原理

原理

实现

  • main.cpp中添加全局变量
1
int clickPosRow, clickPosCol;	//用于储存点击位置
  • 定义函数clickBoard()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//有效点击返回true,无效点击返回false
//MOUSEMSG为鼠标信息类型
bool clickBoard(MOUSEMSG msg){

//(鼠标点击坐标 - 边界长度)/ 格宽 = 行(列)数

//计算列数并取整
int col = (msg.x - MARGIN_X) / BLOCKSIZE;
//计算行数取整
int row = (msg.y - MARGIN_Y) / BLOCKSIZE;

//计算棋子正确坐标,即格子左上角棋子应在的棋盘格线交点处坐标
int leftTopPosX = MARGIN_X + BLOCKSIZE * col;
int leftTopPosY = MARGIN_Y + BLOCKSIZE * row;


}
  • 在文件最上方引用头文件math.h用于后期计算
1
#include <math.h>
  • 之后要判断棋子应在四个交点中具体哪一点上,这里我们用一个do-while循环。继续添加**clickBoard()**函数的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int len;	//用于计算两点见的距离(根据中学所学两点间距离公式)
int selectPos = false; //作为返回值

do{
//左上角
len = sqrt((msg.x - leftTopPosX)*(msg.x - leftTopPosX) + (y - leftTopPosY)*(msg.y - leftTopPosY));
if(len < POS_OFFSET){
clickPosRow = row;
clickPosCol = col;
if ( game.chessMap[clickPosRow][clickPosCol] == 0 ){
selectPos = true;
}
break;
}

//右上角
len = sqrt((msg.x - leftTopPosX - BLOCKSIZE)*(msg.x - leftTopPosX - BLOCKSIZE) + (y - leftTopPosY)*(msg.y - leftTopPosY));
if(len < POS_OFFSET){
clickPosRow = row;
clickPosCol = col + 1;
if ( game.chessMap[clickPosRow][clickPosCol] == 0 ){
selectPos = true;
}
break;
}

//左下角
len = sqrt((msg.x - leftTopPosX)*(msg.x - leftTopPosX) + (y - leftTopPosY - BLOCKSIZE)*(msg.y - leftTopPosY - BLOCKSIZE));
if(len < POS_OFFSET){
clickPosRow = row + 1;
clickPosCol = col;
if ( game.chessMap[clickPosRow][clickPosCol] == 0 ){
selectPos = true;
}
break;
}

//右下角

len = sqrt((msg.x - leftTopPosX - BLOCKSIZE)*(msg.x - leftTopPosX - BLOCKSIZE) + (y - leftTopPosY - BLOCKSIZE)*(msg.y - leftTopPosY - BLOCKSIZE));
if(len < POS_OFFSET){
clickPosRow = row + 1;
clickPosCol = col + 1;
if ( game.chessMap[clickPosRow][clickPosCol] == 0 ){
selectPos = true;
}
break;
}


}while(0);

return selectPos;

更新底层数据

  • chessData.h
1
void updateGameMap(ChessData* data, int row, int col);
  • chessData.cpp
1
2
3
4
5
6
7
8
9
10
11
12
void updateGameMap(ChessData* data, int row, int col){
if(!data) return;

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

data->playFlag = !data->playFlag; //换下棋方

}
  • main.cpp
1
2
3
4
5
6
//玩家走棋
void manGo(){
chessDown(clickPosRow,clickPosCol,CHESS_BLACK);
updateGameMap(&game, clickPosRow, clickPosCol);

}

C++五子棋(二)——游戏界面与棋子渲染

准备

我们首先要在程序中定义一个名为drawPNG的函数,用于输出png格式图片并使背景透明

  • 引入头文件(需要提前安装EasyX)
1
#include <graphics.h>
  • 定义函数 drawPNG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void drawPNG(IMAGE* picture, int  picture_x, int picture_y) //x为载入图片的X坐标,y为Y坐标
{

// 变量初始化
DWORD* dst = GetImageBuffer(); // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
DWORD* draw = GetImageBuffer();
DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
int graphWidth = getwidth(); //获取绘图区的宽度,EASYX自带
int graphHeight = getheight(); //获取绘图区的高度,EASYX自带
int dstX = 0; //在显存里像素的角标

// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
for (int iy = 0; iy < picture_height; iy++)
{
for (int ix = 0; ix < picture_width; ix++)
{
int srcX = ix + iy * picture_width; //在显存里像素的角标
int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
int sg = ((src[srcX] & 0xff00) >> 8); //G
int sb = src[srcX] & 0xff; //B
if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
{
dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
int dr = ((dst[dstX] & 0xff0000) >> 16);
int dg = ((dst[dstX] & 0xff00) >> 8);
int db = dst[dstX] & 0xff;
draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16) //公式: Cp=αp*FP+(1-αp)*BP ; αp=sa/255 , FP=sr , BP=dr
| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8) //αp=sa/255 , FP=sg , BP=dg
| (sb * sa / 255 + db * (255 - sa) / 255); //αp=sa/255 , FP=sb , BP=db
}
}
}
}

初始化游戏

创建游戏界面

素材大小

  • 通过查看res素材文件夹下的棋盘2.jpg可知,图片大小为897*895
  • 通过图片大小计算可得一个格子的大小为67.4
  • 先记下这些参数,后面对我们开发特别重要

创建窗口

  • 首先,定义 float类型常量 BLOCKSIZE,即格子大小
1
const float BLOCKSIZE = 67.4;
  • 然后定义 init() 如下
1
2
3
4
void init(){
initgraph(897,895); //创建897*895大小的窗口,与棋盘2.jpg大小对应
loadimage(0,"res/棋盘2.jpg"); //加载图片到窗口
}

加载音乐

  • 引入播放音乐的 头文件 mmsystem.h
1
#include <mmsystem.h>
  • 加载库 winmm.lib
1
#pragma comment(lib,"winmm.lib");
  • 继续在 init() 函数中添加播放 提示语音(res/start.wav) 的语句(注意添加位置)
1
mciSendString("play res/start.wav",0,0,0);	//提示下棋语音

棋子渲染

加载素材

  • 定义 IMAGE 类型的全局变量 chessWhitechessBlack
1
2
IMAGE chessWhite; //黑棋子变量
IMAGE chessBlack; //白棋子变量
  • init() 函数定义中添加加载图片语句如下(将black.png白棋子素材white.png黑棋子素材加载到变量)
1
2
3
//长和宽都是BLOCKSIZE,最后一个true参数表示原比例缩放防止图片被截断
loadimage(&chessBlack, "res/black.png",BLOCKSIZE,BLOCKSIZE,true);
loadimage(&chessWhite, "res/white.png", BLOCKSIZE, BLOCKSIZE, true);

实现渲染

  • 定义棋子种类
1
2
3
4
typedef enum{
CHESS_WHITE = -1,
CHESS_BLACK = 1
} chess_kind_t;
  • 在实现输出棋子之前我们需要来看几个数据

  • MARGIN_X为上边界大小,MARGIN_Y为左边界大小,因此我们定义同名全局常量
1
2
3
4
const int MARGIN_X = 44;
const int MARGIN_Y = 43;

const int BOARD_GRAD_SIZE = 13; //13*13棋盘大小
  • 之后定义函数 chessDown() 用于打印棋子图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void chessDown(int row, int col, chess_kind_t kind){
mciSendString("play res/down7.wav",0,0,0); //播放下棋音乐

//EasyX是以窗口左上角为坐标原点

//定义棋子x横坐标
int x = MARGIN_X + col * BLOCKSIZE - 0.5 * BLOCKSIZE;
//定义棋子y纵坐标
int y = MARGIN_Y + row * BLOCKSIZE - 0.5 * BLOCKSIZE;

//判断棋子种类并打印
if(kind == CHESS_WHITE){
drawPNG(&chessWhite,x,y);
}else{
drawPNG(&chessBlack,x,y);
}


}

结束

到了这里,我们已经实现了游戏界面的初始化和棋子渲染了,接下来我们就要实现获取鼠标信息来判断有效点击

C++五子棋(一)——开发环境

开发环境

环境准备

  • Visual Studio
  • Windows
  • EasyX图形库

素材文件

素材文件已经准备了,点击此处获取 百度网盘链接
提取码:su6p

创建项目

  • 打开Visual Studio
  • 创建空项目
  • 创建源文件main.cpp
  • 在项目属性中的“高级”一栏里,设置使用多字节字符集
  • 导入资源(解压后的文件夹改名为res)

结束

环境搭建就说这些,不会的自行百度,接下来就要开始开发五子棋了