首先我们给出代码,结合代码我们进行解释:
首先,我们可以将游戏的进入及退出写入一个test函数,然后在主函数里只需写test();就可以访问了,主函数内不需要写太多的东西。
void test() { int input = 0; do { menu(); printf("请选择>:"); scanf("%d", &input); system("cls"); switch (input) { case 1: game(); break; case 0: printf("退出游戏n"); break; default: printf("输入错误,请重新输入!n"); } } while (input); }
这是test函数内部,通过输入一个input变量来控制游戏的进入和退出,menu则是一个简易的的游戏菜单,提示输入的数代表的功能。输入1来进入game()函数,输入0来退出游戏,输入其他数时default提示输入错误,用do while()循环结构,input非零循环继续,为零退出游戏。而system("cls")是清空屏幕,可以先不管,写不写无所谓,主要是改善游戏体验的。
下面是menu函数:
void menu() { printf("************************************************************n"); printf("**1.Start 0.Exit**n"); printf("************************************************************n"); }
简易的游戏菜单。
当输入1时,进入game()函数,在game()函数内实现游戏的逻辑。
下面是game()函数:
void game() { char result = 0; //储存棋盘信息 char board[ROW][COL] = { 0 }; //初始化棋盘 InitBoard(board, ROW, COL); //打印棋盘 DisplayBoard(board, ROW, COL); while (1) { //玩家下棋 PlayerMove(board, ROW, COL); system("cls"); DisplayBoard(board, ROW, COL); //判断输赢 result = IsWin(board, ROW, COL); if (result != 'C') { break; } //电脑下棋 Sleep(500); ComputerMove(board, ROW, COL); system("cls"); DisplayBoard(board, ROW, COL); result = IsWin(board, ROW, COL); if (result != 'C') { break; } } if (result == '*') { system("cls"); DisplayBoard(board, ROW, COL); printf("玩家获胜n"); } if (result == '#') { system("cls"); DisplayBoard(board, ROW, COL); printf("电脑获胜n"); } if (result == 'E') { system("cls"); DisplayBoard(board, ROW, COL); printf("平局n"); } }
同样,里面的system("cls");可忽略。然后,我们创建一个game.h文件来进行game()函数内部出现的函数的定义。再写一个game.c文件用来实现game.h内的函数。当用到自己定义的头文件时,写为#include "game.h"然后在game.c和test.c的首部写上#include "game.h",不妨把库函数也包含在game.h内部,这样只需引用一个game.h就行了。
下面是game.h内部:
#pragma once #include#include #include #include #define ROW 3 #define COL 3 void InitBoard(char board[ROW][COL], int row, int col); void DisplayBoardw(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); int IsFull(char board[ROW][COL], int row, int col);
结合前面的game函数内部,每个函数所需要的参数是一个二维数组,所以我们向函数里传的是一个数组,然后传入数组的行和列,分别用数组和两个整形接收。为了能规定棋盘的大小以及易于改变,所以我们在game.h内部用了宏定义,将二维数组的行(ROW)和列(COL)进行了规定
游戏逻辑在game函数内,下面我们对具体函数的实现进行说明:
首先我们需要一个二维数组来储存棋盘信息而下棋时,我们用”*“来代表玩家落子,用"#"来代表电脑落子,所以我们创建的二维数组是char类型的用来存放字符。
第二初始化棋盘,即给数组的每个元素初始化为空格,下面是代码:
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"); } } }
先打印一行的数据,每个元素为空格、数组中元素内容、空格,然后再打印一个竖杠,而第三列的时候我们不需要最后的竖杠,所以给打印竖杠的前面加上限制条件,当 j < col - 1时才打印竖杠,然后一行就打印完了,每行打印完打一个换行,确定为三行三列,之后打印分割行,即---,而当最后一行时,不需要打印分割行,所以也要给打印分割行时加上限制条件,即i < row - 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"); } } }
即玩家落子,选中坐标,若所选的地方在数组范围内,则继续判断,否则输出非法输入,若在棋盘范围内,则继续判断是否为空格,即是否已经落子,没有的话就将空格改为*,否则输出被占用信息。
注意输入的是位置,而数组的下标是从零开始,所以在以下标形式访问数组元素时输入的x和y要分别减一。
将此过程放入循环内,一旦落子成功便跳出循环。
接下来我们说电脑下棋,由于只是简单的实现,所以我们给电脑的坐标设值是随机数,即x = rand(),但是要在数组范围内,所以分别模上row和col,确保只产生0到2的数所以x = rand() % row
y = rand() % col。而要使用随机数,就要使用srand((unsigned int)time(NULL));设置随机起点,只需设一次,所以放在main函数内部。
int main() { srand((unsigned int)time(NULL)); test(); return 0; }
而对电脑的落子,只需判断所下的地方是否被占用即可,因为不可能超出范围。
放到循环里直到落子成功后break跳出循环。
最主要的是判断部分:
char IsWin(char board[ROW][COL], int row, int col) { int i = 0; int j = 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 (j = 0; j < col; j++) { if ((board[0][j] == board[1][j] && board[1][j] == board[2][j]) && board[1][j] != ' ') { return board[1][j]; } } if ((board[0][0] == board[1][1] && board[1][1] == board[2][2]) && board[1][1] != ' ') { 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) == 1) { return 'C'; } else { return 'E'; } }
判断三行相等、三列相等、对角线相等。
我们规定若相等而且不能等于空格,我们就返回相等的三个里面的任意一个,如玩家赢为返回*,电脑赢为返回#。还有就是棋盘下满为平局,我们返回E,二者都没赢而且棋盘没满就返回C,而在每次玩家落子之后、电脑落子之后都要判断。
玩家下棋和电脑下棋是一个循环往复,直到游戏结束的过程,所以是循环结构,当返回的不是C即说明游戏结束了,所以跳出来判断,若返回为*玩家赢,返回为#电脑赢,返回为E平局。
而判断棋盘是否满了,就要再写一个函数:
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 1; } } } return -1; }
遍历数组,发现有空格就返回1,没有即返回-1,有空格说明棋盘没满。
若返回1,在判断函数里就返回字符C,若返回-1,在判断函数里就返回E,注意判断满没满是要在判断了赢没赢之后的。
以上就是对三子棋的C语言实现的过程解释,水平有限,欢迎指正。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)