├── Chess_Game.pro
├── Chess_Game.pro.user
├── README.md
├── gamewindow.ui
├── img-for-readme.png
├── include
├── Chess.h
├── GameWindow.h
└── ui_gamewindow.h
├── main.cpp
└── src
├── Chess.cpp
└── GameWindow.cpp
/Chess_Game.pro:
--------------------------------------------------------------------------------
1 | QT -= gui
2 | QT += widgets
3 | CONFIG += c++17 console
4 | CONFIG -= app_bundle
5 |
6 | # You can make your code fail to compile if it uses deprecated APIs.
7 | # In order to do so, uncomment the following line.
8 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
9 |
10 | SOURCES += \
11 | main.cpp \
12 | src/Chess.cpp \
13 | src/GameWindow.cpp
14 |
15 | # Default rules for deployment.
16 | qnx: target.path = /tmp/$${TARGET}/bin
17 | else: unix:!android: target.path = /opt/$${TARGET}/bin
18 | !isEmpty(target.path): INSTALLS += target
19 |
20 | HEADERS += \
21 | include/Chess.h \
22 | include/GameWindow.h \
23 | include/ui_gamewindow.h
24 |
25 | FORMS += \
26 | gamewindow.ui
27 |
--------------------------------------------------------------------------------
/Chess_Game.pro.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | EnvironmentId
7 | {b1ba53d0-eae8-4a87-9aa0-eb8592d83d61}
8 |
9 |
10 | ProjectExplorer.Project.ActiveTarget
11 | 0
12 |
13 |
14 | ProjectExplorer.Project.EditorSettings
15 |
16 | true
17 | false
18 | true
19 |
20 | Cpp
21 |
22 | CppGlobal
23 |
24 |
25 |
26 | QmlJS
27 |
28 | QmlJSGlobal
29 |
30 |
31 | 2
32 | UTF-8
33 | false
34 | 4
35 | false
36 | 80
37 | true
38 | true
39 | 1
40 | false
41 | true
42 | false
43 | 0
44 | true
45 | true
46 | 0
47 | 8
48 | true
49 | false
50 | 1
51 | true
52 | true
53 | true
54 | *.md, *.MD, Makefile
55 | false
56 | true
57 |
58 |
59 |
60 | ProjectExplorer.Project.PluginSettings
61 |
62 |
63 | true
64 | false
65 | true
66 | true
67 | true
68 | true
69 |
70 |
71 | 0
72 | true
73 |
74 | true
75 | true
76 | Builtin.DefaultTidyAndClazy
77 | 4
78 |
79 |
80 |
81 | true
82 |
83 |
84 |
85 |
86 | ProjectExplorer.Project.Target.0
87 |
88 | Desktop
89 | Desktop Qt 6.3.1 MinGW 64-bit
90 | Desktop Qt 6.3.1 MinGW 64-bit
91 | qt.qt6.631.win64_mingw_kit
92 | 0
93 | 0
94 | 0
95 |
96 | 0
97 | D:\Chess_Game\build-Chess_Game-Desktop_Qt_6_3_1_MinGW_64_bit-Debug
98 | D:/Chess_Game/build-Chess_Game-Desktop_Qt_6_3_1_MinGW_64_bit-Debug
99 |
100 |
101 | true
102 | QtProjectManager.QMakeBuildStep
103 | false
104 |
105 |
106 |
107 | true
108 | Qt4ProjectManager.MakeStep
109 |
110 | 2
111 | Build
112 | Build
113 | ProjectExplorer.BuildSteps.Build
114 |
115 |
116 |
117 | true
118 | Qt4ProjectManager.MakeStep
119 | clean
120 |
121 | 1
122 | Clean
123 | Clean
124 | ProjectExplorer.BuildSteps.Clean
125 |
126 | 2
127 | false
128 |
129 | false
130 |
131 | Debug
132 | Qt4ProjectManager.Qt4BuildConfiguration
133 | 2
134 |
135 |
136 | D:\Chess_Game\build-Chess_Game-Desktop_Qt_6_3_1_MinGW_64_bit-Release
137 | D:/Chess_Game/build-Chess_Game-Desktop_Qt_6_3_1_MinGW_64_bit-Release
138 |
139 |
140 | true
141 | QtProjectManager.QMakeBuildStep
142 | false
143 |
144 |
145 |
146 | true
147 | Qt4ProjectManager.MakeStep
148 |
149 | 2
150 | Build
151 | Build
152 | ProjectExplorer.BuildSteps.Build
153 |
154 |
155 |
156 | true
157 | Qt4ProjectManager.MakeStep
158 | clean
159 |
160 | 1
161 | Clean
162 | Clean
163 | ProjectExplorer.BuildSteps.Clean
164 |
165 | 2
166 | false
167 |
168 | false
169 |
170 | Release
171 | Qt4ProjectManager.Qt4BuildConfiguration
172 | 0
173 | 0
174 |
175 |
176 | 0
177 | D:\Chess_Game\build-Chess_Game-Desktop_Qt_6_3_1_MinGW_64_bit-Profile
178 | D:/Chess_Game/build-Chess_Game-Desktop_Qt_6_3_1_MinGW_64_bit-Profile
179 |
180 |
181 | true
182 | QtProjectManager.QMakeBuildStep
183 | false
184 |
185 |
186 |
187 | true
188 | Qt4ProjectManager.MakeStep
189 |
190 | 2
191 | Build
192 | Build
193 | ProjectExplorer.BuildSteps.Build
194 |
195 |
196 |
197 | true
198 | Qt4ProjectManager.MakeStep
199 | clean
200 |
201 | 1
202 | Clean
203 | Clean
204 | ProjectExplorer.BuildSteps.Clean
205 |
206 | 2
207 | false
208 |
209 | false
210 |
211 | Profile
212 | Qt4ProjectManager.Qt4BuildConfiguration
213 | 0
214 | 0
215 | 0
216 |
217 | 3
218 |
219 |
220 | 0
221 | Deploy
222 | Deploy
223 | ProjectExplorer.BuildSteps.Deploy
224 |
225 | 1
226 |
227 | false
228 | ProjectExplorer.DefaultDeployConfiguration
229 |
230 | 1
231 |
232 | true
233 | true
234 | true
235 |
236 | 2
237 |
238 | Qt4ProjectManager.Qt4RunConfiguration:D:/Chess_Game/Chess_Game/Chess_Game.pro
239 | D:/Chess_Game/Chess_Game/Chess_Game.pro
240 | false
241 | true
242 | true
243 | false
244 | true
245 | D:/Chess_Game/build-Chess_Game-Desktop_Qt_6_3_1_MinGW_64_bit-Debug
246 |
247 | 1
248 |
249 |
250 |
251 | ProjectExplorer.Project.TargetCount
252 | 1
253 |
254 |
255 | ProjectExplorer.Project.Updater.FileVersion
256 | 22
257 |
258 |
259 | Version
260 | 22
261 |
262 |
263 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chess-Game-2020
2 | This is the new repo of the original project.
3 |
4 | ```Use Qt Creator 6.3 to open the project, then build and run the code.```
5 |
6 | Two years ago, I did this academic project for my c++ practice course, but there were too many "unnecessary" parts for the chess game itself, such as login or register module. Although these are redundant for the core contribution of the project, but are helpful to show my workload to get an 'A'.
7 |
8 | Therefore, I only retain the core part of the project: the alpha-beta algorithm and the chess board panel painted by Qt.
9 |
10 | I have written [a blog](https://blog.csdn.net/weixin_45157679/article/details/108405709) in Chinese.Here is the game panel:
11 |
12 |

13 |
--------------------------------------------------------------------------------
/gamewindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GameWindow
6 |
7 |
8 |
9 | 0
10 | 0
11 | 800
12 | 600
13 |
14 |
15 |
16 | MainWindow
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/img-for-readme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JitingLiu/Chess-Game-2020/7d11c4b2d308679809f9c0689dfeef59a9bd7643/img-for-readme.png
--------------------------------------------------------------------------------
/include/Chess.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include "Chess.h" //vector definition using struct position
7 |
8 | using namespace std;
9 |
10 | #define N 15 //15*15的棋盘
11 |
12 | extern const char chessBoardFlag; //棋盘标志
13 | extern const char chessFlag1; // 玩家1
14 | extern const char chessFlag2; // 玩家2的棋子标志
15 |
16 | typedef struct Position { //坐标
17 | int row; //行
18 | int col; //列
19 | }Position;
20 |
21 | extern vector vec1; //存 player1 的下棋信息
22 | extern vector vec2; //存 player2 的下棋信息
23 | extern vector vec3; //存 player1 + player2 的下棋信息
24 | extern vector vec_blank; //存未下棋的位置
25 | extern vector vec_all; //棋盘的所有位置
26 |
27 | class Chess{
28 | private:
29 | char chessBoard[N][N]; //棋盘
30 | public:
31 | Chess(); //默认构造函数
32 | Chess(char chessBoard[][N]); //构造函数
33 | Chess(const Chess& chess); //拷贝构造函数
34 | ~Chess(){} //
35 | void Init_chessboard(); //棋盘初始化函数
36 | void take_step(Position& pos, int player, char flag); //走一步
37 | int judge_postition(const Position& pos); //判断坐标的合法性
38 | int judge_victory(Position pos, char flag); //判断是否有玩家获胜
39 | char get_coordinate(int, int); //返回(x,y)的信息
40 |
41 | void init_vector(); //容器初始化
42 | void AI_chess(Position& pos, char flag); //AI走
43 | int alpha_beta(int h, int player, int alpha, int beta); //alpha-beta剪枝
44 | void order(); //离最后落子的邻居位置最有可能是最优点
45 | bool has_neightnor(vector::iterator vter); // 判断点 pt 是否有邻居点
46 | long int evaluate(int); //评估函数, 主要是评估当前棋局的得分
47 | bool match_vector(vector& temp, vector& shape); //括号是否匹配
48 | int cal_score(int m, int n, int x_direct, int y_direct, vector& enemy_list,
49 | vector& my_list); //每个方向上的分值计算
50 | };
51 |
52 |
--------------------------------------------------------------------------------
/include/GameWindow.h:
--------------------------------------------------------------------------------
1 | #ifndef GAMEWINDOW_H
2 | #define GAMEWINDOW_H
3 |
4 | #include
5 | #include "Chess.h"
6 | namespace Ui {
7 | class GameWindow;
8 | }
9 |
10 | class GameWindow : public QMainWindow
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit GameWindow(QWidget *parent = 0);
16 | ~GameWindow();
17 | void paintEvent(QPaintEvent* event); //绘图
18 | void mouseMoveEvent(QMouseEvent *event); //鼠标移动事件
19 | void mouseReleaseEvent(QMouseEvent* event); // 实际落子
20 | bool is_win(Position& pos, int player, char flag); //判断具体哪位玩家赢
21 | Chess* get_game()
22 | {
23 | return game;
24 | }
25 | private:
26 | Ui::GameWindow *ui;
27 | Chess* game; // 游戏
28 |
29 | };
30 |
31 | extern int moveX, moveY, lastx, lasty;
32 |
33 | #endif // GAMEWINDOW_H
34 |
--------------------------------------------------------------------------------
/include/ui_gamewindow.h:
--------------------------------------------------------------------------------
1 | /********************************************************************************
2 | ** Form generated from reading UI file 'gamewindow.ui'
3 | **
4 | ** Created by: Qt User Interface Compiler version 5.9.2
5 | **
6 | ** WARNING! All changes made in this file will be lost when recompiling UI file!
7 | ********************************************************************************/
8 |
9 | #ifndef UI_GAMEWINDOW_H
10 | #define UI_GAMEWINDOW_H
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | QT_BEGIN_NAMESPACE
22 |
23 | class Ui_GameWindow
24 | {
25 | public:
26 | QMenuBar *menubar;
27 | QWidget *centralwidget;
28 | QStatusBar *statusbar;
29 |
30 | void setupUi(QMainWindow *GameWindow)
31 | {
32 | if (GameWindow->objectName().isEmpty())
33 | GameWindow->setObjectName(QStringLiteral("GameWindow"));
34 | GameWindow->resize(800, 600);
35 | menubar = new QMenuBar(GameWindow);
36 | menubar->setObjectName(QStringLiteral("menubar"));
37 | GameWindow->setMenuBar(menubar);
38 | centralwidget = new QWidget(GameWindow);
39 | centralwidget->setObjectName(QStringLiteral("centralwidget"));
40 | GameWindow->setCentralWidget(centralwidget);
41 | statusbar = new QStatusBar(GameWindow);
42 | statusbar->setObjectName(QStringLiteral("statusbar"));
43 | GameWindow->setStatusBar(statusbar);
44 |
45 | retranslateUi(GameWindow);
46 |
47 | QMetaObject::connectSlotsByName(GameWindow);
48 | } // setupUi
49 |
50 | void retranslateUi(QMainWindow *GameWindow)
51 | {
52 | GameWindow->setWindowTitle(QApplication::translate("GameWindow", "MainWindow", Q_NULLPTR));
53 | } // retranslateUi
54 |
55 | };
56 |
57 | namespace Ui {
58 | class GameWindow: public Ui_GameWindow {};
59 | } // namespace Ui
60 |
61 | QT_END_NAMESPACE
62 |
63 | #endif // UI_GAMEWINDOW_H
64 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "./include/GameWindow.h"
3 |
4 | int main(int argc, char *argv[])
5 | {
6 | QApplication a(argc, argv);
7 | GameWindow w;
8 | w.show();
9 | return a.exec();
10 | }
11 |
--------------------------------------------------------------------------------
/src/Chess.cpp:
--------------------------------------------------------------------------------
1 | /*Chess.cpp 此文件为Chess类的实现*/
2 |
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "./include/Chess.h"
9 |
10 | #define inf 99999999 //无穷大值
11 | #define Depth 3 //搜索深度为3
12 |
13 | using namespace std;
14 |
15 | //give a definition
16 | const char chessBoardFlag = 'w'; // 棋盘标志
17 | const char chessFlag1 = 'o'; // 玩家1
18 | const char chessFlag2 = 'x'; // 玩家2的棋子标志
19 |
20 | //definition of vector
21 | vector vec1 = {}; //存 player1 的下棋信息
22 | vector vec2 = {}; //存 player2 的下棋信息
23 | vector vec3 = {}; //存 player1 + player2 的下棋信息
24 | vector vec_blank = {}; //存未下棋的位置
25 | vector vec_all = {}; //棋盘的所有位置
26 |
27 | Chess::Chess() //构造函数
28 | {
29 | Init_chessboard();
30 |
31 | }
32 |
33 | Chess::Chess(char chessBoard[][N]) //带参数的构造函数
34 | {
35 | for (int i = 0; i < N ; ++i)
36 | {
37 | for (int j = 0; j < N ; ++j)
38 | {
39 | this->chessBoard[i][j] = chessBoard[i][j];//赋值
40 | }
41 | }
42 |
43 | }
44 |
45 | Chess::Chess(const Chess& chess) //拷贝构造函数
46 | {
47 | for (int i = 0; i < N ; ++i)
48 | {
49 | for (int j = 0; j < N ; ++j)
50 | {
51 | this->chessBoard[i][j] = chess.chessBoard[i][j];//赋值
52 | }
53 | }
54 | }
55 |
56 | void Chess::Init_chessboard() //初始化棋盘
57 | {
58 | for (int i = 0; i < N ; ++i)
59 | {
60 | for (int j = 0; j < N ; ++j)
61 | {
62 | chessBoard[i][j] = chessBoardFlag;//每个位置初始化为空
63 | }
64 | }
65 |
66 | }
67 |
68 | void Chess::init_vector() //容器初始化
69 | {
70 | //对 vector 初始化
71 | vec_blank.clear(); //先清空
72 | vec_all.clear(); //先清空
73 | for (int i = 0; i < N; ++i)
74 | {
75 | for (int j = 0; j < N; ++j)
76 | {
77 | Position pos;
78 | pos.row = i;
79 | pos.col = j;
80 | if (chessBoard[i][j] == chessBoardFlag) //没人走
81 | {
82 | vec_blank.push_back(pos);
83 | vec_all.push_back(pos);
84 | }
85 | else if (chessBoard[i][j] == chessFlag1) //AI走的位置
86 | {
87 | vec_all.push_back(pos);
88 | }
89 | else if (chessBoard[i][j] == chessFlag2) //玩家走的位置
90 | {
91 | vec_all.push_back(pos);
92 | }
93 | }
94 | }
95 | }
96 |
97 | Position *p = new Position(); //记录next step
98 | void Chess::AI_chess(Position& pos, char flag) //AI走
99 | {
100 | init_vector();
101 | alpha_beta(1, Depth, -inf, inf);
102 | if (judge_postition(*p))
103 | {
104 | chessBoard[p->row][p->col] = chessFlag1;
105 | pos.row = p->row;
106 | pos.col = p->col;
107 | }
108 |
109 | }
110 |
111 |
112 | //负值极大算法搜索 alpha + beta剪枝, 合并极大节点和极小节点两种情况, 减少代码量
113 | //在一盘棋局中, 若到棋手A走棋, alpha 相当于棋手A得到的最好的值, 对于棋手A的值, 从对手的角度看就要取负值.
114 | int Chess::alpha_beta(int player, int h, int alpha, int beta)
115 | //h搜索深度,player=1表示AI,player=0表示人类
116 | {
117 | if (vec1.begin() != vec1.end() && vec2.begin() != vec2.end())//不空
118 | {
119 | vector::iterator pter1 = vec1.end()-1;
120 | vector::iterator pter2 = vec2.end()-1; //判断最新添加后是否赢棋
121 | if (h == 1 || judge_victory(*pter1,chessFlag1) || judge_victory(*pter2, chessFlag2))
122 | {
123 | //若到达深度 或是出现胜负
124 | return evaluate(player);
125 | }
126 | }
127 |
128 | if (vec3.begin() == vec3.end()) //尚未下棋。居中坐
129 | {
130 | vector::iterator vter = vec_blank.begin();
131 | vec_blank.erase(vter + 7 * 15 + 7); //先删除居中棋子
132 | (*p).row = 7; //第八行第八列,执先
133 | (*p).col = 7;
134 | return 0;
135 | }
136 |
137 | // vec_blank 为可以落子的集合
138 | order(); //按搜索顺序排序 提高剪枝效率
139 |
140 | //遍历每一个候选步
141 | vector::iterator vter = vec_blank.begin();
142 | while (vter != vec_blank.end())
143 | {
144 | int tempv = vter - vec_blank.begin();
145 | //如果要评估的位置没有相邻的子, 则不去评估 减少计算
146 | if (!has_neightnor(vter))
147 | {
148 | vter++;
149 | continue;
150 | }
151 |
152 | Position *pos = new Position();
153 | pos->col = (*vter).col;
154 | pos->row = (*vter).row;
155 | //判断是否为ai走棋
156 | if (player){
157 | vec1.push_back(*pos);
158 | vec_blank.erase(vter);
159 | }
160 | else{
161 | vec2.push_back(*pos);
162 | vec_blank.erase(vter); //从空白记录删除
163 | }
164 | vec3.push_back(*pos);
165 |
166 | //返回到上一层节点时,会给出分数的相反数(因为返回的值相当于是在对手的选择下对手对当前棋盘的评估分数
167 | //而作为他的对立方, 要把分数取反
168 | //alpha可以视为当前情况下,当前棋手可以得到的最好值,当前棋手得到最好值 == 对手不愿意接受的最差值
169 | //因为对手需要不断提高
170 | int value = -alpha_beta(!player, h - 1, -beta, -alpha);
171 |
172 | vector::iterator vter1 = vec1.end() - 1;
173 | vector::iterator vter2 = vec2.end() - 1;
174 | vector::iterator vter3 = vec3.end() - 1;
175 | vter = vec_blank.insert(vec_blank.begin() + tempv, *pos);
176 | //从空白记录原先位置插入
177 | //将刚才走棋移除
178 | if (player) { //是AI
179 | vec1.erase(vter1);
180 | }
181 | else{ //是玩家
182 | vec2.erase(vter2);
183 | }
184 | vec3.erase(vter3);
185 | //vec3.erase(vter); 这么写不行,超出范围
186 |
187 | delete pos;
188 |
189 | if (value > alpha)
190 | {
191 | //当depth == DEPTH时, 由于在循环内不断迭代, 总会在考虑后三步棋的情况下逐渐找到最好的走子方式;
192 | if (h == Depth) //找到路径了
193 | {
194 | p->row = (*vter).row;
195 | p->col = (*vter).col;
196 | }
197 |
198 | //alpha + beta剪枝点
199 | //当当前value > beta 时相当于 对手的 value < -alpha, 对手肯定不会考虑这个选择
200 | if (value >= beta)
201 | {
202 | //因为当前 depth中 仍需要遍历候选点, return 之后直接返回上层, 上层将返回值取负值
203 | //因为当调用该函数时alpah > - beta, 必定返回上层迭代时, value < alpha, 达到剪枝目的
204 | return beta;
205 | }
206 |
207 | alpha = value; //更新alpha值
208 |
209 | }
210 | vter++;
211 |
212 | }
213 |
214 | return alpha;
215 |
216 | }
217 |
218 |
219 | //离最后落子的邻居位置最有可能是最优点
220 | void Chess::order()
221 | {
222 | // vec3.end()-1 表示 vec3 中最后一个落子
223 |
224 | vector::iterator last_pt = vec3.end()-1; //实时添加
225 | //在最后落子附近搜索是否有可以落子的点
226 | for (int i = -1; i < 2; ++i) {
227 | for (int j = -1; j < 2; ++j) {
228 | //当 i = j = 0 是表示最后落子, 因为在其附近搜索所以舍弃 i = j = 0;
229 | if ( (i == 0 && j == 0) || (*last_pt).row + i < 0 || (*last_pt).row + i > 14||
230 | (*last_pt).col + j < 0 || (*last_pt).col + j > 14)
231 | {
232 | continue;
233 | }
234 | vector::iterator vter = vec_blank.begin();
235 | for (; vter < vec_blank.end(); vter++) {
236 | if (chessBoard[(*last_pt).row + i][(*last_pt).col + j] != chessBoardFlag)
237 | {
238 | break; //该位置不是空
239 | }
240 | //当最后落子点附近有可选点时, 先从删除, 然后从候选点列表首部插入
241 | if (((*last_pt).row + i)== (*vter).row && ((*last_pt).col + j) == (*vter).col)
242 | {
243 | vec_blank.erase(vter);
244 | Position* pos = new Position ();
245 | (*pos).row = (*last_pt).row + i;
246 | (*pos).col = (*last_pt).row + j;
247 | vec_blank.insert(vec_blank.begin(), *pos);
248 | break;
249 | }
250 | }
251 | }
252 | }
253 | }
254 |
255 | bool Chess::has_neightnor(vector::iterator pter) // 判断点 *vter 是否有邻居点
256 | {
257 | vector::iterator vter = vec3.begin();
258 | if (vter == vec3.end())
259 | {
260 | return false; //棋局尚未开始,一定没有邻居点
261 | }
262 | for (int i = -1; i < 2; ++i){
263 | for (int j = -1; j < 2; ++j){
264 | //当 i = j = 0 是表示最后落子, 因为在其附近搜索所以舍弃 i = j = 0;
265 | if ((i == 0 && j == 0) || (*pter).row + i < 0 || (*pter).row + i > 14 ||
266 | (*pter).col + j < 0 || (*pter).col + j > 14){
267 | continue;
268 | }
269 | vter = vec3.begin(); //循环前重置
270 | while (vter != vec3.end())
271 | {
272 | if (((*pter).row + i) == (*vter).row && ((*pter).col + j) == (*vter).col){
273 | return true;
274 | }
275 | vter++;
276 | }
277 |
278 | }
279 | }
280 | return false;
281 |
282 | }
283 |
284 | //棋型的评估分数, 表示当棋局为不同形状时的得分
285 | vector shape_score[15] = { {50, 0, 1, 1, 0, 0},
286 | {50, 0, 0, 1, 1, 0,},
287 | {500, 1, 1, 0, 1, 0},
288 | {500, 0, 0, 1, 1, 1},
289 | {500, 1, 1, 1, 0, 0},
290 | {500, 0, 1, 1, 1, 0},
291 | {500, 0, 1, 0, 1, 1, 0},
292 | {500, 0, 1, 1, 0, 1, 0},
293 | {50000, 1, 1, 1, 0, 1},
294 | {50000, 1, 1, 0, 1, 1},
295 | {50000, 1, 0, 1, 1, 1},
296 | {50000, 1, 1, 1, 1, 0},
297 | {50000, 0, 1, 1, 1, 1},
298 | {50000, 0, 1, 1, 1, 1, 0},
299 | {inf, 1, 1, 1, 1, 1} };
300 |
301 | int ratio = 3; // 进攻的系数 大于1 防守型, 小于1 进攻型
302 |
303 | long int Chess::evaluate(int player) //评估函数, 主要是评估当前棋局的得分
304 | {
305 | //evaluate 为启发式的评价函数,alpha + beta搜索是相当于在假设双方在同一评价函数函数情况下才有效
306 | long int total_score = 0;
307 | vector my_list;//假定初始值
308 | vector enemy_list;
309 |
310 | if (player) { //是AI方
311 | my_list = vec1;
312 | enemy_list = vec2;
313 | }
314 | else {
315 | my_list = vec2;
316 | enemy_list = vec1;
317 | }
318 |
319 | //算自己的得分
320 | long int my_score = 0;
321 |
322 | vector::iterator pter = my_list.begin();
323 | for(; pter != my_list.end(); ++pter)
324 | {
325 | int m = (*pter).row;
326 | int n = (*pter).col;
327 | my_score += cal_score(m, n, 0, 1, enemy_list, my_list);
328 | my_score += cal_score(m, n, 1, 0, enemy_list, my_list);
329 | my_score += cal_score(m, n, 1, 1, enemy_list, my_list);
330 | my_score += cal_score(m, n, -1, 1, enemy_list, my_list);
331 | }
332 |
333 | //算敌人的得分, 并减去
334 | long int enemy_score = 0;
335 |
336 | pter = enemy_list.begin();
337 | for (; pter != enemy_list.end(); ++pter)
338 | {
339 | int m = (*pter).row;
340 | int n = (*pter).col;
341 | enemy_score += cal_score(m, n, 0, 1, my_list, enemy_list);
342 | enemy_score += cal_score(m, n, 1, 0, my_list, enemy_list);
343 | enemy_score += cal_score(m, n, 1, 1, my_list, enemy_list);
344 | enemy_score += cal_score(m, n, -1, 1, my_list, enemy_list);
345 | }
346 |
347 |
348 | //本方分数 减去 敌方分数
349 | total_score = my_score - (int)(enemy_score * ratio);
350 |
351 | return total_score;
352 |
353 | }
354 |
355 |
356 |
357 | int Chess::cal_score(int m, int n, int x_direct, int y_direct, vector& enemy_list,
358 | vector& my_list) //每个方向上的分值计算
359 | {
360 | int max_score = 0; //如果没有匹配的模型返回空值
361 |
362 | //在落子点 左右方向上循环查找得分形状
363 | for (int i = -5; i < 1; ++i)
364 | {
365 | vector temp{}; //临时变量
366 | for (int j = 0; j < 6; ++j)
367 | {
368 | if (m + (i + j) * x_direct < 0 || m + (i + j) * x_direct >14 ||
369 | n + (i + j) * y_direct < 0 || n + (i + j) * y_direct > 14)//在棋盘内判断
370 | {
371 | continue; //有一个在棋盘外,
372 | }
373 | vector::iterator pter1 = enemy_list.begin();
374 | vector::iterator pter2 = my_list.begin();
375 | while (pter1 != enemy_list.end() || pter2 != my_list.end())
376 | {
377 |
378 | if (m + (i + j) * x_direct == (*pter1).row && n + (i + j) * y_direct == (*pter1).col) {
379 | //说明该位置是敌方棋子
380 | temp.push_back(2);
381 | break; // 退出,下一轮循环
382 | }
383 | else if (m + (i + j) * x_direct == (*pter2).row && n + (i + j) * y_direct == (*pter2).col) {
384 | //说明该位置是我方棋子
385 | temp.push_back(1);
386 | break;
387 | }
388 |
389 | if (pter1 == enemy_list.end()-1){
390 | if (pter2 == my_list.end()-1) {
391 | //说明该位置是双方均没有落子
392 | temp.push_back(0);
393 | break; //均为空,继续退出循环
394 | }
395 | else {
396 | pter2++;
397 | }
398 | }
399 | else {
400 | pter1++;
401 | if (pter2 != my_list.end()-1) {
402 | pter2++;
403 | }
404 | }
405 | }
406 | }
407 | if (temp.size() < 5)
408 | {
409 | continue; //凑不成五个,不用计算分数了
410 | }
411 |
412 | vector tmp_shap5 = { temp[0], temp[1], temp[2], temp[3], temp[4] }; //记录连五个的情况
413 | vector tmp_shap6 = { temp[0], temp[1], temp[2], temp[3], temp[4] }; //记录连六个的情况
414 | if (temp.size() == 6)
415 | {
416 | tmp_shap6.push_back(temp[5]);//防止越界
417 | }
418 |
419 | for (int k = 0; k < 15; k++)
420 | {
421 | if (match_vector(tmp_shap5, shape_score[k]) || match_vector(tmp_shap6, shape_score[k])) {
422 | //模型记录中五个的情况不会成为6个的齐头的字串
423 | if (shape_score[k][0] > max_score)
424 | {
425 | max_score = shape_score[k][0]; //复制
426 | }
427 |
428 | }
429 |
430 | }
431 | if (max_score == inf) {
432 | //判断出输赢了,可以结束了
433 | break;
434 | }
435 | }
436 |
437 | return max_score;
438 |
439 | }
440 |
441 | bool Chess::match_vector(vector& temp, vector& shape)
442 | {
443 | vector::iterator iter1 = temp.begin();
444 | vector::iterator iter2 = shape.begin() + 1; //第一个位置是分数
445 | while (iter1 != temp.end() && iter2 != shape.end())
446 | //只要有一个到头就不判断,模型记录中5个的情况不会成为6个的齐头的字串
447 | {
448 | if (*iter1 != *iter2)
449 | return false; //模型不匹配
450 | else{ //继续
451 | ++iter1;
452 | ++iter2;
453 | }
454 | }
455 | return true;
456 | }
457 |
458 | void Chess::take_step (Position& pos, int player, char flag)
459 | {
460 | chessBoard[pos.row][pos.col] = flag;
461 | }
462 |
463 | int Chess::judge_postition(const Position& pos) //判断坐标的合法性
464 | {
465 | //在棋盘上
466 | if (pos.row >= 0 && pos.row < N && pos.col >= 0 && pos.col < N) {
467 | //所在位置为空(没有棋子)
468 | if (chessBoard[pos.row][pos.col] == chessBoardFlag) {
469 | return 1; //合法
470 | }
471 | }
472 | return 0; //非法
473 | }
474 |
475 | int Chess::judge_victory(Position pos, char flag) //判断是否有玩家获胜
476 | {
477 | int begin = 0;
478 | int end = 0;
479 |
480 | //判断行是否满足条件
481 | (pos.col - 4) >= 0 ? begin = (pos.col - 4) : begin = 0;//往前探4个位置
482 | (pos.col + 4) >= N ? end = N - 1: end = (pos.col + 4);//往后探4个位置
483 | for (int i = pos.row, j = begin; j + 4 <= end; ++j)
484 | {
485 | if (chessBoard[i][j] == flag && chessBoard[i][j + 1] == flag &&
486 | chessBoard[i][j + 2] == flag && chessBoard[i][j + 3] == flag &&
487 | chessBoard[i][j + 4] == flag)//该棋子周围是否出现五子连珠
488 | return 1;
489 |
490 | }
491 |
492 | //判断列是否满足条件
493 | (pos.row - 4) >= 0 ? begin = (pos.row - 4) : begin = 0;
494 | (pos.row + 4) >= N ? end = N - 1: end = (pos.row + 4);
495 | for (int j = pos.col, i = begin; i + 4 <= end; ++i) {
496 | if (chessBoard[i][j] == flag && chessBoard[i + 1][j] == flag &&
497 | chessBoard[i + 2][j] == flag && chessBoard[i + 3][j] == flag &&
498 | chessBoard[i + 4][j] == flag)//该棋子周围是否出现五子连珠
499 | return 1;
500 |
501 | }
502 |
503 | //判断主对角线是否满足条件
504 | int length = 0; //相对长度
505 | int start = 0;
506 | int finish = 0;
507 | pos.row > pos.col ? length = pos.col : length = pos.row; //选择一个较小的值作限界
508 | if (length > 4)
509 | {
510 | length = 4;
511 | }
512 | begin = pos.row - length; //横坐标起始位置
513 | start = pos.col - length; //纵坐标起始位置
514 |
515 | pos.row > pos.col ? length = N - pos.row - 1 : length = N - pos.col - 1;
516 | if (length > 4)
517 | {
518 | length = 4;
519 | }
520 | end = pos.row + length; //横坐标结束位置
521 | finish = pos.col + length; //纵坐标结束位置
522 |
523 | for (int i = begin, j = start; (i + 4 <= end) && (j + 4 <= finish); ++i, ++j)
524 | {
525 | if (chessBoard[i][j] == flag && chessBoard[i + 1][j + 1] == flag &&
526 | chessBoard[i + 2][j + 2] == flag && chessBoard[i + 3][j + 3] == flag &&
527 | chessBoard[i + 4][j + 4] == flag)
528 | return 1;
529 | }
530 | //判断副对角线是否满足条件
531 | pos.row > (N - pos.col - 1) ? length = N - pos.col - 1 : length = pos.row;
532 | if (length > 4)
533 | {
534 | length = 4;
535 | }
536 | begin = pos.row - length; //横坐标起始位置
537 | start = pos.col + length; //纵坐标起始位置
538 |
539 | (N - pos.row -1) > pos.col ? length = pos.col : length = N - pos.row - 1;
540 | if (length > 4)
541 | {
542 | length = 4;
543 | }
544 | end = pos.row + length; //横坐标结束位置
545 | finish = pos.col - length; //纵坐标结束位置
546 |
547 | for (int i = begin, j = start; (i + 4 <= end) && (j - 4 >= finish); ++i, --j)
548 | {
549 | if (chessBoard[i][j] == flag && chessBoard[i + 1][j - 1] == flag &&
550 | chessBoard[i + 2][j - 2] == flag && chessBoard[i + 3][j - 3] == flag &&
551 | chessBoard[i + 4][j - 4] == flag)
552 | return 1;
553 | }
554 |
555 | //该棋局尚未开始
556 | for (int x = 0; x < N; ++x)
557 | {
558 | for (int y = 0; y < N; ++y)
559 | {
560 | if (chessBoard[x][y] == chessBoardFlag)
561 | {
562 | return 0; //未下棋
563 | }
564 | }
565 | }
566 | return -1; //和局
567 |
568 | }
569 |
570 |
571 | char Chess::get_coordinate(int x, int y) //返回(x,y)的信息
572 | {
573 | return chessBoard[x][y];
574 |
575 | }
576 |
--------------------------------------------------------------------------------
/src/GameWindow.cpp:
--------------------------------------------------------------------------------
1 | //本文件为游戏窗口
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "./include/GameWindow.h"
11 | #include "./include/ui_gamewindow.h"
12 | #include "./include/Chess.h"
13 |
14 | using namespace std;
15 |
16 | int moveX = 0, moveY = 0, lastx = 0, lasty = 0;
17 |
18 | GameWindow::GameWindow(QWidget *parent) :
19 | QMainWindow(parent),
20 | ui(new Ui::GameWindow)
21 | {
22 | ui->setupUi(this);
23 | //设置界面大小和背景
24 | game = new Chess();
25 | this->setFixedSize(660,660);//设置窗口大小
26 | setWindowTitle("五子棋");
27 | }
28 |
29 | GameWindow::~GameWindow()
30 | {
31 | delete ui;
32 | }
33 |
34 | void GameWindow::mouseReleaseEvent(QMouseEvent* event) //下棋动作
35 | {
36 | Position player1; // AI
37 | Position player2; // 玩家
38 |
39 | // 玩家执棋
40 | player2.row = moveX;
41 | player2.col = moveY;
42 | if (game->judge_postition(player2) == 1){ //判断坐标是否合法
43 | game->take_step(player2, 2, chessFlag2); //玩家走
44 | vec2.push_back(player2); //添加记录
45 | vec3.push_back(player2);
46 | }
47 | update();
48 | lastx = player2.row; //记录最后下棋的位置
49 | lasty = player2.col;
50 | // 判断输赢
51 | if (is_win(player2, 2, chessFlag2)){ //玩家
52 | game->Init_chessboard(); //清空
53 | update(); //再来一局
54 | return;
55 | }
56 |
57 | game->AI_chess(player1, chessFlag1);
58 | // 如果是人机模式,需要调用AI下棋
59 | vec1.push_back(player1); //添加记录
60 | vec3.push_back(player1);
61 | update(); // 重绘
62 | lastx = player1.row; // 记录最后下棋的位置
63 | lasty = player1.col;
64 | // 判断输赢
65 | if (is_win(player1, 1, chessFlag1)){ //AI
66 | game->Init_chessboard(); //清空
67 | update(); //再来一局
68 | }
69 |
70 |
71 | }
72 |
73 | bool GameWindow::is_win(Position& pos, int player, char flag) //判断具体哪位玩家赢
74 | {
75 | if (game->judge_victory(pos, flag) != 0)
76 | {
77 | //判断有无人获胜
78 | if (game->judge_victory(pos, flag) == 1)
79 | {
80 | QString str;
81 | //判断是否有人获胜,1表示获胜
82 | if (player == 1) {
83 | str = "Black player"; //黑子是玩家1
84 | }
85 | else { // player == 2, 是玩家2
86 | str = "White player"; //白子是玩家2
87 | }
88 | //是否再来一局
89 | QMessageBox msg(this);//对话框设置父组件
90 | msg.setWindowTitle("Congratulations!");//对话框标题
91 | msg.setText(str +" win!\nAnother Game or Not?");//对话框提示文本
92 | msg.setStandardButtons(QMessageBox::Ok | QMessageBox:: No );//对话框上的按钮
93 | if(msg.exec() == QMessageBox::Ok){ //如果再来一局
94 | update();
95 | game->Init_chessboard(); //清空
96 | }
97 | else{
98 | this->close(); //退出
99 | }
100 | }
101 | return true; //有人获胜
102 |
103 | }
104 |
105 | return false; //没人获胜 或者再来一局
106 |
107 | }
108 |
109 | void GameWindow::mouseMoveEvent(QMouseEvent *event) //鼠标移动事件
110 | {
111 | moveX = (event->position().y()-40)/40;
112 | moveY = (event->position().x()-20)/40;
113 | update(); //更新后就重画
114 | }
115 |
116 | void GameWindow::paintEvent(QPaintEvent* event)
117 | {
118 | //绘制棋盘边界边框,棋盘内颜色
119 | QPainter painter(this); //画板
120 | painter.setRenderHint(QPainter::Antialiasing,true); //抗锯齿
121 |
122 | this->setAutoFillBackground(true);
123 | QPalette palette ; //画背景色
124 | palette.setColor(QPalette::Window, QColor("#B1723C"));
125 | this->setPalette(palette);
126 |
127 | centralWidget()->setMouseTracking(true);//接受鼠标的移动
128 | setMouseTracking(true);
129 |
130 | QPen pen = painter.pen(); //画笔
131 | pen.setColor(QColor("#8D5822")); //棋盘边界边框
132 | pen.setWidth(7);
133 | painter.setPen(pen);
134 |
135 | QBrush brush; //画刷,棋盘内颜色
136 | brush.setColor(QColor("#EEC085"));
137 | brush.setStyle(Qt::SolidPattern);
138 | painter.setBrush(brush);
139 | painter.drawRect(20,40,600,600);
140 |
141 | //绘制棋盘
142 | pen.setColor(Qt::black);
143 | pen.setWidth(1);
144 | painter.setPen(pen); //黑线
145 | for(int i = 0; i < 15; ++i)
146 | {
147 | painter.drawLine(40+i*40,60,40+i*40,620);//纵线,QT的坐标60最上,620最下
148 | painter.drawLine(40,60+i*40,600,60+i*40);//横线,QT的坐标40最左,600最右
149 | }
150 |
151 | //绘制棋盘中间的五个黑点
152 | brush.setColor(Qt::black); //五个小黑点
153 | painter.setBrush(brush);
154 | painter.drawRect(115,175,10,10);
155 | painter.drawRect(475,175,10,10);
156 | painter.drawRect(155,495,10,10);
157 | painter.drawRect(475,495,10,10);
158 | painter.drawRect(315,335,10,10);
159 |
160 | //循环遍历,画棋子
161 | painter.setPen(Qt::NoPen); //去掉pen,避免画出的棋子边缘带线条
162 | for(int i=0; i < 15; ++i)
163 | {
164 | for(int j=0; j <15; ++j)
165 | {
166 | if(game->get_coordinate(i,j) == chessFlag1)// 黑子,AI
167 | {
168 | brush.setColor(Qt::black);
169 | painter.setBrush(brush);
170 | //在第i行,第j列画一个圆形
171 | painter.drawEllipse(QPoint((j+1)*40,(i+1)*40+20), 18, 18);
172 | }
173 | else if(game->get_coordinate(i,j) == chessFlag2)//白子,玩家2
174 | {
175 | brush.setColor(Qt::white);
176 | painter.setBrush(brush);
177 | painter.drawEllipse(QPoint((j+1)*40,(i+1)*40+20), 18, 18);
178 | }
179 | }
180 | }
181 |
182 | pen.setColor(Qt::red);
183 | pen.setWidth(1);
184 | painter.setPen(pen);
185 | //鼠标移动后,当前红方框标识
186 | if((moveX*40+40)>=20 && (moveX*40+40)<=620 && (moveY*40+20)>=40 && (moveY*40+20)<=640)
187 | {
188 | painter.drawLine((moveY+1)*40-20,(moveX+1)*40,(moveY+1)*40-10,(moveX+1)*40);
189 | painter.drawLine((moveY+1)*40+20,(moveX+1)*40,(moveY+1)*40+10,(moveX+1)*40);
190 | painter.drawLine((moveY+1)*40-20,(moveX+1)*40+40,(moveY+1)*40-10,(moveX+1)*40+40);
191 | painter.drawLine((moveY+1)*40+20,(moveX+1)*40+40,(moveY+1)*40+10,(moveX+1)*40+40);
192 | painter.drawLine((moveY+1)*40-20,(moveX+1)*40,(moveY+1)*40-20,(moveX+1)*40+10);
193 | painter.drawLine((moveY+1)*40+20,(moveX+1)*40,(moveY+1)*40+20,(moveX+1)*40+10);
194 | painter.drawLine((moveY+1)*40-20,(moveX+1)*40+40,(moveY+1)*40-20,(moveX+1)*40+30);
195 | painter.drawLine((moveY+1)*40+20,(moveX+1)*40+40,(moveY+1)*40+20,(moveX+1)*40+30);
196 |
197 | }
198 | //lastx,lasty记录最后一个落子的位置
199 | //画最后一个落子位置
200 |
201 | painter.drawLine((lasty+1)*40-6,(lastx+1)*40+20,(lasty+1)*40-1,(lastx+1)*40+20);
202 | painter.drawLine((lasty+1)*40+1,(lastx+1)*40+20,(lasty+1)*40+6,(lastx+1)*40+20);
203 | painter.drawLine((lasty+1)*40,(lastx+1)*40+14,(lasty+1)*40,(lastx+1)*40+19);
204 | painter.drawLine((lasty+1)*40,(lastx+1)*40+21,(lasty+1)*40,(lastx+1)*40+26);
205 |
206 |
207 | }
208 |
--------------------------------------------------------------------------------