参考了B站UP主比特鹏哥的教程视频,加入了自己的学习笔记和理解。

链接:https://www.bilibili.com/video/BV1oi4y1g7CF?t=464&p=17


游戏框架

在做游戏之前需要建立一个游戏实现基本框架,那么三子棋游戏实现的框架机制是怎么样的呢?

  1. 游戏菜单
    • 系统打印游戏菜单
    • 玩家选择是否开始游戏
    • 开始游戏进入游戏内容
    • 退出游戏,游戏结束
  2. 游戏内容
    • 初始化打印棋盘
    • 玩家选择落子坐标
      • 判断玩家输入坐标的合法性
        • 坐标是否溢出
        • 原有坐标位置是否已有棋子
      • 打印落子后的棋盘
      • 判断输赢
    • 电脑选择落子坐标
      • 坐标值伪随机数输入的方法
      • 打印落子后的棋盘
      • 判断输赢
  3. 游戏规则
    • 胜利:率先使得三行||三列||对角线相同棋满为胜
    • 平局:棋盘已满但没有双方棋子均没有满足上述条件判为平局

创建工程

  创建一个新的工程,新建工程文件,工程文件包括以下三个:

  头文件 game.h   功能函数C文件 function.c   游戏执行C文件 game.c


头文件

头文件包括了宏定义,库函数的引用以及功能函数的声明。下面是头文件game.h的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//宏定义
#define _CRT_SECURE_NO_WARNINGS 1//vs中scanf函数的老毛病,懂得都懂
#define ROW 3//宏定义行数为3
#define COL 3//宏定义列数为3

//引用库函数
#include<stdio.h>//这个库函数里包含格式化输入输出
#include<stdlib.h>//这个库函数里包含伪随机数的生成

//函数声明
void InitBoard(char board[ROW][COL], int row, int col);//初始化棋盘功能函数的声明
void DisplayBoard(char board[ROW][COL], int row, int col); //棋盘打印功能函数的声明
void PlayerMove(char board[ROW][COL], int row, int col); //玩家输入功能函数的声明
void ComputerMove(char board[ROW][COL], int row, int col);//电脑输入函数的声明
char Win(char board[ROW][COL], int row, int col);//胜负判断功能函数的声明
char Full(char board[ROW][COL], int row, int col);//棋盘已满,判断平局功能函数的声明

游戏菜单

emmm,游戏菜单没什么好说的,下一个。

1
2
3
4
5
6
7
//游戏菜单
void menu(void)
{
printf("****************************************\n");
printf("*****1.开始游戏 0.退出游戏*****\n");
printf("****************************************\n");
}

菜单执行

完成上一步之后,就立刻执行菜单了,要求用户选择。

输入1是执行game函数

输入0是退出游戏

其他输入,呃…..警告处分,拉出去斩了。

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
void test(void)
{
int input = 0;
do
{
menu();//执行游戏菜单
printf("\n");//换个行,好看点(狗头)
printf("系统提示:请选择:>>>");//系统提示
scanf("%d", &input);//读取用户输入,存放到input里
printf("\n");
switch (input)//switch,懂得都懂,不解释了
{
case 1:
game();//跳转执行game函数功能
break;
case 0:
printf("破游戏,俺不玩了\n");
break;
default:
printf("电脑:FBI warning 非法输入,非法输入!!!请您自觉点\n\n");
printf("电脑乖,咱重新输入!!!\n\n");
break;
}
} while (input);
}

游戏执行

上一步玩家输入1后就调用game函数啦,所以在他调用之前就要写好game函数哦,意思是game函数放到调用之前。当然你如果提前声明了,也不要紧。(就比如,emmm,我声明了我有女朋友,但是还没有找到,编译器你不要给我报错)

下面分析整个游戏执行过程:

游戏开始执行——>初始化棋盘InitBoard(棋盘里所有的值都是0)——>打印棋盘DisplayBoard(打印简陋的棋盘框框)——>玩家下棋PlayerMove——>打印棋盘DisplayBoard(打印玩家下玩后的棋盘)——>判断玩家是否胜利Win(每次都要判断,有点浪费了,后期可优化)——>电脑下棋ComputerMove(电脑自动下棋,采用的是伪随机数,后期可优化)——>打印棋盘DisplayBoard(打印电脑下棋后的棋盘)——>判断电脑是否胜利Win(同样每次都要判断)——>打印胜负情况(根据上面的返回值打印游戏胜负情况,返回*玩家胜利,返回#电脑胜利,返回Q平局,返回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
void game(void)
{
char ret = 0;//申请一个变量来存放返回值
char board[ROW][COL] = { 0 };//申请并初始化数组

//初始化棋盘
InitBoard(board, ROW, COL);//初始化棋盘,有空格占位

//打印棋盘
DisplayBoard(board, ROW, COL);//打印设计好的棋盘

while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);//执行玩家下棋函数
DisplayBoard(board, ROW, COL);//打印玩家下棋后的棋盘结果

//判断玩家是否胜利
ret = Win(board, ROW, COL);//执行win函数,把返回值赋值到ret中
//根据返回值判断是否要继续
if (ret != 'C')
{
break;
}

//电脑下棋
ComputerMove(board, ROW, COL);//执行电脑下棋函数
DisplayBoard(board, ROW, COL);//打印电脑下棋后的棋盘结果

//判断电脑是否胜利
ret = Win(board, ROW, COL);//执行win函数,把返回值赋值到ret中
//根据返回值判断是否要继续
if (ret != 'C')
{
break;
}
}
//根据返回值的不同,打印相应的游戏结果。
if (ret == '*')
{
printf("恭喜你赢啦!!!(不过我是随机下的,我的算法没有任何技术含量(狗头狗头狗头)\n");
printf("\n");
}
else if (ret == '#')
{
printf("你输了!!!你真笨!!!你是个憨憨吧!\n");
printf("\n");
}
else
{
printf("平局!!!你真笨!!!略略略!\n");
printf("\n");
}
}

执行三子棋

1
2
3
4
5
int main(void)//引用主函数,程序的入口
{
test();//执行游戏
return 0;
}

功能函数

上面用到的所有函数都要在function.c中写好。下面把写好的函数放这里吧。算法原理我写在注释里了。

初始化棋盘

1
2
3
4
5
6
7
8
9
10
11
12
13
#include"game.h"//引用头文件

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)//定义InitBoard初始化棋盘函数
{
for (int i = 0; i < row; i++)//for循环,不解释,懂得都懂
{
for (int j = 0; j < col; j++)
{
board[i][j] = ' ';//数组中每个元素都用空格代替
}
}
}

打印棋盘

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 DisplayBoard(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)//定义了i变量
{
//打印行,外层循环执行一次换一行
for (int j = 0; j < col; j++)//定义了j变量
{
if (j < col - 1)//棋盘特殊要求,因为棋盘最右边没有|
{
printf(" %c |", board[i][j]);
}
else
printf(" %c ", board[i][j]);//打印最后一列
}
printf("\n");

//打印列,外层循环执行一次换一行
if (i < row - 1)//控制最后一行, 打印行中不需要这个特殊结构
{
for (int k = 0; k < col; k++)//定义了k变量
{
if (k < col - 1)
{
printf("---|");//同上
}
else
{
printf("---");
}

}
}
printf("\n");
}
}

玩家输入

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
//玩家输入
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("系统提示:请输入您要下的坐标:>>>");
scanf("%d%d", &x, &y);//获取玩家坐标
printf("\n");
//判断xy是否合法,一个是是否溢出,一个是是否被占用
if (x > 0 && x < row + 1 && y>0 && y < col + 1)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';//如果是空格就替换成*
break;
}
else
{
printf("电脑:这我下过了,你个憨憨\n");//占用判断
printf("\n");

}
}
else
{
printf("电脑:FBI warning 非法输入,非法输入!!!请您自觉点\n\n");//非法输入
printf("电脑:乖,咱重新输入!!!\n\n");
}
}
}

电脑输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//电脑输入
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑:我走过啦!!!到你啦!!!\n\n");
while (1)
{
x = rand() % row;//库函数中的伪随机数,需要引用头文件
y = rand() % col;//同上
if (board[x][y] == ' ')
{
board[x][y] = '#';//如果是空格就替换成#
break;
}
}
}

胜负判断

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
//判断胜负
char Win(char board[ROW][COL], int row, int col)
{
int i = 0;
//判断行
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')//行元素一样且不是空格
{
return board[i][1];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')//列元素一样且不是空格
{
return board[1][i];
}
}
//判断对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')//对角线元素一样且不是空格
{
return board[1][1];
}
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
{
return board[1][1];
}
//判断是否平局
if (1 == Full(board, ROW, COL))//平局判断,函数在下面,已在头文件中声明,位置就无所谓了
{
return 'Q';
}
return 'C';
}

平局算法

1
2
3
4
5
6
7
8
9
10
11
12
13
//平局算法函数
char Full(char board[ROW][COL], int row, int col)//棋盘满了还没达到上面的条件,就算平局啦!
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}

完整程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//宏定义
#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 3
#define COL 3

//引用库函数
#include<stdio.h>
#include<stdlib.h>

//函数声明
void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
void PlayerMove(char board[ROW][COL], int row, int col);
void ComputerMove(char board[ROW][COL], int row, int col);
char Win(char board[ROW][COL], int row, int col);
char Full(char board[ROW][COL], int row, int col);
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
#include"game.h"

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}

//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (j < col - 1)
{
printf(" %c |", board[i][j]);
}
else
printf(" %c ", board[i][j]);
}
printf("\n");
if (i < row - 1)
{

for (int k = 0; k < col; k++)
{
if (k < col - 1)
{
printf("---|");
}
else
{
printf("---");
}
}
}
printf("\n");
}
}


//玩家输入
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("系统提示:请输入您要下的坐标:>>>");

scanf("%d%d", &x, &y);
printf("\n");
//判断xy是否合法,一个是是否溢出,一个是是否被占用
if (x > 0 && x < row + 1 && y>0 && y < col + 1)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("电脑:这我下过了,你个憨憨。\n");
printf("\n");

}
}
else
{
printf("电脑:FBI warning 非法输入,非法输入!!!请您自觉点\n\n乖,咱重新输入!!!\n\n");
}
}
}

//电脑输入
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑:我走过啦!!!到你啦!!!\n\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}

//判断胜负
char Win(char board[ROW][COL], int row, int col)
{
int i = 0;
//判断行
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
}
//判断对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
{
return board[1][1];
}
//判断是否平局
if (1 == Full(board, ROW, COL))
{
return 'Q';
}
return 'C';
}
//平局算法函数
char Full(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}

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
#include"game.h"

//游戏菜单
void menu(void)
{
printf("****************************************\n");
printf("*****1.开始游戏 0.退出游戏*****\n");
printf("****************************************\n");
}

void game(void)
{
char ret = 0;
char board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断玩家是否胜利
ret = Win(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断电脑是否胜利
ret = Win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}

if (ret == '*')
{
printf("恭喜你赢啦!!!(不过我是随机下的,我的算法没有任何技术含量(狗头狗头狗头))\n");
printf("\n");
}
else if (ret == '#')
{
printf("你输了!!!你真笨!!!你是个憨憨吧!\n");
printf("\n");
}
else
{
printf("平局!!!你真笨!!!略略略!\n");
printf("\n");
}
}

void test(void)
{
int input = 0;
do
{
menu();
printf("\n");
printf("系统提示:请选择:>>>");
scanf("%d", &input);
printf("\n");
switch (input)
{
case 1:
game();
break;
case 0:
printf("破游戏,俺不玩了。\n");
break;
default:
printf("FBI warning 非法输入,非法输入,请您自觉点。\n\n乖,咱重新输入!!!\n");
break;
}
} while (input);
}

int main(void)
{
test();
return 0;
}

游戏测试

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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
****************************************
*****1.开始游戏 0.退出游戏*****
****************************************

系统提示:请选择:>>>6

FBI warning 非法输入,非法输入,请您自觉点。

乖,咱重新输入!!!
****************************************
*****1.开始游戏 0.退出游戏*****
****************************************

系统提示:请选择:>>>1

| |
---|---|---
| |
---|---|---
| |

系统提示:请输入您要下的坐标:>>>2 2

| |
---|---|---
| * |
---|---|---
| |

电脑:我走过啦!!!到你啦!!!

| |
---|---|---
| * |
---|---|---
| | #

系统提示:请输入您要下的坐标:>>>3 3

电脑:这我下过了,你个憨憨。

系统提示:请输入您要下的坐标:>>>1 1

* | |
---|---|---
| * |
---|---|---
| | #

电脑:我走过啦!!!到你啦!!!

* | |
---|---|---
| * |
---|---|---
| # | #

系统提示:请输入您要下的坐标:>>>6 6

电脑:FBI warning 非法输入,非法输入!!!请您自觉点

乖,咱重新输入!!!

系统提示:请输入您要下的坐标:>>>1 3

* | | *
---|---|---
| * |
---|---|---
| # | #

电脑:我走过啦!!!到你啦!!!

* | | *
---|---|---
| * | #
---|---|---
| # | #

系统提示:请输入您要下的坐标:>>>2 1

* | | *
---|---|---
* | * | #
---|---|---
| # | #

电脑:我走过啦!!!到你啦!!!

* | | *
---|---|---
* | * | #
---|---|---
# | # | #

你输了!!!你真笨!!!你是个憨憨吧!

****************************************
*****1.开始游戏 0.退出游戏*****
****************************************

系统提示:请选择:>>>1

| |
---|---|---
| |
---|---|---
| |

系统提示:请输入您要下的坐标:>>>1 1

* | |
---|---|---
| |
---|---|---
| |

电脑:我走过啦!!!到你啦!!!

* | |
---|---|---
| # |
---|---|---
| |

系统提示:请输入您要下的坐标:>>>1 2

* | * |
---|---|---
| # |
---|---|---
| |

电脑:我走过啦!!!到你啦!!!

* | * | #
---|---|---
| # |
---|---|---
| |

系统提示:请输入您要下的坐标:>>>3 1

* | * | #
---|---|---
| # |
---|---|---
* | |

电脑:我走过啦!!!到你啦!!!

* | * | #
---|---|---
| # |
---|---|---
* | | #

系统提示:请输入您要下的坐标:>>>2 1

* | * | #
---|---|---
* | # |
---|---|---
* | | #

恭喜你赢啦!!!(不过我是随机下的,我的算法没有任何技术含量(狗头狗头狗头))

****************************************
*****1.开始游戏 0.退出游戏*****
****************************************

系统提示:请选择:>>>1

| |
---|---|---
| |
---|---|---
| |

系统提示:请输入您要下的坐标:>>>1 1

* | |
---|---|---
| |
---|---|---
| |

电脑:我走过啦!!!到你啦!!!

* | # |
---|---|---
| |
---|---|---
| |

系统提示:请输入您要下的坐标:>>>2 1

* | # |
---|---|---
* | |
---|---|---
| |

电脑:我走过啦!!!到你啦!!!

* | # |
---|---|---
* | |
---|---|---
# | |

系统提示:请输入您要下的坐标:>>>3 3

* | # |
---|---|---
* | |
---|---|---
# | | *

电脑:我走过啦!!!到你啦!!!

* | # |
---|---|---
* | # |
---|---|---
# | | *

系统提示:请输入您要下的坐标:>>>3 2

* | # |
---|---|---
* | # |
---|---|---
# | * | *

电脑:我走过啦!!!到你啦!!!

* | # |
---|---|---
* | # | #
---|---|---
# | * | *

系统提示:请输入您要下的坐标:>>>1 3

* | # | *
---|---|---
* | # | #
---|---|---
# | * | *

平局!!!你真笨!!!略略略!

****************************************
*****1.开始游戏 0.退出游戏*****
****************************************

系统提示:请选择:>>>0

破游戏,俺不玩了

J:\code\三子棋\Debug\三子棋.exe (进程 24452)已退出,代码为 0
按任意键关闭此窗口. . .

写在后面

上面的C语言小程序实现了三子棋,但是还有好多可以优化的地方,比如外观,电脑算法,还有可不可以实现四子棋、五子棋、多子棋呢,所以这个程序可优化的地方还有很多。

(伪随机数不随机哇,有没有什么好的算法呢emmmm,我现在还不知道,明天找看,还有实现多子棋功能,emmmm加油哇↖(^ω^)↗)