因为我们定义了比较多的函数,所以我将其每个函数都单独讲解,在这个项目中,我做了两个人机对战,一个非常简单的,一个简单的。
(声明函数就不展示了)
int flag = 0;
int map[19][19];
我们先定义两个全局变量。
其中flag变量用来表示这个回合谁落子,偶数表示黑方落子,基数表示白方落子。
数组中的数组代表子的颜色,2表示黑子,1表示白字。
system(“cls”); 用来清屏。
int main() {
menuView();
return 0;
}
这里主要是调用菜单界面函数。
void gameView(void) {
int n, q, w, e = 0, r = 0, t, y, b, m, i = 0;
while (1) {
init();
gameView_ShowMap();
while (i == 0) {
if (flag % 2 == 0 && e != 2) {
printf("请输入黑子坐标(输入20 20)暂停");
scanf("%d %d", &q, &w);
if (q + w >= 40)
zantingView();
t = playerMove(q, w);
if (t == 2) {
printf("落子成功\n");
}
e = isWin(q, w);
if (e == 2) {
printf("黑子赢\n");
printf("白子输\n");
jieshuView();
}
}
if (flag % 2 == 1 && r != 1) {
printf("请输入白子坐标(输入20 20)暂停");
scanf("%d %d", &q, &w);
if (q + w >= 40)
zantingView();
y = playerMove(q, w);
if (y == 1) {
printf("落子成功\n");
}
r = isWin(q, w);
if (r == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
}
};
}
return;
}
在这里我们先初始化棋盘(也就是void init(void)),之后打印棋盘。
然后我们就可以输入想要落子的坐标了,在这里如果我们输入20 20那么就会进入暂停界面,当我们输入了落子坐标后,会将我们输入的坐标作为参数传入到落子的函数当中,如果成功落子,那么就会返回一个y=1的值,之后我们通过检测y的值就可以判断我们是否落子成功了。如果成功了,那么我们就可以调用胜利判断的函数,进行胜利的判断。判断胜利函数会返回一个值,我们可以通过检测函数返回的值来判断是否胜利,如果胜利了,那么我们就会打印哪一方赢了,哪一方输入,并且调用结算节目函数。
void menuView(void) {
while (1) {
printf("1. 面对面模式\n");
printf("2. 人机对战\n");
printf("3. 进入设置\n");
printf("4. 退出游戏\n");
printf("请输入序号:");
int choose;
scanf("%d", &choose);
switch (choose) {
case 1:
system("cls");
gameView();
break;
case 2:
system("cls");
nanduView();
case 3:
printf("敬请期待\n");
break;
case 4:
exit(0);
break;
default:
break;
}
}
}
进入了菜单函数,我们就可以进行选择,在这里 system(“cls”); 是用来进行清屏的,避免许多界面堆在一起。
void zantingView(){
int x = 0,y;
printf("1.结束对局\n");
printf("2.返回游戏\n");
printf("请输入编号:\n");
scanf("%d", &x);
if (x != 1 && x != 2)
{
printf("输入错误,请重新输入");
scanf("%d", &x);
}
switch (x) {
case 1:
if (flag % 2 == 1) {
system("cls");
printf("黑子赢\n");
printf("白子输\n");
jieshuView();
}
if (flag % 2 == 0) {
system("cls");
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
break;
case 2:
return;
default:
break;
}
return;
}
暂停界面我们可以进行选择,如果选择结束游戏,那么就直接调用结算界面函数。
void jiandanView() {
int q, w, e = 0, t, r = 0;
while (1) {
init();
gameView_ShowMap();
while (1) {
if (flag % 2 == 0 && e != 2) {
printf("请输入黑子坐标(输入20 20)暂停");
scanf("%d %d", &q, &w);
if (q + w >= 40)
zantingView();
t = playerMove(q, w);
if (t == 2) {
printf("落子成功\n");
}
e = isWin(q, w);
if (e == 2) {
printf("黑子赢\n");
printf("白子输\n");
jieshuView();
}
}
if (flag % 2 == 1 && r != 1) {
q = rand() % 20;
w = rand() % 20;
playerMove(q, w);
r = isWin(q, w);
if (r == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
}
}
}
}
玩家部分和前文的游戏界面函数相同,我这里主要讲解一下人机如何落子。
在这个函数中,我们采用了随机函数来生成随机数用来进行人机落子。
我们生成了两个随机数,代表两个坐标。
void renjiView() {
int q, w, e = 0, t, r = 0, p = 0, k = 0;
while (1) {
init();
gameView_ShowMap();
while (1) {
if (flag % 2 == 0 && e != 2) {
printf("请输入黑子坐标(输入20 20)暂停");
scanf("%d %d", &q, &w);
if (q + w >= 40)
zantingView();
t = playerMove(q, w);
if (t == 2) {
printf("落子成功\n");
}
e = isWin(q, w);
if (e == 2) {
printf("黑子赢\n");
printf("白子输\n");
jieshuView();
}
}
if (flag % 2 == 1 && r != 1) {
p = rand() % 8;
switch (p) {
case 1:
if (map[q - 1][w - 1] == 0 && q - 1 > 0 && q - 1 < 18 && w - 1 > 0 && w - 1 < 18) {
map[q - 1][w - 1] = 1;
gameView_ShowMap();
k = isWin(q - 1, w - 1);
if (k == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
++flag;
break;
}
else
p = rand() % 8;
continue;
case 2:
if (map[q][w - 1] == 0 && q > 0 && q < 18 && w - 1 > 0 && w - 1 < 18) {
map[q][w - 1] = 1;
gameView_ShowMap();
k = isWin(q, w - 1);
if (k == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
++flag;
break;
}
else
p = rand() % 8;
continue;
case 3:
if (map[q + 1][w - 1] == 0 && q + 1 > 0 && q + 1 < 18 && w - 1 > 0 && w - 1 < 18) {
map[q + 1][w - 1] = 1;
gameView_ShowMap();
k = isWin(q + 1, w - 1);
if (k == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
++flag;
break;
}
else
p = rand() % 8;
continue;
case 4:
if (map[q + 1][w] == 0 && q + 1 > 0 && q + 1 < 18 && w > 0 && w < 18) {
map[q + 1][w] = 1;
gameView_ShowMap();
k = isWin(q + 1, w);
if (k == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
++flag;
break;
}
else
p = rand() % 8;
continue;
case 5:
if (map[q + 1][w + 1] == 0 && q + 1 > 0 && q + 1 < 18 && w + 1 > 0 && w + 1 < 18) {
map[q + 1][w + 1] = 1;
gameView_ShowMap();
k = isWin(q + 1, w + 1);
if (k == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
++flag;
break;
}
else
p = rand() % 8;
continue;
case 6:
if (map[q][w + 1] == 0 && q > 0 && q < 18 && w + 1 > 0 && w + 1 < 18) {
map[q][w + 1] = 1;
gameView_ShowMap();
k = isWin(q, w + 1);
if (k == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
++flag;
break;
}
else
p = rand() % 8;
continue;
case 7:
if (map[q - 1][w + 1] == 0 && q - 1 > 0 && q - 1 < 18 && w + 1 > 0 && w + 1 < 18) {
map[q - 1][w + 1] = 1;
gameView_ShowMap();
k = isWin(q - 1, w + 1);
if (k == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
++flag;
break;
}
else
p = rand() % 8;
continue;
case 8:
if (map[q - 1][w] == 0 && q - 1 > 0 && q - 1 < 18 && w > 0 && w < 18) {
map[q - 1][w] = 1;
gameView_ShowMap();
k = isWin(q - 1, w);
if (k == 1) {
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
++flag;
break;
}
else
p = rand() % 8;
continue;
default:
break;
}
}
}
}
}
这里我们的主要思路是,在玩家落子的四周随机落子来达到类似围堵的效果,if里许多的判断条件为了避免数组越界,并且在人机落子之后,我们调用判断胜利函数来判断人机是否胜利,最后用随机函数重新生成一个数。
int playerMove(int x, int y) {
extern int flag;
extern int map[19][19];
if (map[x][y] == 0&&(x+y)<40&&x>0&&y>0) {
if (flag % 2 == 0) {
map[x][y] = 2;
flag++;
gameView_ShowMap();
return 2;
}
else if (flag % 2 == 1) {
map[x][y] = 1;
flag++;
gameView_ShowMap();
return 1;
}
}
}
在这里,我们先判断玩家输入的坐标是否符合我们的要求,如果符合了,那么就改变数组内对应的值,并且返回一个值用以判断是否落子成功。
int isWin(int x, int y) {
int b = 0, k, c, d,z;
extern int map[19][19];
if (map[x][y] == 2) {
b += 1;
for (k = 1; map[x - k][y - k] == 2; k++)
b += 1;
for (k = 1; map[x + k][y + k] == 2; k++)
b += 1;
for (k = 1, z = 1; map[x - k][y + k] == 2; k++)
z += 1;
for (k = 1; map[x + k][y - k] == 2; k++)
z += 1;
for (k = 1, c = 1; map[x + k][y] == 2; k++)
c += 1;
for (k = 1; map[x - k][y] == 2; k++)
c += 1;
for (k = 1, d = 1; map[x][y + k] == 2; k++)
d += 1;
for (k = 1; map[x][y - k] == 2; k++)
d += 1;
if (b >= 5 || c >= 5 || d >= 5 || z >= 5)
return 2;
}
int e = 0, h, f, g,n;
if (map[x][y] == 1) {
e += 1;
for (h = 1; map[x - h][y - h] == 1; h++)
e += 1;
for (h = 1; map[x + h][y + h] == 1; h++)
e += 1;
for (k = 1, n = 1; map[x - k][y + k] == 2; k++)
n += 1;
for (k = 1; map[x + k][y - k] == 2; k++)
n += 1;
for (h = 1, f = 1; map[x + h][y] == 1; h++)
f += 1;
for (h = 1; map[x - h][y] == 1; h++)
f += 1;
for (h = 1, g = 1; map[x][y + h] == 1; h++)
g += 1;
for (h = 1; map[x][y - h] == 1; h++)
g += 1;
if (e >= 5 || f >= 5 || g >= 5 || n>=5)
return 1;
}
}
这个判断胜利的函数看似复杂,其实并不怎么复杂,我们将玩家输入的坐标作为参数,开始逐个查找,先是左上方,右下方,之后是右上方,右下方,之后是正下方和正上方,之后是正右方,正左方逐个查找,最后判断是否满足胜利的条件,如果满足,就返回一个值到游戏界面函数,之后游戏界面函数通过判断这个值进入游戏结算函数
void init(void) {
extern int flag;
extern int map[19][19];
for (int i = 0; i < 19; i++)
{
for (int j = 0; j < 19; j++)
{
map[i][j] = 0;
}
}
flag = 0;
return;
}
这个函数通过两个循环将map数组初始化以便进行游戏。
void gameView_ShowMap(void) {
extern int map[19][19];
int b = 0;
printf(" 口为黑棋,龘为白棋,十为空地\n");
printf(" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18\n");
for (int i = 0; i < 19; i++) {
printf("%-2d ", b++);
for (int j = 0; j < 19; j++) {
if (map[i][j] == 0) {
printf("十 ");
}
else if (map[i][j] == 1) {
printf("龘 ");
}
else {
printf("口 ");
}
}
printf("\n");
}
return;
}
这个函数用来打印棋盘,如果是空白就为十,黑子就为口,白字就为龘。
void jieshuView() {
int y;
printf("请输入编号:\n");
printf(" 1.退出游戏\n");
printf(" 2.返回菜单\n");
scanf_s("%d", &y);
if (y != 1 && y != 2) {
printf("输入错误,请重新输入:");
scanf_s("%d", &y);
}
switch (y) {
case 1:
exit(0);
break;
case 2:
system("cls");
menuView();
break;
default:
break;
}
}
结算界面函数让我们进行选择,我们可以选择返回菜单继续游戏,也可以直接退出游戏
void zantingView() {
int x = 0,y;
printf("1.结束对局\n");
printf("2.返回游戏\n");
printf("请输入编号:\n");
scanf("%d", &x);
if (x != 1 && x != 2)
{
printf("输入错误,请重新输入");
scanf("%d", &x);
}
switch (x) {
case 1:
if (flag % 2 == 1) {
system("cls");
printf("黑子赢\n");
printf("白子输\n");
jieshuView();
}
if (flag % 2 == 0) {
system("cls");
printf("白子赢\n");
printf("黑子输\n");
jieshuView();
}
break;
case 2:
return;
default:
break;
}
return;
}
暂停界面我们有两个选择,一个是返回游戏,一个是结束游戏,如果结束游戏那么就直接进入游戏结算函数,如果返回游戏,则游戏继续。
至此,代码讲解完毕。
(本人是新手写作,可能有很多地方没有解释清楚并且可能有一些出错的地方,请大家谅解,由于自己技术不足,代码可读性可能很差,在这里我说一声抱歉,也请大家谅解。并且代码可能有许多可以简化的地方,但是我还没发现,也请各位指出。