创建项目及其代码分类

三子棋(井字棋)横竖斜三子连成线则获胜

test.c - 游戏逻辑代码编写

game.h - 游戏函数声明,符号定义,头文件的包含

game.c - 游戏函数代码实现

主函数和菜单函数编写

test.c写入主函数main

使用do while循环语句可以让程序一开始就执行一次

我们需要调用一个menu函数用于游戏的开始界面

然后通过输入1或者0 开始游戏或退出游戏

输入非1和0的输需重新输入

开始游戏后调用game函数

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
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d",&input);
switch (input)
{
case 1:
printf("开始三子棋游戏\n");
printf("游戏需要输入坐标(如:1 2 即第一行第二列)\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}

在主函数上面定义一个menu函数,完成游戏开始界面

1
2
3
4
5
6
7
void menu()
{
printf("**********************************\n");
printf("********** 1.play **********\n");
printf("********** 0.exit **********\n");
printf("**********************************\n");
}

游戏逻辑函数编写

开始游戏后,主函数内调用game函数

我们在主函数上面定义一个game函数

并创建一个二维数组

1
2
3
4
5
6
void game()
{
//使用头文件内define定义的行和列,便于以后修改
char board[ROW][COL];

}

然后在头文件game.h中,使用#define定义两个常量,行和列

1
2
3
//符号定义
#define ROW 3
#define COL 3

game函数中调用InitBoard函数,用于初始化棋盘

1
2
3
4
5
6
7
void game()
{
//使用头文件内define定义的行和列,便于以后修改
char board[ROW][COL];
//初始化棋盘 - 空格
InitBoard(board,ROW,COL);
}

初始化棋盘

在实现函数功能前,我们需要先在头文件game.h里声明函数

1
2
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);

然后再game.c中进行实现初始化棋盘

就是把二维数组中的所有元素都变为空格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for ( i = 0; i < row; i++)
{
for ( j = 0; j < col; j++)
{
//把每个元素初始化为空格
board[i][j] = ' ';
}
}
}

打印棋盘

然后我们需要制作一个棋盘,也就是打印一个棋盘

我们先在game函数中调用函数DisplayBoard,用来打印棋盘

1
2
3
4
5
6
7
8
9
void game()
{
//使用头文件内define定义的行和列,便于以后修改
char board[ROW][COL];
//初始化棋盘 - 空格
InitBoard(board,ROW,COL);
//打印棋盘 - 打印数组
DisplayBoard(board, ROW, COL);
}

同样的,在头文件中进行声明

1
2
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);

然后在game.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
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]); //打印棋子
if (j < col -1) //如果j小于最后一位下标,打印 | ,反之不打印 | ,直接换行
{
printf("|");
}
else
{
printf("\n");
}
}
if (i < row -1)//如果i小于最后一位下标,打印分割线 ,反之不打印直接换行
{
for ( j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
else
{
printf("\n");
}
}
}
}
}

头文件包含

我们可以将需要引入的c语言头文件放到我们自己创建的头文件game.h

这样我们只需要引用game.h即可

1
2
3
//头文件包含
#include <stdio.h>
#include <string.h>

玩家下棋

game函数内进行循环,调用PlayerMove函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void game()
{
//使用头文件内define定义的行和列,便于以后修改
char board[ROW][COL];
//初始化棋盘 - 空格
InitBoard(board,ROW,COL);
//打印棋盘 - 打印数组
DisplayBoard(board, ROW, COL);
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
}
}

声明函数

1
2
//玩家下棋
void PlayerMove(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
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家回合\n");
while (1)
{
printf("请输入下棋的坐标:>");
scanf("%d %d", &x, &y);
//判断输入的坐标是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//下棋,判断坐标是否被占用
if (board[x-1][y-1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标已经有棋子咯,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}

电脑下棋

玩家下完棋之后,电脑开始下棋

game函数中调用ComputerMove函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void game()
{
//使用头文件内define定义的行和列,便于以后修改
char board[ROW][COL];
//初始化棋盘 - 空格
InitBoard(board,ROW,COL);
//打印棋盘 - 打印数组
DisplayBoard(board, ROW, COL);
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
}
}

声明函数

1
2
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);

接着在game.c中实现

我们使用随机数的方式让电脑下棋

需要先在主函数中使用srand

然后在ComputerMove函数中使用随机数rand

然后判断当前位置是否为空格

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
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d",&input);
switch (input)
{
case 1:
printf("开始三子棋游戏\n");
printf("游戏需要输入坐标(如:1 2 即第一行第二列)\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
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)
{

printf("电脑回合:>\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}

}

别忘记了引入头文件

1
2
3
4
5
//头文件包含
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

判断输赢

一共有四种情况

1.玩家赢了 - return *
2.电脑赢了 - return #
3.平局 - return Q
4.游戏继续 - return C

我们让电脑或玩家赢的时候返回自身的棋子的字符,比较方便

首先定义一个值ret用于存储返回来的字符

game函数中调用IsWin,把其的返回值赋值给ret

如果返回值不为 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
void game()
{
//使用头文件内define定义的行和列,便于以后修改
char board[ROW][COL];
//初始化棋盘 - 空格
InitBoard(board,ROW,COL);
//打印棋盘 - 打印数组
DisplayBoard(board, ROW, COL);
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断玩家是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断电脑是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
{
printf("你赢了!!!\n");
}
else if(ret == '#')
{
printf("你输了!!!\n");
}
else
{
printf("平局\n");
}
}

然后开始编写IsWin函数

先声明函数

1
2
//判断输赢
char IsWin(char board[ROW][COL], int row, int col);

然后实现函数

我们通过循环判断行和列是否有相同的棋子

然后写一个函数IsFull来判断两个对角线是否有相同的棋子

注意:这种方法只能用于3*3的三子棋

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
//判断输赢
//1.玩家赢了 - return *
//2.电脑赢了 - return #
//3.平局 - return Q
//4.游戏继续 - return C
char IsFull(char board[ROW][COL], int row, int col)//判断平局
{
int i = 0;
int j = 0;
for ( i = 0; i < row; i++)
{
for ( j = 0; j < col; j++)
{
if (board[i][j] == ' ')//如果有一个地方没有棋子,就继续游戏
{
return 'C';
}
}
}
return 'Q';
}
char IsWin(char board[ROW][COL], int row, int col)//注:只能适用与3*3的三子棋(井字棋)
{
int i = 0;
//判断三行
for ( i = 0; i < row; i++)
{
//如果一行都相等,且有一个不为空格,返回该棋子
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
}
//判断三列
for (i = 0; i < col; i++)
{
//如果一列都相等,且有一个不为空格,返回该棋子
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
//判断两对角
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
{
return board[0][0];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
{
return board[0][2];
}
//判断平局(棋盘是否下满,满了返回Q,不满返回C(因为前面的判断都已经结束,只剩下继续游戏))
int ret = IsFull(board, ROW, COL);
return ret;
}

功能增加

其实代码还有很多地方可以优化,特别是最后进行判断的时候,只能用于3*3的三子棋,不过如果需要通用,要用到算法,过于困难了

这里就增加几个比较简单的功能

电脑下棋优化

我们可以让电脑下棋时把中间位置先占据

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 ComputerMove(char board[ROW][COL], int row, int col)
{

printf("电脑回合:>\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
if (board[row / 2][col / 2] == ' ')//如果中间没用棋子,则直接下中间
{
board[row / 2][col / 2] = '#';
break;
}
else
{
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
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
void game()
{
//使用头文件内define定义的行和列,便于以后修改
char board[ROW][COL];
//初始化棋盘 - 空格
InitBoard(board,ROW,COL);
//打印棋盘 - 打印数组
DisplayBoard(board, ROW, COL);
char ret = 0;//游戏是否继续
int n = rand() % 2; //随机数先后手
if (n)
{
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断玩家是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断电脑是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
}
else
{
while (1)
{
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断电脑是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断玩家是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
}

if (ret == '*')
{
printf("你赢了!!!\n");
}
else if(ret == '#')
{
printf("你输了!!!\n");
}
else
{
printf("平局\n");
}
DisplayBoard(board, ROW, COL);
Sleep(1000);
for (int i = 3; i > 0; i--)
{
printf("倒计时后重新开始游戏:%d\n" , i);
Sleep(1000);
}
system("cls");

}

功能展示

代码展示

test.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
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("**********************************\n");
printf("********** 1.play **********\n");
printf("********** 0.exit **********\n");
printf("**********************************\n");
}
void game()
{
//使用头文件内define定义的行和列,便于以后修改
char board[ROW][COL];
//初始化棋盘 - 空格
InitBoard(board,ROW,COL);
//打印棋盘 - 打印数组
DisplayBoard(board, ROW, COL);
char ret = 0;//游戏是否继续
int n = rand() % 2; //随机数先后手
if (n)
{
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断玩家是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断电脑是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
}
else
{
while (1)
{
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断电脑是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断玩家是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
}

if (ret == '*')
{
printf("你赢了!!!\n");
}
else if(ret == '#')
{
printf("你输了!!!\n");
}
else
{
printf("平局\n");
}
DisplayBoard(board, ROW, COL);
Sleep(1000);
for (int i = 3; i > 0; i--)
{
printf("倒计时后重新开始游戏:%d\n" , i);
Sleep(1000);
}
system("cls");

}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d",&input);
switch (input)
{
case 1:
printf("开始三子棋游戏\n");
printf("游戏需要输入坐标(如:1 2 即第一行第二列)\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}

game.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
#pragma once
//符号定义
#define ROW 3
#define COL 3

//头文件包含
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <windows.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 IsWin(char board[ROW][COL], int row, int col);

game.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
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//函数的实现

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for ( i = 0; i < row; i++)
{
for ( j = 0; j < col; j++)
{
//把每个元素初始化为空格
board[i][j] = ' ';
}
}
}

//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]); //打印棋子
if (j < col -1) //如果j小于最后一位下标,打印 | ,反之不打印 | ,直接换行
{
printf("|");
}
else
{
printf("\n");
}
}
if (i < row -1)//如果i小于最后一位下标,打印分割线 ,反之不打印直接换行
{
for ( j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
else
{
printf("\n");
}
}
}
}
}

//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家回合\n");
while (1)
{
printf("请输入下棋的坐标:>");
while (getchar() != '\n')
{
}
scanf("%d %d", &x, &y);
//判断输入的坐标是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//下棋,判断坐标是否被占用
if (board[x-1][y-1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标已经有棋子咯,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}

//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{

printf("电脑回合:>\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
if (board[row / 2][col / 2] == ' ')//如果中间没用棋子,则直接下中间
{
board[row / 2][col / 2] = '#';
break;
}
else
{
board[x][y] = '#';
break;
}
}
}

}

//判断输赢
//1.玩家赢了 - return *
//2.电脑赢了 - return #
//3.平局 - return Q
//4.游戏继续 - return C
char IsFull(char board[ROW][COL], int row, int col)//判断平局
{
int i = 0;
int j = 0;
for ( i = 0; i < row; i++)
{
for ( j = 0; j < col; j++)
{
if (board[i][j] == ' ')//如果有一个地方没有棋子,就继续游戏
{
return 'C';
}
}
}
return 'Q';
}
char IsWin(char board[ROW][COL], int row, int col)//注:只能适用与3*3的三子棋(井字棋)
{
int i = 0;
//判断三行
for ( i = 0; i < row; i++)
{
//如果一行都相等,且有一个不为空格,返回该棋子
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
}
//判断三列
for (i = 0; i < col; i++)
{
//如果一列都相等,且有一个不为空格,返回该棋子
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
//判断两对角
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
{
return board[0][0];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
{
return board[0][2];
}
//判断平局(棋盘是否下满,满了返回Q,不满返回C(因为前面的判断都已经结束,只剩下继续游戏))
int ret = IsFull(board, ROW, COL);
return ret;
}

三子棋程序下载链接

点击这里进行下载