test.c部分
main函数
int main()
{
test(); //test可以测试该游戏的全部功能
return 0;
}
一个程序的运行是从main函数开始,main函数结尾的,在写程序代码的时候尽量减少main函数中的代码,这样做的目的是使代码封装在函数内部,这样会使程序更加简洁,更清晰,容易修改,这样写代码是一个好的代码习惯。
test函数
//定义一个输入,代表游戏的开始或者退出
int input = 0;
//测试三子棋的函数
void test()
{
srand((unsigned int)time(NULL));
do
{
menu(); //打印一个菜单,提示 1开始 0退出
again: //防止输入错误时打印多份菜单
printf("请输入:>");
scanf("%d", &input);
switch (input) //使用switch分几种不同输入时进入不同的函数
{
case 0:
printf("退出游戏\n");
break;
case 1:
case 2:
system("cls"); //当进入一次游戏时,清一次屏幕
game();
break;
default:
printf("输入错误,重新输入!\n");
goto again;
break;
}
} while (input); //当input=0时刚好退出,放在这里很合理
}
当程序执行时,我们要先设计一个菜单,给用户提供选择,我们要将用户选择的值存放起来,调用相对应的函数,在这里我用了一个switch语句来进行选择,把input设置为全局变量是因为我这里有三个选择,双人版,单人版,和退出。方便后面再双人版和单人版的逻辑实现。
game.h部分
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3 //棋盘的行
#define COL 3 //棋盘的列
//游戏菜单
void menu();
//游戏实现
void game();
在这里引入整个工程所需要的头文件,其它的.c文件包含这个头文件,就可以实现该头文件里面包含的内容。
ROW 是游戏棋盘的行,COL 是列,这里使用宏定义是为了方便后期改成五子棋,四子棋之类的。
在这个头文件中同时也包含了game.c里面的部分函数声明。
game.c部分
游戏菜单
//游戏菜单
void menu()
{
printf("**********************\n");
printf("**** 三 子 棋 ****\n");
printf("**** 1.双人对决 ****\n");
printf("**** 2.人机对决 ****\n");
printf("**** 0.退出 ****\n");
printf("**********************\n");
}
在主函数里面会调用此游戏菜单,供用户选择。
棋盘的初始化
void Initmap(char arr[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ' ';
}
}
}
游戏的数据我们这里选择一个字符型二维数组来进行存放,开始初始化数组内容全部为’ '空格,这样设置后面棋盘打印的比较美观。
棋盘的打印
void Print(char arr[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c |", arr[i][j]);
}
printf("\b \n"); //注意退格符要与空格搭配使用
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---|");
}
printf("\b \n");
}
}
}
这里我设置的为三子棋,所以只打印三行三列,但是在这里我们打印了5行,第一行打印数组的第一行内容,每打印一个符号后面跟一个|当分隔符,但是这样会多出来一个|,在这里我们就要使用\b退格符要搭配空格使用才起作用,就可以消除最后面的那个|,第二行打印一行下划线,用于区分每一行的,以此类推,但是这样也会多打印一行下滑线,这里我们用if语句进行判断,当打印到最后一行时,不打印下划线。
玩家下棋
void Input(char map[ROW][COL], int row, int col, char ret, char* gamer)
{
int x, y;
printf("%s输入:>", gamer);
while (1)
{
scanf("%d %d", &x, &y);
if (x > row || y > col || x <= 0 || y <= 0)
{
printf("非法输入,重新输入:>");
}
else
{
if (map[x - 1][y - 1] != ' ')
{
printf("位置重复占用,重新输入:>");
}
else
{
map[x - 1][y - 1] = ret;
break;
}
}
}
}
void Input(char map[ROW][COL], int row, int col, char ret, char* gamer)
该函数有四个参数第一个是我们传进去的棋盘,第二个是行数,第三个是列数,第四个是要存的符号,就是二维数组中保存的符号,方便后面双人版的实现。
1.首先我们要提示玩家下棋;
2.当玩家下完棋之后,我们要判断位置的合法性,有没有越界或者已经下过棋子;
3.当以上条件都成立时,我们将玩家输入的坐标,在二维数组中存放数据,在这里玩家下棋输入的下标,认为都是从1开始的,但是数组的下标是从0开始的,所以要-1。
电脑下棋
void ComperInput(char map[ROW][COL], int row, int col)
{
// *
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (map[x][y] == ' ')
{
map[x][y] = '*';
break;
}
}
}
void ComperInput(char map[ROW][COL], int row, int col);
该函数有三个参数;
第一个是二维数组,第二个是行数,第三个是列数;
1.电脑下棋不用提示;
2.电脑下棋的坐标我们用随机数函数生成,控制取值范围在:0~2之内;
3.这里只需用判断该坐标是否有棋子;
判断输赢
//‘D’表示平局,'A'表示玩家1胜,'B'表示玩家2胜,
// ‘C’表示游戏继续
//输入坐标为(1,1)时,实际在数组中的坐标是(0,0)
char is_win(char map[ROW][COL], int row, int col)
{
int i, j;
//A和B只能在进入函数时初始化一次
int A = 1, B = 1;
//记录下棋的次数
count++;
//行判断
for (i = 0; i < row; i++)
{
for (j = 0; j < col - 1; j++)
{
if (map[i][j] != map[i][j + 1])
{
A = 0;
B = 0;
break;
/*if (map[i][j] == '#' && map[i][j + 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i][j + 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i][j + 1] == ' ')
{
A = 0, B = 0;
break;
}*/
}
else
{
if (map[i][j] == '#' && map[i][j + 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i][j + 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i][j + 1] == ' ')
{
A = 0, B = 0;
break;
}
}
}
if (A == 1)
{
return 'A';
}
else if (B == 1)
{
return 'B';
}
}
//列判断
for (i = 0; i < row; i++)
{
for (j = 0; j < col - 1; j++)
{
if (map[j][i] != map[j + 1][i])
{
A = 0;
B = 0;
break;
/*if (map[j][i] == '#' && map[j][i + 1] == '#')
{
A = 1;
}
if (map[j][i] == '*' && map[j][i + 1] == '*')
{
B = 1;
}
if (map[j][i] == ' ' || map[j][i + 1] == ' ')
{
A = 0, B = 0;
break;
}*/
}
else
{
if (map[j][i] == '#' && map[j + 1][i] == '#')
{
A = 1;
}
if (map[j][i] == '*' && map[j + 1][i] == '*')
{
B = 1;
}
if (map[j][i] == ' ' || map[j + 1][i] == ' ')
{
A = 0, B = 0;
break;
}
}
}
if (A == 1)
{
return 'A';
}
else if (B == 1)
{
return 'B';
}
}
//左对角线判断
for (i = 0,j = 0; i < row - 1,j < col - 1; i++, j++)
{
if (map[i][j] != map[i + 1][j + 1])
{
A = 0;
B = 0;
break;
/*if (map[j][i] == '#' && map[j + 1][i + 1] == '#')
{
A = 1;
}
if (map[j][i] == '*' && map[j + 1][i + 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i + 1][j + 1] == ' ')
{
A = 0, B = 0;
}*/
}
else
{
if (map[i][j] == '#' && map[i + 1][j + 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i + 1][j + 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i + 1][j + 1] == ' ')
{
A = 0, B = 0;
break;
}
}
}
if (A == 1)
{
return 'A';
}
else if (B == 1)
{
return 'B';
}
//右对角线判断
for (i = 0, j = col - 1; (j >= 1 && i < row - 1); j--, i++)
{
if (map[i][j] != map[i + 1][j - 1])
{
A = 0;
B = 0;
break;
/*if (map[i][j] == '#' && map[i + 1][j - 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i + 1][j - 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i + 1][j - 1] == ' ')
{
A = 0, B = 0;
}*/
}
else
{
if (map[i][j] == '#' && map[i + 1][j - 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i + 1][j - 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i + 1][j - 1] == ' ')
{
A = 0, B = 0;
break;
}
}
}
if (A == 1)
{
return 'A';
}
else if (B == 1)
{
return 'B';
}
//判断棋盘是否已满
if (count == row * col)
{
//这里要将A和B置为0,否则出去A和B都等于1
A = 0, B = 0;
return 'D';
}
return 'C';
}
1.行判断,判断一行是否都是一个符号;
2.列判断,判断一列是否都是一个符号;
3.左对角线判断,判断对角线上的符号是否相同;
4.右对角线判断,判断对角线上的符号是否相同;
5.用返回的参数决定游戏谁赢谁输或者游戏结束,
//‘D’表示平局,'A’表示玩家1胜,'B’表示玩家2胜,
// ‘C’表示游戏继续
游戏引擎
void game()
{
char ret = 0; //用于记录输赢的符号
char map[ROW][COL]; //用一个二维数组存储输入的数据
Initmap(map, ROW, COL); //初始化棋盘
Print(map, ROW, COL); //打印游戏棋盘
count = 0;
//下棋过程
while (1)
{
//玩家1下棋
Input(map, ROW, COL, '#', "玩家1");
//清除上一次的棋盘
system("cls");
//下一次棋,打印一次棋盘
Print(map, ROW, COL); //打印游戏棋盘
//判断游戏是否结束
//返回值是C时游戏继续
if ((ret = is_win(map, ROW, COL)) != 'C')
{
break;
}
//玩家2下棋
switch (input)
{
case 1:
Input(map, ROW, COL, '*', "玩家2");
break;
case 2:
ComperInput(map, ROW, COL);
break;
}
system("cls");
Print(map, ROW, COL); //打印游戏棋盘
//判断游戏是否结束
//返回值是C时游戏继续
if ((ret = is_win(map, ROW, COL)) != 'C')
{
break;
}
}
if (ret == 'A')
{
printf("玩家1胜!\n");
}
else if (ret == 'B')
{
printf("玩家2胜!\n");
}
else if (ret == 'D')
{
printf("平局\n");
}
}
该函数是整合了上面的函数功能来实现我们想要的功能。
调用此函数就可以驱动游戏的核心。
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3 //棋盘的行
#define COL 3 //棋盘的列
//定义一个输入,代表游戏的开始或者退出
int input = 0;
//记录判断的次数,等于下棋的个数
int count = 0;
//游戏菜单
void menu()
{
printf("**********************\n");
printf("**** 三 子 棋 ****\n");
printf("**** 1.双人对决 ****\n");
printf("**** 2.人机对决 ****\n");
printf("**** 0.退出 ****\n");
printf("**********************\n");
}
void Print(char arr[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c |", arr[i][j]);
}
printf("\b \n"); //注意退格符要与空格搭配使用
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---|");
}
printf("\b \n");
}
}
}
void Initmap(char arr[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ' ';
}
}
}
//玩家下棋用#,电脑下棋用*
void Input(char map[ROW][COL], int row, int col, char ret, char* gamer)
{
int x, y;
printf("%s输入:>", gamer);
while (1)
{
scanf("%d %d", &x, &y);
if (x > row || y > col || x <= 0 || y <= 0)
{
printf("非法输入,重新输入:>");
}
else
{
if (map[x - 1][y - 1] != ' ')
{
printf("位置重复占用,重新输入:>");
}
else
{
map[x - 1][y - 1] = ret;
break;
}
}
}
}
//电脑下棋
void ComperInput(char map[ROW][COL], int row, int col)
{
// *
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (map[x][y] == ' ')
{
map[x][y] = '*';
break;
}
}
}
//‘D’表示平局,'A'表示玩家1胜,'B'表示玩家2胜,
// ‘C’表示游戏继续
//输入坐标为(1,1)时,实际在数组中的坐标是(0,0)
char is_win(char map[ROW][COL], int row, int col)
{
int i, j;
//A和B只能在进入函数时初始化一次
int A = 1, B = 1;
//记录下棋的次数
count++;
//行判断
for (i = 0; i < row; i++)
{
for (j = 0; j < col - 1; j++)
{
if (map[i][j] != map[i][j + 1])
{
A = 0;
B = 0;
break;
/*if (map[i][j] == '#' && map[i][j + 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i][j + 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i][j + 1] == ' ')
{
A = 0, B = 0;
break;
}*/
}
else
{
if (map[i][j] == '#' && map[i][j + 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i][j + 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i][j + 1] == ' ')
{
A = 0, B = 0;
break;
}
}
}
if (A == 1)
{
return 'A';
}
else if (B == 1)
{
return 'B';
}
}
//列判断
for (i = 0; i < row; i++)
{
for (j = 0; j < col - 1; j++)
{
if (map[j][i] != map[j + 1][i])
{
A = 0;
B = 0;
break;
/*if (map[j][i] == '#' && map[j][i + 1] == '#')
{
A = 1;
}
if (map[j][i] == '*' && map[j][i + 1] == '*')
{
B = 1;
}
if (map[j][i] == ' ' || map[j][i + 1] == ' ')
{
A = 0, B = 0;
break;
}*/
}
else
{
if (map[j][i] == '#' && map[j + 1][i] == '#')
{
A = 1;
}
if (map[j][i] == '*' && map[j + 1][i] == '*')
{
B = 1;
}
if (map[j][i] == ' ' || map[j + 1][i] == ' ')
{
A = 0, B = 0;
break;
}
}
}
if (A == 1)
{
return 'A';
}
else if (B == 1)
{
return 'B';
}
}
//左对角线判断
for (i = 0,j = 0; i < row - 1,j < col - 1; i++, j++)
{
if (map[i][j] != map[i + 1][j + 1])
{
A = 0;
B = 0;
break;
/*if (map[j][i] == '#' && map[j + 1][i + 1] == '#')
{
A = 1;
}
if (map[j][i] == '*' && map[j + 1][i + 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i + 1][j + 1] == ' ')
{
A = 0, B = 0;
}*/
}
else
{
if (map[i][j] == '#' && map[i + 1][j + 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i + 1][j + 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i + 1][j + 1] == ' ')
{
A = 0, B = 0;
break;
}
}
}
if (A == 1)
{
return 'A';
}
else if (B == 1)
{
return 'B';
}
//右对角线判断
for (i = 0, j = col - 1; (j >= 1 && i < row - 1); j--, i++)
{
if (map[i][j] != map[i + 1][j - 1])
{
A = 0;
B = 0;
break;
/*if (map[i][j] == '#' && map[i + 1][j - 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i + 1][j - 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i + 1][j - 1] == ' ')
{
A = 0, B = 0;
}*/
}
else
{
if (map[i][j] == '#' && map[i + 1][j - 1] == '#')
{
A = 1;
}
if (map[i][j] == '*' && map[i + 1][j - 1] == '*')
{
B = 1;
}
if (map[i][j] == ' ' || map[i + 1][j - 1] == ' ')
{
A = 0, B = 0;
break;
}
}
}
if (A == 1)
{
return 'A';
}
else if (B == 1)
{
return 'B';
}
//判断棋盘是否已满
if (count == row * col)
{
//这里要将A和B置为0,否则出去A和B都等于1
A = 0, B = 0;
return 'D';
}
return 'C';
}
//游戏实现
void game()
{
char ret = 0; //用于记录输赢的符号
char map[ROW][COL]; //用一个二维数组存储输入的数据
Initmap(map, ROW, COL); //初始化棋盘
Print(map, ROW, COL); //打印游戏棋盘
count = 0;
//下棋过程
while (1)
{
//玩家1下棋
Input(map, ROW, COL, '#', "玩家1");
//清除上一次的棋盘
system("cls");
//下一次棋,打印一次棋盘
Print(map, ROW, COL); //打印游戏棋盘
//判断游戏是否结束
//返回值是C时游戏继续
if ((ret = is_win(map, ROW, COL)) != 'C')
{
break;
}
//玩家2下棋
switch (input)
{
case 1:
Input(map, ROW, COL, '*', "玩家2");
break;
case 2:
ComperInput(map, ROW, COL);
break;
}
system("cls");
Print(map, ROW, COL); //打印游戏棋盘
//判断游戏是否结束
//返回值是C时游戏继续
if ((ret = is_win(map, ROW, COL)) != 'C')
{
break;
}
}
if (ret == 'A')
{
printf("玩家1胜!\n");
}
else if (ret == 'B')
{
printf("玩家2胜!\n");
}
else if (ret == 'D')
{
printf("平局\n");
}
}
//测试三子棋的函数
void test()
{
srand((unsigned int)time(NULL));
do
{
menu(); //打印一个菜单,提示 1开始 0退出
again: //防止输入错误时打印多份菜单
printf("请输入:>");
scanf("%d", &input);
switch (input) //使用switch分几种不同输入时进入不同的函数
{
case 0:
printf("退出游戏\n");
break;
case 1:
case 2:
system("cls"); //当进入一次游戏时,清一次屏幕
game();
break;
default:
printf("输入错误,重新输入!\n");
goto again;
break;
}
} while (input); //当input=0时刚好退出,放在这里很合理
}
int main()
{
test();
return 0;
}
结语
到这里这篇博客已经结束啦。
这份博客👍如果对你有帮助,给博主一个免费的点赞以示鼓励欢迎各位🔎点赞👍评论收藏⭐️,谢谢!!!
如果有什么疑问或不同的见解,欢迎评论区留言欧👀