前言
三子棋是C语言学习过程中一个非常经典的小游戏,学习三子棋的目的呢,主要是学习如何组织一个较大的代码工程。
三子棋的代码,主要分为三个模块,三个模块的功能相互串联,多个文件可以分别处理各自模块的功能,能更好处理各个模块之间的逻辑并且便于后期调试,也使得代码的可读性提高。
下面就来详细的说说如何运用模块之间的串联关系来实现三子棋这样一个小游戏**
零、三子棋实现的代码
0.1 test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void mune()
{
printf("*************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("*************************\n");
}
void game()
{
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('Q' == ret)
{
printf("平局\n");
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
mune();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
0.2 game.h
#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 3
#define COL 3
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<stdlib.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);
0.3 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] = ' ';
}
}
//memset - 初始化库函数-头文件string.h
//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]);
if(j<col-1)
printf("|");
}
printf("\n");
if (i < row - 1)
{
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 (j = 0; j < col; j++)
{
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 < row; 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,用来测试游戏的逻辑。
创建一个game.c,函数的实现过程。
创建一个game.h,函数的声明
二、三子棋的功能实现
2.1 实现游戏的初始界面
写出主函数
int main()
{
return 0;
}
打印菜单
void mune()
{
printf("*************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("*************************\n");
}
我们希望这个游戏能够玩过之后能够接着玩,所以我们写有个do while循环
选择1:开始游戏
选择0:退出游戏
选择其他:重新输入
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
mune();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
2.2 创建一个名为board的二维数组并进行初始化
char board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(board, ROW, COL);
//初始化棋盘
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] = ' ';
}
}
//memset - 初始化库函数-头文件string.h
//memset(&board[0][0], ' ', row * col * sizeof(board[0][0]));
}
2.3 显示棋盘—打印棋盘
game()函数所有的实现过程放在game.h中。
//打印棋盘
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)
{
for (j = 0;j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
}
printf("\n");
}
}
用循环的方法将棋盘打印成一个“井”字形,这样棋盘的大小会随着数组行和列的变化而变化。
2.4 玩家下棋
因为玩家并不一定知道我们棋盘的行和列是从0开始的
比如玩家下棋的坐标为(1, 1).实际在编译器中的真正坐标是(0,0)。
所以为了让玩家真正下棋是的坐标正确的显示在棋盘当中,我们把玩家下棋的坐标置成board[x - 1][y - 1]。
//玩家下棋
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");
}
}
}
2.5 电脑下棋
电脑下棋时并不需要像玩家那样考虑到坐标正确与否的问题,需要设置一个在二维数组当中
没被下过的坐标位置的随机值。
//电脑随机下棋
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;
}
}
}
在主函数中用srand函数设置一个随机值
srand((unsigned int)time(NULL));
2.6 判断输赢
判断输赢的条件很简单。 在进行了玩家下棋—>电脑下棋—>玩家下棋…之后 获胜的一方只要能在棋盘的横向,纵向以及对角线连成相同的三个字符(玩家为 " * " , 电脑为" # ")即为获胜方
创建一个IsWin()函数并根据最终棋盘上的结果返回一个字符打印最后的结果
返回的符号代表的意义:
玩家赢:*
电脑赢:#
平局:Q
继续:C
//判断棋盘是否满了,棋盘满了的情况下还没有分出输赢即是平局的结果
int 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 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 < row; 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';
}
2.7 game.c函数的调用以及输赢结果的返回
所有函数的调用都在test.c当中的game()函数中
函数的实现过程放在game.c当中。
void game()
{
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('Q' == ret)
{
printf("平局\n");
}
}
总结
开篇也提到过,三子棋代码的实现分为三个模块,三个模块的功能相互串联,多个文件可以分别处理各自模块的功能,能更好处理各个模块之间的逻辑并且便于后期调试,也使得代码的可读性提高。
三子棋是一个经典的C语言小游戏,我们能从中体会并学习到一个大工程将如何来构建框架,使得我们的代码的可读性以及维护性更高。