①使用 多文件形式
源文件两个:main.c(主流程) 和 function.c(用到的函数);一个头文件 main.h(放各类生命,联系 main.c 和 function.c )。
文件名字可以自己设置(可以按自己需求和喜好)。
(本人,设置文件时忘记改文件类型了,用了C++的 .cpp 文件类型,如果是C语言,源文件后缀请用 .c )
②效果展示:
![]() | ![]() |
③mian.c文件中的主程序:
int main()
{
int input = 0;
do
{
printf("三子棋游戏,要玩吗?(要:1,不要:0)\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("程序结束!\n");
break;
default:
printf("输入错误,请重新输入: \n");
}
} while (input);
return 0;
}
注意:
此处想,玩完一把不过瘾,还想玩,就用了一个 do-while 循环,此处用 刚刚输入的值(input接收的)来作为判断条件,只要这一轮过程你输入的不是0,那就必然能进入下一轮循环,一旦是0了,也就不再继续循环了,能够达成结束程序的效果。
④输入1,进入游戏,用game()函数实现游戏内容:
void game()
{
srand((unsigned int)time(NULL));
char board[ROW][LINE];
char judge = '0';
//初始化棋盘
BeginBoard(board, ROW, LINE);
//打印棋盘
DisplayBoard(board, ROW, LINE);
while (1)
{
//玩家下棋
PlayerMove(board, ROW, LINE);
DisplayBoard(board, ROW, LINE);
// 判读输赢
judge = IsWin(board, ROW, LINE);
if (judge != 'C')
break;
//电脑下棋
ComputerMove(board, ROW, LINE);
DisplayBoard(board, ROW, LINE);
// 判读输赢
judge = IsWin(board, ROW, LINE);
if (judge != 'C')
break;
}
if (judge == '*')
printf("恭喜,玩家赢了!\n");
else if (judge == '#')
printf("很遗憾,电脑赢了!\n");
else
printf("平局!\n");
}
注意:
此处用的 ROW 和 LINE,是由 #define 定义的标识符常量(我们设置的是3,所以开头看到的效果是三子棋游戏,我们可以根据自己需要去改动 ROW,LINE 来实现几子棋游戏),是为了方便我们以后想改格子个数,不想玩3子棋了,而是想玩5子棋,方便修改。(此代码放到头文件中了,下面会有展示)
注意:
此处用循环来实现,在电脑和玩家都没赢的时候,两方会一直下棋,所以用循环,至于循环条件,我们没用循环条件,而是用了break再合适的时机结束程序,(合适时机:判断输赢的函数,返回值,如果是 ‘C’:两方都没赢呢,‘Q’:平局了,‘*’:玩家赢了,‘#’:电脑赢了,所以当返回值不是‘C'时,那就是肯定有结果了,不用再继续下棋了,我们就可以跳出循环了)。
⑤看 game()函数内部所需的函数:
此部分代码放到了 function.c 里了,main.c 中放主程序,我们已经完成主程序了,下面看每一小部分了。
1>初始化函数 和 打印棋盘的函数
//初始化棋盘
void BeginBoard(char board[][LINE], int row, int line)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < line; j++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
void DisplayBoard(char board[ROW][LINE], int row, int line)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < line; j++)
{
printf(" %c ", board[i][j]);
if (j < line - 1)
printf("|");
}
printf("\n");
if (i < row - 1)
{
for (int j = 0; j < line; j++)
{
printf("---");
if (j < line - 1)
printf("|");
}
printf("\n");
}
}
}
2>玩家下棋 和 电脑下棋 函数
//玩家下棋
void PlayerMove(char board[ROW][LINE], int row, int line)
{
printf("请输入,你要下的坐标:(横纵坐标间用空格隔开)\n");
while (1)
{
int x = 0, y = 0;
scanf_s("%d %d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= line)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
printf("该位置已被占用,请重新输入:\n");
}
else
printf("输入坐标非法,请重新输入:\n");
}
}
//电脑下棋
void ComputerMove(char board[ROW][LINE], int row, int line)
{
printf("电脑下棋:\n");
int x = 0, y = 0;
while (1)
{
x = rand() % row;
y = rand() % line;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
注意:此处涉及 随机函数rand()的应用,rand()要搭配 srand()函数用,而 srand(),又需要time()函数。
3>判断输赢的函数
//判断输赢
char IsWin(char board[ROW][LINE], int row, int line)
{
// 玩家赢(*),电脑赢(#),平局(Q),谁都还没赢(C)
//玩家赢或电脑赢 (玩家赢:*,电脑赢:#, 都没赢:n)
char sym = WhoWin(board, ROW, LINE);
if (sym != 'n')
return sym;
else
{
//判断是否满了:满(0),没满(1)
int sym2 = IsFull(board, ROW, LINE);
if (sym2 == 1)
return 'C';
else
return 'Q';
}
}
在判断输赢的函数中,又涉及了两个新的函数,一个用来判断具体的赢了没有,一个用来判断格子满没满
4>具体判断输赢函数:
//判断是否赢了
char WhoWin(char board[ROW][LINE], int row, int line)
{
//判断行
for (int i = 0; i < row; i++)
{
char i_sym = board[i][0];
if (i_sym != ' ')
{
int flag = 1;
for (int j = 0; j < line; j++)
{
if (board[i][j] != i_sym)
{
flag = 0;
break;
}
}
if (flag == 1)
return i_sym;
}
}
//判断列
for (int j = 0; j < line; j++)
{
char j_sym = board[0][j];
if (j_sym != ' ')
{
int flag = 1;
for (int i = 0; i < row; i++)
{
if (board[i][j] != j_sym)
{
flag = 0;
break;
}
}
if (flag == 1)
return j_sym;
}
}
//判断对角线
if (ROW == LINE)
{
int flag1 = 1, flag2 = 1;
char con1 = board[0][LINE - 1];
char con2 = board[0][0];
for (int i = 0; i < row; i++)
{
for (int j = 0; j < line; j++)
{
if ( j == (row - 1 - i))
{
if (board[i][j] != con1 || con1 == ' ')
flag1 = 0;
}
if ( (i == j))
{
if (board[i][j] != con2 || con2 == ' ')
flag2 = 0;
}
}
}
if (flag1 == 1)
return con1;
else if (flag2 == 1)
return con2;
else
return 'n';
}
}
三子棋游戏中赢:能连成一行了,能连成一列了,对角线能连起来了,下面我们从这三个方面进行分析:
行:
列的思想与判断行的思想基本一致,这里不再赘述;
下面判断对角线:
5>判断棋盘是不是满了函数:
// 判断是否满了
int IsFull(char board[ROW][LINE], int row, int line)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < line; j++)
{
if (board[i][j] == ' ')
return 1;
}
}
return 0;
}
注意:这里没必要用标志变量,判断对角线时用标志变量是因为,我不能直接在标志变量处返回东西啊,因为我下面还有另一个方向的对角线要判断呢,但我又不能什么都不干,不然后续我就不知道这条线到底满不满足赢的条件了。
⑥下面我们看头文件 main.h 里面放了些什么?
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
# define ROW 3
#define LINE 3
void BeginBoard(char board[][LINE], int row, int line);
void DisplayBoard(char board[ROW][LINE], int row, int line);
void PlayerMove(char board[ROW][LINE], int row, int line);
void ComputerMove(char board[ROW][LINE], int row, int line);
char IsWin(char board[ROW][LINE], int row, int line);
1.里面是我们的函数的声明,我们要通过这些函数声明来将 function.c 中定义的函数让主函数 main.c 可以调用,做法就是,让两个源文件(function.c 和 main.c)都包括上表示包括头文件的代码:即#include"main.h",这样两个源文件就能联系起来了。
2.里面还有我们使用到的一些库函数的头文件,和 用#define定义的ROW 和 LINE 两个标识符常量,我们可以在这里改变 ROW 和 LINE 代表的值,以此获得不同数目的格子。
如:是3行8列的格子了。