今天实现一个三子棋小游戏,虽然小,但是“麻雀虽小,五脏俱全”,通过三子棋游戏,我们来学习如何模块化地写代码。
首先,我们创建一个game.h头文件,再创建一个game.c和test.c源文件。
//头文件用来放各种函数的声明,以及引用库里的头文件;game.h用来写各个函数的内容;test.c用来将各个函数模块连接起来
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//函数的声明
//初始化棋盘
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);
//头文件(上图)
然后,开始在test.c中写代码。我们希望玩家在一开始的时候可以看到游戏的一个界面,并且决定自己玩还是不玩游戏,所以这里我们用一个do while循环,先让玩家看到界面,再选择自己玩不玩,并且如果想,可以一直玩下去:
void menu()
{
printf("***************************\n");
printf("*********三 子 棋**********\n");
printf("*********1.开始游戏********\n");
printf("*********0.退出游戏********\n");
printf("***************************\n");
}
//menu
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//后面电脑随机生成坐标要用到rand函数
do
{
menu();
printf("请输入>: ");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
这里我们把input作为while的条件,输入1开始游戏,0退出游戏。
接下来,我们就要开始写游戏的内容了也就是game函数。
下棋嘛,肯定要有棋盘,所以创建一个3*3的二维数组,元素全部是空格,就定义为board函数吧,在头文件里面声明一下,再在game.c里面写:
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';//空格不仅占位了,而且又看不见;0虽然看不见,但是不能占位
}
}
//memset(&board[0][0], ' ', row* col * sizeof(board[0][0]));
}
//也可以用memset函数
创建好了数组,但是什么都看不到,我们要加上一些条条框框,所以定义DisplayBoard函数:
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)
printf("|");
}
printf("\n");
if (i < row - 1)
{
j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
}
printf("\n");
}
}
接下来就可以看到棋盘了:
棋盘做好了,就可以下棋了,你下一步,打印一下棋盘,电脑下一步,再打印一下棋盘,你再下一步,打印一下棋盘,电脑下一步......我们发现这里要用到循环,并且,下完之后,我们最好都去判断一下输赢:
void game()
{
//创建一个3*3的二维数组
char board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(board,ROW,COL);
//显示棋盘
DisplayBoard(board,ROW,COL);
while (1)
{
//玩家下棋
PlayerMove(board,ROW,COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
//判读输赢
ret = IsWin(board, ROW, COL);
//电脑下棋
ComputerMove(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
}
这就是大概的流程,以及各自的函数;
首先,玩家下棋:
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");
}
}
}
在玩家的眼中,大多是认为第一行第一列就是1 1,而不是0 0,所以我们就要按照玩家的习惯去写,下完了打印一下棋盘,
接着电脑下棋,这里就是个小游戏,不是什么人机大战,所以不会写的电脑很厉害,当然现在我也写不出来那种类似于人工智能的代码,所以就rand取随机数就好了(srand函数写外面,一次游戏调用一次就行了):
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋>:\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
电脑的话坐标就直接从0开始到2结束就好了,省的麻烦,接着打印棋盘;
现在还有最后一个模块:判断输赢,也就是IsWin函数:
//判断输赢
//玩家赢 —— '*'
//电脑赢 —— '#'
//平局 —— 'Q'
//继续 —— 'C'
char IsWin(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][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[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//判断是否是平局
if (IsFull(board, row, col))
return 'Q';
//游戏继续
return 'C';
}
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (i = 0; i < col; i++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
我们一开始在头文件里面用define定义了常量ROW和COL,是为了以后可以快速的改变棋盘的大小,但是在判断输赢中又把我们的棋盘限制住了,但是呢,无论如何还是满足了作为“三”子棋的条件,以后学的更多了之后可以再改进改进。
这里IsWin函数中玩家和电脑赢返回的‘*’ ‘#‘正好是玩家和电脑输入的’*’ ‘#’,灵活地运用了函数的返回值:
void game()
{
//创建一个3*3的二维数组
char board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(board,ROW,COL);
//显示棋盘
DisplayBoard(board,ROW,COL);
char ret = 0;
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 if (ret == 'Q')
printf("平局!\n");
}
而且把判断输赢放在循环外面,平局的判断放在里面,也减少了代码的重复;
这样,我们的三子棋小游戏就写好了,思路不算复杂,主要还是认识并了解代码的模块化,毕竟这么写下来也有个300多行代码,如果还像以前做题目一样全部写在一个源文件里面,就太麻烦了。
最后,源码附上:
game.h :
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//函数的声明
//初始化棋盘
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 :
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';//空格不仅占位了,而且又看不见;0虽然看不见,但是不能占位
}
}
//memset(&board[0][0], ' ', row* col * sizeof(board[0][0]));
}
//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]);
// }
// printf("\n");
// }
//}//什么都看不到
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
// int i = 0;
// int j = 0;
// for (i = 0; i < row; i++)
// {
// printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
// if (i < row - 1)
// printf("---|---|---\n");
// }
//}//列数卡死了
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)
printf("|");
}
printf("\n");
if (i < row - 1)
{
j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
}
printf("\n");
}
}
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");
}
}
}
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋>:\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (i = 0; i < col; i++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
//判断输赢
//玩家赢 —— '*'
//电脑赢 —— '#'
//平局 —— 'Q'
//继续 —— 'C'
char IsWin(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][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[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//判断是否是平局
if (IsFull(board, row, col))
return 'Q';
//游戏继续
return 'C';
}
test.c :
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("***************************\n");
printf("*********三 子 棋**********\n");
printf("*********1.开始游戏********\n");
printf("*********0.退出游戏********\n");
printf("***************************\n");
}
void game()
{
//创建一个3*3的二维数组
char board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(board,ROW,COL);
//显示棋盘
DisplayBoard(board,ROW,COL);
char ret = 0;
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 if (ret == 'Q')
printf("平局!\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请输入>: ");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}