Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
zmx0142857
GitHub Repository: zmx0142857/mini-games
Path: blob/master/c/go/gomoku.c
364 views
1
#define EXTERN_GAME_H
2
#include "main.h"
3
#include <assert.h>
4
#define INF 99999
5
6
// 棋型
7
enum GomokuType {
8
GOMOKU_LIVE_1, GOMOKU_RUSH_1, GOMOKU_SLEEP_1, GOMOKU_DEAD_1, // 活1 冲1 眠1 死1
9
GOMOKU_LIVE_2, GOMOKU_RUSH_2, GOMOKU_SLEEP_2, GOMOKU_DEAD_2,
10
GOMOKU_LIVE_3, GOMOKU_RUSH_3, GOMOKU_SLEEP_3, GOMOKU_DEAD_3,
11
GOMOKU_LIVE_4, GOMOKU_RUSH_4, GOMOKU_SLEEP_4, GOMOKU_DEAD_4,
12
GOMOKU_WIN, // 5 子连珠
13
GOMOKU_LONG // 长连
14
};
15
16
// 棋型对应的分数
17
int gomoku_score_map[] = {
18
2, 1, 1, 0,
19
10, 2, 2, 0,
20
100, 50, 5, 0,
21
1000, 500, 50, 0,
22
10000,
23
10000
24
};
25
26
// 五子棋: 获取指定方向上的棋型分数
27
// 假定 (x, y) 处未落子
28
int gomoku_line_score(int x, int y, int direction, enum Color color, int debug) {
29
static int dx[8] = { 0, 1, 1, 1 };
30
static int dy[8] = { 1, 1, 0, -1 };
31
enum Color rival_color = flip(color);
32
33
int block_l = 0, block_r = 0; // 阻挡物的位置
34
bool block_blank_l = false, block_blank_r = false; // 与阻挡物间是否有空隙
35
int blank_l = false, blank_r = false; // 是否遇到空白
36
bool skip_l = false, skip_r = false; // 己方棋子是否断开
37
int count_l = 1, count_r = 1; // 左右计数, 二者取大
38
int offset, xx, yy;
39
40
// 沿正方向看 (r)
41
for (offset = 1; offset <= 5; ++offset) {
42
xx = x + dx[direction] * offset;
43
yy = y + dy[direction] * offset;
44
if (!bounded(xx, yy) || board[xx][yy] == rival_color) {
45
block_r = offset;
46
break;
47
} else if (board[xx][yy] == BLANK) {
48
block_blank_r = true;
49
++blank_r;
50
} else { // board[xx][yy] == color
51
block_blank_r = false;
52
if (blank_r == 0) {
53
++count_r;
54
++count_l;
55
}
56
if (blank_r == 1) {
57
skip_r = true;
58
++count_r;
59
}
60
}
61
}
62
63
// 沿反方向看 (l)
64
for (offset = -1; offset >= -5; --offset) {
65
xx = x + dx[direction] * offset;
66
yy = y + dy[direction] * offset;
67
if (!bounded(xx, yy) || board[xx][yy] == rival_color) {
68
block_l = offset;
69
break;
70
} else if (board[xx][yy] == BLANK) {
71
block_blank_l = true;
72
++blank_l;
73
} else { // board[xx][yy] == color
74
block_blank_l = false;
75
if (blank_l == 0) {
76
++count_l;
77
++count_r;
78
}
79
if (blank_l == 1) {
80
skip_l = true;
81
++count_l;
82
}
83
}
84
}
85
86
bool dead_l = block_l && !block_blank_l;
87
bool dead_r = block_r && !block_blank_r;
88
if (block_l == 0) block_l = -INF;
89
if (block_r == 0) block_r = INF;
90
91
int type = 0;
92
int count = count_l > count_r ? count_l : count_r;
93
int skip = count_l > count_r ? skip_l
94
: count_l < count_r ? skip_r
95
: skip_l < skip_r ? skip_l
96
: skip_r;
97
if (count <= 4) {
98
type += (count - 1) * 4;
99
if (block_r - block_l > 5) { // 有足够空隙
100
if (dead_l || dead_r) { // 左右是否堵死
101
type += 1; // RUSH
102
} else {
103
type += 0; // LIVE
104
}
105
} else { // 没有足够空隙
106
if (block_blank_l || block_blank_r) { // 是否留有空白
107
type += 2; // SLEEP
108
} else {
109
type += 3; // DEAD
110
}
111
}
112
} else if (count == 5) {
113
type = GOMOKU_WIN;
114
} else { // count > 5
115
type = GOMOKU_LONG;
116
}
117
118
int score = gomoku_score_map[type];
119
if (skip == 0) {
120
score += 10;
121
}
122
if (debug > 0) {
123
mvprint(MSG_LINE + debug + direction, 0,
124
"type: %d, skip: %d, skip_l: %d, skip_r: %d, count_l: %d, count_r: %d, score: %d", type, skip, skip_l, skip_r, count_l, count_r, score
125
);
126
}
127
return score;
128
}
129
130
// 五子棋: 获取指定方向上的连续棋子数
131
int gomoku_line_count(int x, int y, int direction, enum Color color) {
132
static int dx[8] = { 0, 1, 1, 1 };
133
static int dy[8] = { 1, 1, 0, -1 };
134
int count = 1, offset, xx, yy;
135
// 沿正方向看
136
for (offset = 1; offset < 5; ++offset) {
137
xx = x + dx[direction] * offset;
138
yy = y + dy[direction] * offset;
139
if (!bounded(xx, yy) || board[xx][yy] != color) break;
140
++count;
141
}
142
// 沿反方向看
143
for (offset = -1; offset > -5; --offset) {
144
xx = x + dx[direction] * offset;
145
yy = y + dy[direction] * offset;
146
if (!bounded(xx, yy) || board[xx][yy] != color) break;
147
++count;
148
}
149
return count;
150
}
151
152
// 五子棋: 获取位置 (x, y) 的分数
153
int gomoku_ai_score(int x, int y)
154
{
155
int score = 0;
156
for (int i = 0; i < 4; ++i) {
157
score += gomoku_line_score(x, y, i, color, 0);
158
score += gomoku_line_score(x, y, i, flip(color), 0);
159
}
160
return score;
161
}
162
163
// 五子棋: 遍历所有可能的落子位置, 计算最高分
164
void gomoku_ai_next_move(int *ret_x, int *ret_y)
165
{
166
int best_score = -INF;
167
int best_x = -INF;
168
int best_y = -INF;
169
int best_count = 0;
170
171
for (int x = 0; x < WIDTH; ++x) {
172
for (int y = 0; y < HEIGHT; ++y) {
173
if (board[x][y] == BLANK) {
174
int score = gomoku_ai_score(x, y);
175
if (score > best_score) {
176
best_score = score;
177
best_count = 1;
178
best_x = x;
179
best_y = y;
180
}
181
if (score == best_score) {
182
++best_count;
183
// 第 n 个最优解以 1/n 的概率取代当前最优解
184
if (rand() % best_count == 0) {
185
best_x = x;
186
best_y = y;
187
}
188
}
189
}
190
}
191
}
192
193
assert(best_score != -INF);
194
195
*ret_x = best_x;
196
*ret_y = best_y;
197
198
// for (int i = 0; i < 4; ++i) {
199
// gomoku_line_score(best_x, best_y, i, color, 1);
200
// gomoku_line_score(best_x, best_y, i, flip(color), 5);
201
// }
202
}
203
204
// 五子棋: 检查各个方向, 若游戏结束, 返回 true
205
bool gomoku_check_directions(int x, int y)
206
{
207
for (int i = 0; i < 4; ++i) {
208
int count = gomoku_line_count(x, y, i, color);
209
if (count >= 5) return true;
210
}
211
return false;
212
}
213
214
// 五子棋: 下一步棋: 若游戏结束, 返回 true
215
bool gomoku_step(int x, int y)
216
{
217
set_board(x, y, color);
218
if (gomoku_check_directions(x, y)) {
219
MSG(color == BLACK ? "黑胜" : "白胜");
220
return true;
221
}
222
color = flip(color);
223
return false;
224
}
225
226
// 五子棋: 若游戏结束, 返回 true
227
bool rule_gomoku(int x, int y)
228
{
229
if (gomoku_step(x, y)) return true;
230
if (!is_ai) return false;
231
gomoku_ai_next_move(&x, &y);
232
if (gomoku_step(x, y)) return true;
233
return false;
234
}
235
236