一、整个程序的实现思路及设计
实现思路
1.打印一个简单的菜单供玩家选择如下:
2.玩家下棋
3.电脑下棋
4.判断输赢
5.循环菜单是否要继续游戏
使用多文件的方式编写
- test.c文件游戏的框架
- game.c文件游戏的实现
- game.h文件游戏函数的声明
二、test.c文件的设计
在主函数(main)中的设计
int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: //printf("三子棋\n"); game(); break; case 0: printf("退出游戏\n"); break; default: printf("请输入合理的选择\n"); break; } } while (input); return 0; }
使用menu()函数来打印菜单;
void menu() { printf("***********************************\n"); printf("************ 1.paly ********\n"); printf("************ 0.exit ********\n"); printf("***********************************\n"); }
使用do…while循环打印菜单;
使用srand()函数来随机生成电脑下的棋子;
使用switch根据玩家的选择来进行不同的实现;
游戏的实现放在game函数中;
void game() { //char board[3][3] = { 0 }; 初始化棋盘 //InitBoard(board, 3, 3); //定义一个数组用来存放数据 char board[ROW][COL] = { 0 }; //初始化棋盘 InitBoard(board, ROW, COL); //打印棋盘 DispalyBoard(board, ROW, COL); char ret = '0'; while (1) { //玩家下棋 PlayerMove(board, ROW, COL); //打印棋盘 DispalyBoard(board, ROW, COL); ret=IsWin(board, ROW, COL); if (ret!='c') { break; } //电脑下棋 ComputerMove(board, ROW, COL); //打印棋盘 DispalyBoard(board, ROW, COL); ret=IsWin(board, ROW, COL); if (ret != 'c') { break; } } if (ret=='*') { printf("玩家赢\n"); } else if (ret == '#') { printf("电脑赢\n"); } else { printf("平局\n"); } }
整个test.c文件的代码如下:
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void menu() { printf("***********************************\n"); printf("************ 1.paly ********\n"); printf("************ 0.exit ********\n"); printf("***********************************\n"); } void game() { //char board[3][3] = { 0 }; 初始化棋盘 //InitBoard(board, 3, 3); //定义一个数组用来存放数据 char board[ROW][COL] = { 0 }; //初始化棋盘 InitBoard(board, ROW, COL); //打印棋盘 DispalyBoard(board, ROW, COL); char ret = '0'; while (1) { //玩家下棋 PlayerMove(board, ROW, COL); //打印棋盘 DispalyBoard(board, ROW, COL); ret=IsWin(board, ROW, COL); if (ret!='c') { break; } //电脑下棋 ComputerMove(board, ROW, COL); //打印棋盘 DispalyBoard(board, ROW, COL); ret=IsWin(board, ROW, COL); if (ret != 'c') { break; } } if (ret=='*') { printf("玩家赢\n"); } else if (ret == '#') { printf("电脑赢\n"); } else { printf("平局\n"); } } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: //printf("三子棋\n"); game(); break; case 0: printf("退出游戏\n"); break; default: printf("请输入合理的选择\n"); break; } } while (input); return 0; }
三、game.h文件的设计
此文件主要用来声明函数如下:
#pragma once #include<stdio.h> #include<stdlib.h> #include<time.h> #define ROW 3 #define COL 3 //版本一没有使用对象宏来定义,以后修改更加的麻烦, //void InitBoard(char board[3][3], int row, int col); //初始化棋盘,放置空格 void InitBoard(char board[ROW][COL], int row, int col); //声明打印棋盘的函数 void DispalyBoard(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文件的设计
1.使用InitBoard()函数初始化棋盘
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] = ' '; } } }
2.使用DispalyBoard()函数打印棋盘
版本一:
void DispalyBoard(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++) { printf("%c", board[i][j]); } printf("\n"); } }
版本二:
void DispalyBoard(char board[ROW][COL], int row, int col) { int i = 0; for (i = 0; i < row; i++) { //打印数据 printf(" %c | %c | %c ", board[i][0], board[i][1], board[i][2]); printf("\n"); //打印分割行 if (i<row-1) { printf("---|---|---"); printf("\n"); } } }
版本三:
void DispalyBoard(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++) { printf(" %c ", board[i][j]); if (j<col-1) { printf("|"); } } printf("\n"); //打印分割行 最后一行不答应所以需要判断 if (i < row - 1) { int j = 0; for ( j = 0; j < col; j++) { printf("---"); //最后一个|不打印所以也需要判断 if (j<col-1) { printf("|"); } } printf("\n"); } } }
就以上三个版本来说:
我觉得最好的就是第三个版本,因为要是想改变棋盘的大小只需要在game.h中修改对象宏的大小即可,前两个版本都有一定的缺陷,我建议使用第三个版本来实现棋盘的打印,这样可以使代码的可读性和后期的修改更好。
3.使用PlayerMove()函数实现玩家下棋
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"); } } }
以上的代码进行了两次判断:
- 第一次是判断坐标输入是否合法(也就是坐标是否在棋盘的范围)
- 第二次是判断坐标是否被占用,要是被占用就使用循环重新输入;
4.使用ComputerMove()函数实现电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; x = rand() % row; y = rand() % col; printf("电脑下棋\n"); while (1) { if (board[x][y]==' ') { board[x][y] = '#'; break; } } }
其中使用了rand()函数它的作用是生成随机的值
- 使用rand()%row是来控制随机值的大小
- 同时还要再主函数中使用srand()函数来声明
- 再game.h头文件中还要引用两个头文件(stdlib.h和time.h)
5.使用IsWin()函数实现结果的判断
版本一:
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[0][0]; } 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 'Q'; } //继续 return 'c'; }
版本二:
char IsWin(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++) { //不为空字符进入判断 if (board[i][j] != ' ') { //如果列上连续的三个值相等就返回,注意j+2不能超过列 if (j < col - 2 && board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2]) { return board[i][j]; } //如果行上连续的三个值相等就返回,注意i+2不能超过行 else if (i < col - 2 && board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j]) { return board[i][j]; } //如果左斜线上连续的三个值相等就返回 else if (i < row - 2 && j < col - 2 && board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2]) { return board[i][j]; } //如果右斜线上连续的三个值相等就返回 else if (i >= 2 && j <= i && board[i][j] == board[i - 1][j + 1] && board[i - 1][j + 1] == board[i - 2][j +2]) { return board[i][j]; } } } } //平局 if (IsFull(board, row, col) == 1) { return 'Q'; } //继续 return 'c'; }
以上的两个版本我更加喜欢第二个版本:
- 当棋盘的大小改变的时候,也可以进行判断不需要再次修改代码
- 版本一只适合3*3的棋盘
综上考虑个人觉得第二个版本更加的好
6.使用IsFull()函数判断棋盘是否下满
int IsFull(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++) { if (board[i][j]==' ') { return 0; } } } return 1; }
下面是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] = ' '; } } } //版本1 //void DispalyBoard(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++) // { // printf("%c", board[i][j]); // } // printf("\n"); // } //} //版本二 //void DispalyBoard(char board[ROW][COL], int row, int col) //{ // int i = 0; // for (i = 0; i < row; i++) // { // 打印数据 // printf(" %c | %c | %c ", board[i][0], board[i][1], board[i][2]); // printf("\n"); // 打印分割行 // if (i<row-1) // { // printf("---|---|---"); // printf("\n"); // } // // } //} //版本三 void DispalyBoard(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++) { printf(" %c ", board[i][j]); if (j<col-1) { printf("|"); } } printf("\n"); //打印分割行 最后一行不答应所以需要判断 if (i < row - 1) { int 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; x = rand() % row; y = rand() % col; printf("电脑下棋\n"); while (1) { if (board[x][y]==' ') { board[x][y] = '#'; break; } } } //判断棋盘是否下满 int IsFull(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++) { if (board[i][j]==' ') { return 0; } } } return 1; } //判断结果的第二个版本 char IsWin(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++) { //不为空字符进入判断 if (board[i][j] != ' ') { //如果列上连续的三个值相等就返回,注意j+2不能超过列 if (j < col - 2 && board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2]) { return board[i][j]; } //如果行上连续的三个值相等就返回,注意i+2不能超过行 else if (i < col - 2 && board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j]) { return board[i][j]; } //如果左斜线上连续的三个值相等就返回 else if (i < row - 2 && j < col - 2 && board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2]) { return board[i][j]; } //如果右斜线上连续的三个值相等就返回 else if (i >= 2 && j <= i && board[i][j] == board[i - 1][j + 1] && board[i - 1][j + 1] == board[i - 2][j +2]) { return board[i][j]; } } } } //平局 if (IsFull(board, row, col) == 1) { return 'Q'; } //继续 return 'c'; }