通过对数组,函数,循环知识的应用我们可以独立地创建一个项目--三子棋。首先我们对于三子棋的实现要有一个大概的思路和逻辑。
文件的创建
工欲善其事必先利其器,为了更好地完成项目,先创建三个文件--两个源文件,一个头文件。测试文件-test.c,游戏文件game.c,游戏头文件game.h。游戏文件主要就是存放与游戏过程实现有关代码,例如:初始化函数,打印棋盘函数等;测试文件主要就是存放main函数和一些辅助实现的函数;游戏头文件里就是游戏文件中函数的声明,做头文件被测试文件引入。
先在test.c中把主函数写一写:
int main()
{
test();
return 0;
}
//将测试单独写成一个函数,分块处理,使逻辑更加清晰
再把test函数写一写,当程序运行到test函数时,就已经进入到游戏的初始界面啦。
#include<stdio.h>
void menu()
{
printf("---------------------\n");
printf("------1.play---------\n");
printf("------0.exit---------\n");
printf("---------------------\n");
}
void test()
{
int input = 0;//用到的变量最好在函数前面引入,不要在复合语句中引入,
//这样它的作用域就小了,复合语句外再想用这个变量就无法使用了。
do
{
menu();
printf("请选择:");
scanf("%d",&input);
switch (input)
{
case 1:
game();
break;
case 0: //因为游戏玩一次常常玩不够,所以选择的是循环结构
printf("离开游戏\n");
break;
default:
printf("输入错误请重新输入");
break;
}
}while(input);
}
int main()
{
test();
return 0;
}
大体思路
1.下的棋子也算是数据,数据要存储在哪里?
2.要先初始化棋盘,并且打印出来,看看初始的棋盘是怎样的。
3.游戏开始,玩家下一步,电脑下一步。要清楚这是一个有条件的循环过程。
4.判断输赢,判断输赢的根据又是什么?
在game()的函数中写下思路
void game()
{
char board[3][3] = {0};//初始化一个数组用来存放数据
//初始化棋盘;
//打印棋盘;
//游戏开始:玩家走,电脑走。循环的过程。
//判断输赢。
}
为了使程序更具改造性,在game.h中宏定义。之后就用字符串来表示数字,以后需要改成五子棋,十字棋等等,只需要在头文件这里修改3为5,10就好了。
#define ROW 3
#define COL 3
void game()
{
char board[3][3] = {0};//初始化一个数组用来存放数据
//初始化棋盘;
//打印棋盘;
//游戏开始:玩家走,电脑走。循环的过程。
//判断输赢。
}
1.数据的储存
三子棋--三行三列。这样的数据一定是被存储在一组二维数组中的。并且是字符类型的数组中。
2.初始化棋盘,打印棋盘
初始化棋盘,在没有下棋的时候,每个棋点都应该是空格。在游戏文件下写一个数组初始化的函数
//写在game.c内
//初始化棋盘
void Initboard(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++)
{
board[i][j] = ' ';
}
}
}
打印棋盘,打印棋盘就要知道棋盘是什么样子的,
大概就是这个样子。照葫芦画瓢,代码诞生:
void Displayboard(char board[ROW][COL], int row, int col)
{
int i = 0;
for(i = 0; i < row; i++)
{
//打印数据
printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2] );
//打印分割行
if(i < col -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)
{
for( j = 0; j < col; j++)
{
printf("___");
if( j < col - 1)
printf("|");
}
}
//换行
printf("\n");
}
}
这样一个棋盘的初始化函数和打印函数就完成了。
3.游戏开始
游戏的过程无非就是玩家下棋,电脑下棋。就写两个函数,一个是玩家下棋的函数,一个是电脑下棋的函数,还是在game.c的文件下写。
void Player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋>");
int x = 0;
int y = 0;
scanf("%d %d",&x,&y);
while (1)
{
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 Computer_move(char board[ROW][COL], int row, int col)
{
int x = rand() % 3;//随机生成数字,取3的余数,总是0~2
int y = rand() % 3;
while (1)
{
if( board[x][y] ==' ')
{
board[x][y] = '#';//电脑下棋用'#'
break;
}
}
}
这些代码看起来好像都没有错。但是,却跑不起来!因为电脑下棋的函数,随机生成的x,y进入到函数中就是固定的了!不符合条件的话一直循环!无法跳出。所以随机生成的数应该在循环内部。
void Computer_move(char board[ROW][COL], int row, int col)
{
while (1)
{
int x = rand() % 3;//随机生成数字,取3的余数,总是0~2
int y = rand() % 3;
if( board[x][y] ==' ')
{
board[x][y] = '#';//电脑下棋用'#'
break;
}
}
}
4.判断输赢
最后再写一个函数判断输赢。如果玩家赢了返回'*',如果电脑赢了返回'#',如果平局返回'p',如果游戏还在继续就返回'c'。
int is_full(char board[ROW][COL],int row, int col)
{
int n = 0;
int m = 0;
for ( n = 0; n < row; n++)
{
for ( m = 0; m < col; m++)
{
if ( board[n][m] == ' ')
{
return 0;
}
}
}
return 1;
}
char is_win( char board[ROW][COL],int row, int col)
{
int i = 0;
//判断行是否有3个相同的棋子
for (i = 0; i < row; i++)
{
if( board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] !=' ')
{
return board[i][1];
}
}
//判断列是否为3个相同的棋子
for (i = 0; i < col; i++)
{
if( board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] !=' ')
{
return board[1][i];
}
}
//判断对角线是否有三个相同的棋子
for(i = 0; i < col; i++)
{
if( board[0][0] == board[1][1] && board[1][1] ==board[2][2] && board[1][1] != ' ')
return board[1][1];
if( board[2][0] == board[1][1] && board[1][1] ==board[0][2] && board[1][1] != ' ')
return board[1][1];
//判断是否平局
if (1 == is_full())
return 'p';
return 'c';
}
判断输赢的函数就大功告成了。
5.完善游戏函数
所有要被使用的函数都已经写好,最后就该组装这些函数了。主要就是添加在test.c下的game()函数中。在此之前,应该把所有用到的函数都在game.h中声明一下,然后test.c文件引入game.h头文件,这样游戏里面的函数才能被正常调用。
//game.h下的全部代码
#include<stdio.h>
#include<time.h>
#include<stdlib.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 Player_move(char board[ROW][COL], int row, int col);
//电脑下棋
void Computer_move(char board[ROW][COL], int row, int col);
//判断是否获胜
char is_win(char board[ROW][COL], int row, int col);
void game()
{
char board[3][3] = {0};//初始化一个数组用来存放数据
//初始化棋盘
Initboard(board,ROW,COL);
//打印棋盘
Displayboard(board,ROW,COL);
//游戏开始
while(1)
{
//玩家走
Player_move(board,ROW,COL);
Displayboard(board,ROW,COL);
if ( is_win(board,ROW,COL) != 'c')
break;
//电脑走
Computer_move(board,ROW,COL);
Displayboard(board,ROW,COL);
if ( is_win(board,ROW,COL) != 'c')
break;
}
//判断输赢
if ( is_win(board) == '*' )
printf("玩家获胜");
if ( is_win(board) == '#' )
printf("电脑获胜");
if ( is_win(board) == 'p' )
printf("平局");
}
game()函数被完善好了!
6.完整代码
//game.h下的全部代码
#include<stdio.h>
#include<time.h>
#include<stdlib.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 Player_move(char board[ROW][COL], int row, int col);
//电脑下棋
void Computer_move(char board[ROW][COL], int row, int col);
//判断是否获胜
char is_win(char board[ROW][COL], int row, int col);
//test.c下的全部代码
#include"game.h"
void game()
{
char ret;
//数据存储到一个字符的二维数组中。玩家为’*‘,电脑为’#‘
char board[ROW][COL] = { 0 }; //初始应均为空格
Initboard(board,ROW,COL); //初始化棋盘
Displayboard(board, ROW, COL); //打印棋盘
while (1)
{
Player_move(board,ROW,COL);
Displayboard(board, ROW, COL); //打印棋盘
ret = is_win(board, ROW, COL);
if (ret != 'c')
break;
Computer_move(board, ROW, COL);
Displayboard(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'c')
break;
}
if (ret == '*')
{
printf("玩家胜\n");
}
if (ret == '#')
{
printf("电脑胜\n");
}
if (ret == 'p')
{
printf("平局\n");
}
}
void menu()
{
printf("---------------------\n");
printf("------1.play---------\n");
printf("------0.exit---------\n");
printf("---------------------\n");
}
void test()
{
srand((unsigned)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("离开游戏");
break;
default:
printf("输入错误,请重新输入");
break;
}
} while(input);
}
int main()
{
test();
return 0;
}
//game.c下的全部代码
#include"game.h"
//初始化棋盘
void Initboard(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++)
{
board[i][j] = ' ';
}
}
}
//展示棋盘
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 Player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋>");
int x = 0;
int y = 0;
while (1)
{
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 Computer_move(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 is_full(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;
}
//玩家胜返回'*',电脑胜返回'#',平局返回'p',继续返回'c'
char is_win(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][1] != ' ')
return board[i][1];
}
//列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
return board[1][i];
}
//对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
return board[1][1];
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[1][1];
//平局
if (1 == is_full(board, ROW, COL))
return 'p';
//继续
return 'c';
}
运行一下看看效果:
基本上可以实现三子棋的功能,不过这里仍有不足,我们可不可以赋予电脑智能化呢?就是让电脑有自己的思想,可以和我们进行pk,而不只是单纯地随机生成数字下棋。这些下去我们仍需要思考,在这里就不做过多的解释了。