[杂项项目]C语言打造智能AI小游戏——三子棋

三子棋具体就不多介绍了,AI棋用O表示,我们的棋用X表示。先手后手自己选。这里主要说一下AI算法。首先AI会对整个棋盘MAP[3],[3]进行遍历,每个格子都有一个权值weight[i][j]。权值的第一次计算方法是对每一行i每一列j进行计算,计算基于这一格有几种获胜的路线。例如没有走时,9个格的权值为:

[] [] [] 3 2 3
[] [] [] 2 4 2
[] [] [] 3 2 3

角落有横竖斜三种获胜方法,边有横竖两种方法,中心有横竖两斜4种方法。假如某格有X阻挡,那么权值会相应变化,例如

[] [] [] 2 1 2
[] X [] 1 0 1
[] [] [] 2 1 2

由于有中心阻挡,那么角只有横竖两种方法赢,因此权值为2。但该AI还需继续优化。例如以下:

[] X [] 1 0 1
[] 0 [] 1 0 2
X X 0 0 0 0

AI面对这种情况,本来下在第一个格子就赢了,但他会选择权值为2的格子。因此我们可以在现有权值条件的情况下,在附加个权值。假如某一格子的路线上已经有AI的子了,那么该条路线上未落子的所有格子权值再加1,如果有两个已落子的AI格子权值就加2。例如以下情况:

原权值 优化
[] X [] 2 0 2 5 0 3
[] 0 [] 2 0 2 3 0 4
[] X 0 2 0 0 3 0 0

第一个格子5是由于斜路线上有两个子,那在原有的基础2上先加1,再加2,所以权值为2+1+2。第2行第3列权值为4,因为横竖两条路线均有1个子,因此权值为2+1+1,其余格子路线上均有1个子,因此权值为2+1。这样落在权值5那个格子上AI就赢了。

但是仅仅这些判断是不够的。例如:

原权值 优化
X [] X 0 1 0 0 2 0
[] 0 [] 1 0 1 2 0 2
[] [] 0 1 2 0 2 4 0

AI会选择权值为4的格子。那此时AI选择权值为2的格子时,你只需要走一步就赢了,AI就输了,因此该AI算法还得继续。

那么AI可以逆向思考一番,AI可以考虑一下当前情况在计算AI权值的同时再计算一次我们的权值。例如上面例子:

AI
X [] X 0 1 0 0 1 0
[] 0 [] 1 0 1 1 0 0
[] [] 0 1 2 0 1 0 0
优化AI 优化你
0 2 0 0 4 0
2 0 2 2 0 0
2 4 0 2 0 0

我们把两个权值相加(另外把AI的权值应该再加1,因为AI判断赢要优先于输或平,假如AI方有权值大的,代表一步能赢的,我方也有这种情况,那相加权值相同,AI既可以选择赢棋那一格,也可以选择堵你将要赢的那一格。但AI判断赢要优先于堵对方赢。例子就不举了):

相加权 加1权
X [] X 0 6 0 0 7 0
[] 0 [] 4 0 2 5 0 3
[] [] 0 4 4 0 5 4 0

因此AI判断到这里就会堵你了。

AI算法就介绍到这里,至于三子棋程序,由于过于简单,就不过多介绍了。主要介绍AI思想,Windows系统编译的程序。如果是Linux需要把以下代码改下。接下来我就贴上代码,供参考,如有不懂欢迎留言。

project.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once  
#define COLUMN 3
#define ROW 3
#define GOON 0
#define WIN -1
#define DEFEAT 1
void init(char(*MAP)[COLUMN]);
int victory(char(*MAP)[COLUMN]);
bool check(char(*MAP)[COLUMN]);
void show(char(*MAP)[COLUMN]);
void check_stemp(char(*MAP)[COLUMN], int *x, int *y);
void ai_auto(char(*MAP)[COLUMN]);
int menu();
int first_hand(char(*MAP)[COLUMN]);
int ai_hand(char(*MAP)[COLUMN]);
void play(char(*MAP)[COLUMN]);

project.c

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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include<stdio.h>  
#include<stdlib.h>
#include<string.h>
#include"project.h"
int vis[ROW][COLUMN];
int weight[ROW][COLUMN];
int x, y;
void init(char(*MAP)[COLUMN]) {
for (int i = 0; i < ROW; i++)
for (int j = 0; j < COLUMN; j++) {
MAP[i][j] = ' ';
vis[i][j] = 0;
}
}
int victory(char(*MAP)[COLUMN]) {
for (int i = 0; i < 3; i++) {
if (MAP[i][0] == 'X'&&MAP[i][1] == 'X'&&MAP[i][2] == 'X')
return WIN;
else if (MAP[i][0] == 'O'&&MAP[i][1] == 'O'&&MAP[i][2] == 'O')
return DEFEAT;
if (MAP[0][i] == 'X'&&MAP[1][i] == 'X'&&MAP[2][i] == 'X')
return WIN;
else if (MAP[0][i] == 'O'&&MAP[1][i] == 'O'&&MAP[2][i] == 'O')
return DEFEAT;
}
if (MAP[0][0] == 'X'&&MAP[1][1] == 'X'&&MAP[2][2] == 'X')
return WIN;
else if (MAP[0][0] == 'O'&&MAP[1][1] == 'O'&&MAP[2][2] == 'O')
return DEFEAT;
if (MAP[0][2] == 'X'&&MAP[1][1] == 'X'&&MAP[2][0] == 'X')
return WIN;
else if (MAP[0][2] == 'O'&&MAP[1][1] == 'O'&&MAP[2][0] == 'O')
return DEFEAT;
return GOON;
}
bool check(char(*MAP)[COLUMN]) {
for (int i = 0; i < ROW; i++)
for (int j = 0; j < COLUMN; j++)
if (!vis[i][j])
return false;
return true;
}
void show(char(*MAP)[COLUMN]) {
printf("-------------\n");
for (int i = 0; i < ROW; i++) {
printf("|");
for (int j = 0; j < COLUMN; j++) {
printf(" %c ", MAP[i][j]);
printf("|");
}
printf("\n");
printf("-------------\n");
}
}
void check_stemp(char(*MAP)[COLUMN], int *x, int *y) {
while (*x > 3 || *x <= 0 || *y>3 || *y <= 0 || MAP[*y - 1][*x - 1] != ' ') {
system("cls");
show(MAP);
printf("Input error, please reenter!\n");
scanf("%d%d", x, y);
}
MAP[*y - 1][*x - 1] = 'X';
vis[*y - 1][*x - 1] = 1;
}
void Empowerment(char(*MAP)[COLUMN], int(*weight)[COLUMN], char ai_X, char ai_0, int flag) {
for (int i = 0; i < 3; i++) {
if (MAP[i][0] != ai_X&&MAP[i][1] != ai_X&&MAP[i][2] != ai_X) {
weight[i][0]++;
weight[i][1]++;
weight[i][2]++;
int tmp = 1;
for (int j = 0; j < 3; j++)
if (MAP[i][j] == ai_0) {
weight[i][0] += tmp+flag;
weight[i][1] += tmp+flag;
weight[i][2] += tmp+flag;
tmp += 10;
}
}
if (MAP[0][i] != ai_X&&MAP[1][i] != ai_X&&MAP[2][i] != ai_X) {
weight[0][i]++;
weight[1][i]++;
weight[2][i]++;
int tmp = 1;
for (int j = 0; j < 3; j++)
if (MAP[j][i] == ai_0) {
weight[0][i] += tmp+flag;
weight[1][i] += tmp+flag;
weight[2][i] += tmp+flag;
tmp += 10;
}
}
}
if (MAP[0][0] != ai_X&&MAP[1][1] != ai_X&&MAP[2][2] != ai_X) {
weight[0][0]++;
weight[1][1]++;
weight[2][2]++;
int tmp = 1;
for (int j = 0; j < 3; j++)
if (MAP[j][j] == ai_0) {
weight[0][0] += tmp+flag;
weight[1][1] += tmp+flag;
weight[2][2] += tmp+flag;
tmp += 10;
}

}
if (MAP[0][2] != ai_X&&MAP[1][1] != ai_X&&MAP[2][0] != ai_X) {
weight[0][2]++;
weight[1][1]++;
weight[2][0]++;
int tmp = 1;
for (int j = 0; j < 3; j++)
if (MAP[j][2 - j] == ai_0) {
weight[0][2] += tmp+flag;
weight[1][1] += tmp+flag;
weight[2][0] += tmp+flag;
tmp += 10;
}
}
}
void ai_auto(char(*MAP)[COLUMN]) {
int weight[3][3] = { 0 };
Empowerment(MAP, weight, 'X', 'O', 1);
Empowerment(MAP, weight, 'O', 'X', 0);
int maxn = -1;
int maxm = -1;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (vis[i][j])
weight[i][j] = -1;
if (maxn < weight[i][j]) {
maxn = weight[i][j];
maxm = i * 3 + j;
}
}
MAP[maxm / 3][maxm % 3] = 'O';
vis[maxm / 3][maxm % 3] = 1;
}
int menu() {
int tmp;
printf("*************************************************************\n");
printf("* Please enter: * 1:First hand * 2.Back hand * Others:Close *\n");
printf("*************************************************************\n");
scanf("%d", &tmp);
return tmp;
}
int first_hand(char(*MAP)[COLUMN]) {
system("cls");
show(MAP);
printf("Please enter:x y\n");
scanf("%d%d", &x, &y);
check_stemp(MAP, &x, &y);
system("cls");
show(MAP);
if (victory(MAP) == WIN) {
printf("Congratulations on your victory, you are as smart as Qianyouyou.\n");
return 1;
}
else if (check(MAP)) {
printf("Draw.As smart as me.\n");
return 1;
}
system("pause");
return 0;
}
int ai_hand(char(*MAP)[COLUMN]) {
ai_auto(MAP);
system("cls");
show(MAP);
if (victory(MAP) == DEFEAT) {
printf("You lose, you're as stupid as a pig.\n");
return 1;
}
else if (check(MAP)) {
printf("Draw.As smart as me.\n");
return 1;
}
return 0;
}
void play(char(*MAP)[COLUMN]) {
while (!check(MAP) || victory(MAP) == GOON) {
if (first_hand(MAP))
break;
if (ai_hand(MAP))
break;
}
system("pause");
}

main.c

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
#include<stdio.h>  
#include<stdlib.h>
#include"project.h"
char MAP[ROW][COLUMN];
int main() {
char str[2];
do {
init(MAP);
switch (menu()) {
case 1:
first_hand(MAP);
case 2: {
ai_hand(MAP);
play(MAP);
}
default: {
printf("Do you need to start again?(Y/N)");
scanf("%s", str);
}break;
}
} while (str[0] == 'Y' || str[0] == 'y');
printf("Thank you for using!\n");
system("pause");
return 0;
}

文章结束了,但我们的故事还在继续
坚持原创技术分享,您的支持将鼓励我继续创作!