本文主要是介绍数组+函数+循环就能实现的三子棋和五子棋(动态匹配输赢),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
设计思路:
敲代码的时候要分类文件,这样看起来更加清晰简洁。
三子棋
源文件的实现:
实现menu()
实现Game()
实现头文件:
实现game.c
数组的初始化
棋盘的打印
玩家下棋
电脑下棋
判断输赢
完整代码:
game.h
源文件
game.c
五子棋
游戏的核心来了:动态匹配输赢
我们先来看 行:
再来看列:
对角线左上-右下
对角线右上-左下
动态匹配输赢的完整代码
棋盘更改后的代码
设计思路:
游戏的主体部分:
1.我们下棋,首先需要棋盘。
2.我们落的棋子需要存放。
3.玩游戏:玩家下棋,电脑下棋(又或者第二个玩家下棋)
4.判断输赢
敲代码的时候要分类文件,这样看起来更加清晰简洁。
头文件的相关知识都在这,点"我"即可
1.专门实现我们游戏的函数,放在一个文件中 --->game.c
2.一个头文件,专门存放一些函数声明,宏定义,库函数的头文件--->game.h
3.主函数所在的源文件。
三子棋
源文件的实现:
就像我们玩小游戏一般,进入游戏先给你菜单-->选择玩与不玩-->进入游戏/退出游戏/输入错误
int main()
{int input = 0;do{menu();//菜单printf("请选择输入:>");scanf("%d", &input);switch (input){case 1:Game();break;case 0:printf("游戏结束\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);
}
根据我们所示的代码,我们需要分别去实现menu(),和Game()。
实现menu()
简简单单随你怎么设置
void menu()
{printf("************************\n");printf("************************\n");printf("******* 1.play *******\n");printf("******* 0.exit *******\n");printf("************************\n");
}
实现Game()
开篇的时候呢,我们写了游戏的大体设计思路,我们暂且就按照那里实现,一些其他细节写
的时候在进行修修改改
void Game()
{char board[ROW][COL] = { 0 };//定义一个二维数组,用来存棋子Init_board(board, ROW, COL);//初始化数组Display_board(board, ROW, COL);//打印棋盘Player_Play(board, ROW, COL);//玩家下棋 Is_win(board, ROW, COL);//判断输赢 Computer_Play(board, ROW, COL);//电脑下棋}
在使用函数的时候,我们要去声明,我们将这些声明放入头文件中。是不是发现有用到
ROW,COL(行列),这是我们定义的宏,更加简便,如果你要改动行和列的话,不设置
宏,那就需要到每一个函数中删删改改,容易出问题,定义宏,我们可以一劳永逸。
实现头文件:
右击源文件/头文件-->添加-->新有项-->选择头文件
(编译器是VS2019)
#include<stdio.h>//定义的宏
#define ROW 3
#define COL 3//函数声明//数组初始化
void Init_board(char board[ROW][COL], int row, int col);
//打印棋盘
void Display_board(char board[ROW][COL], int row, int col);
//玩家下棋
void Player_Play(char board[ROW][COL], int row, int col);
//电脑下棋
void Computer_Play(char board[ROW][COL], int row, int col);
//判断能否赢
char Is_win(char board[ROW][COL], int row, int col);
实现game.c
右击头文件/源文件-->添加-->新有项-->选择c++(文件)
(编译器是VS2019)
数组的初始化
这个比较简单,双层循环。初始化成空格。
void Init_board(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] = ' ';}}}
棋盘的打印
我们先来看看棋盘的样子,看你怎么设计的,你就怎么实现
看棋盘的样子,我们肯定能想到是双层循环,然后实现这些字符“ %c ” , "|" ,“---” 的组合。%c,这个位置是我们的棋子。
写的时候要注意最右边是没有"|",最后一行的时候是没有“---”,写的时候我们要把握好控制条件。
void Display_board(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){for (j = 0; j < col; j++){printf("---");if(j<col-1)printf("|");}printf("\n");}}
}
玩家下棋
在写的时候,我们需要注意的是,没学编程的人肯定不知道数组下标是从0开始的,因此在我们设计的时候呢,输入的坐标要从1开始。
玩家输入坐标后有哪些需要注意的呢?
1.坐标的合法性,假如我是3*3的棋盘,那坐标应该是x:1-3,y:1-3
2.输入的坐标,坐标处有没有被占用,这也是我们要考虑的。
3.输入坐标之后,我们是不是得看下我们的棋子落在哪里呢,所以需要去打印棋盘
4.在输入不合法的坐标的时候,需要重新输入。这样一理,我们会想到要用循环。
void Player_Play(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)//判断坐标的合法性 x:1-3 y:1-3{if (board[x - 1][y - 1] == ' ')//判断坐标处有没有被占用{board[x - 1][y - 1] = '*';//下棋Display_board(board, ROW, COL);//打印棋盘printf("\n");break;}else{printf("坐标处已被占用,重新输入\n");}}else{printf("坐标输入错误,重新输入\n");}}
}
电脑下棋
电脑下棋的时候,需要注意一下的就是坐标的产生。
随机数的产生我们只要记住一个组合就可以了。
rand() --产生随机数,srand((unsigned int)time(NULL))--作用就是让rand()产生时时刻刻变化的随机数。
如果没有srand((unsigned int)time(NULL)),每一次rand()产生的数都是固定的。
time(NULL)是一个时间戳,时时刻刻都在变化,这样srand()接收的数一直在变化,因此rand()产生的数不会固定。
srand((unsigned int)time(NULL)); 这个只要调用一次就可以了,我们就放入主函数之中
我们需要产生0-2(0,1,2)之间的数(根据我们的行和列来考虑的),rand()/row的余数就是我们想要的值,所以我们就用%
也需要判断,坐标有没有被占用,输入完之后也要打印棋盘
void Computer_Play(char board[ROW][COL], int row, int col)
{printf("电脑走:\n");//x,y 放在循环里面和循环外面有什么区别嘛?有区别。// 如果x y和我们玩家输入的一样,不在循环里的话,x,y就不能改变// 程序会出问题//int x = rand() % row; //0-2//int y = rand() % col;while (1){int x = rand() % row; //0-2int y = rand() % col;if (board[x][y] == ' ')//判断坐标处有没有被占用{board[x][y] = '#';Display_board(board, ROW, COL);//打印棋盘printf("\n");break;}}
}
判断输赢
我们想一下游戏的结果有哪些?
玩家赢或者电脑赢:有三个字符一样(不是' ')。
平局:棋盘写满了,又或是不是其他三种情况的时候
游戏继续:不是上面三种情况,就是游戏继续,又或者是,棋盘中没有满并且没有人获胜的时候,游戏继续。
在什么时候判断输赢?
是每下一个棋子,我们都要去判断一下,有没有赢
要是赢了,游戏结束。如果没赢,需要重复下棋,因此我们需要用到循环。
怎么判断输赢呢?
看我们的返回值,如果返回的是‘*’ --->玩家赢, ‘#’--->电脑赢,‘P--->平局
'C'--->游戏继续。
前面两个,是在 玩家下棋,电脑下棋 的函数中,你输入的棋子是什么,我们的返回值就是什么。
后面两个就看你想让它返回什么,你就设置成什么。
这是Game()的删改。
void Game()
{char board[ROW][COL] = { 0 };Init_board(board, ROW, COL);//初始化数组Display_board(board, ROW, COL);//打印棋盘char ret = 0;while (1){Player_Play(board, ROW, COL);//玩家下棋 ret = Is_win(board, ROW, COL);//判断输赢if (ret != 'C')//只要不是游戏继续,游戏就结束 break;Computer_Play(board, ROW, COL);//电脑下棋ret = Is_win(board, ROW, COL);//判断输赢if (ret != 'C') break;}if ('*' == ret){printf("玩家赢\n");}else if ('#' == ret){printf("电脑赢\n");}else {printf("平局\n");}Display_board(board, ROW, COL);//游戏结束的时候在打印下棋盘}
Is_win()函数的实现
以下写法比较差,因为我们判断输赢的时候把它写死了,要是换一个4*4的棋盘就行不通了,动态版的稍后就会提到,也是我们能写出五子棋的关键。
// 判断 棋盘是否满了
//static int Is_Full(char board[ROW][COL], int row, int col)
//加入 static 使这个函数只能在game.c.存在,增加隐蔽性。
//{
// 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 Is_win(char board[ROW][COL], int row, int col)
{int i= 0;int j=0;//判断 游戏继续 不能放在前面 否则就会出现玩家游戏赢了,游戏仍然继续的bug,//又或者是电脑游戏赢了,游戏仍然继续的bug//行 三个元素 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][1];}}//列 三个元素 for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][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];//剩下 平局 和 继续 的时候, 这两者顺序随意。for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){if (board[i][j] == ' ')return 'C';}}return 'P';// 可以写一个Is_Full的函数判断是否满了 满了返回1,//否则返回0.接收的时候,ret==1,则返回P,else 则返回C 。 上面已经给出来了
}
完整代码:
game.h
#include<stdio.h>
#include<stdlib.h> //rand(),srand()的头文件
#include<time.h> //time(NULL)的头文件#define ROW 3
#define COL 3//函数声明//数组初始化
void Init_board(char board[ROW][COL], int row, int col);
//打印棋盘
void Display_board(char board[ROW][COL], int row, int col);
//玩家下棋
void Player_Play(char board[ROW][COL], int row, int col);
//电脑下棋
void Computer_Play(char board[ROW][COL], int row, int col);
//判断能否赢
char Is_win(char board[ROW][COL], int row, int col);
源文件
#include"game.h"
void menu()
{printf("************************\n");printf("************************\n");printf("******* 1.play *******\n");printf("******* 0.exit *******\n");printf("************************\n");
}void Game()
{char board[ROW][COL] = { 0 };Init_board(board, ROW, COL);//初始数组Display_board(board, ROW, COL);//打印棋盘char ret = 0;while (1){Player_Play(board, ROW, COL);//玩家下棋 ret = Is_win(board, ROW, COL);if (ret != 'C')//只要不是继续,游戏就结束 break;Computer_Play(board, ROW, COL);//电脑下棋ret = Is_win(board, ROW, COL);if (ret != 'C') break;}
//出循环后判断游戏结果if ('*' == ret){printf("玩家赢\n");}else if ('#' == ret){printf("电脑赢\n");}else {printf("平局\n");}Display_board(board, ROW, COL);//结束时在打印一下棋盘}
int main()
{srand((unsigned int)time(NULL));//产生随机种子int input = 0;do{menu();printf("请选择输入:>");scanf("%d", &input);switch (input){case 1:Game();break;case 0:printf("游戏结束\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);
}
game.c
#include"game.h"//初始化
void Init_board(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] = ' ';}}}//打印棋盘
// | |
// ---|---|---
// | |
// ---|---|---
// | |
void Display_board(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++)// 打印的是 %c |{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_Play(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)//判断坐标的合法性 x:1-3 y:1-3{if (board[x - 1][y - 1] == ' ')//判断坐标处有没有被占用{board[x - 1][y - 1] = '*';Display_board(board, ROW, COL);//打印棋盘printf("\n");break;}else{printf("坐标处已被占用,重新输入\n");}}else{printf("坐标输入错误,重新输入\n");}}
}//电脑下棋
void Computer_Play(char board[ROW][COL], int row, int col)
{printf("电脑走:\n");while (1){int x = rand() % row; //0-2int y = rand() % col;if (board[x][y] == ' ')//判断坐标处有没有被输入{board[x][y] = '#';Display_board(board, ROW, COL);//打印棋盘printf("\n");break;}}
}//判断棋盘满没满//static int Is_Full(char board[ROW][COL], int row, int col)
加入 static 使这个函数只能在game.c.存在,增加隐蔽性。
//{
// 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 Is_win(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;//判断 继续 不能放在前面 否则就会出现玩家游戏赢了,游戏仍然继续的bug,//又或者是电脑游戏赢了,游戏仍然继续的bug//行 三个元素 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][1];}}//列 三个元素 for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] ==board[2][j] && board[0][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];//剩下 平局 和 继续 的时候, 这两者顺序随意。for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){if (board[i][j] == ' ')return 'C';}}return 'P';// 可以写一个Is_Full的函数判断是否满了 满了返回1,//否则返回0.接收的时候,ret==1,则返回P,else 则返回C 。 上面已经给出来了
}
五子棋
五子棋和我们三子棋代码差不多。主要的差别就是判断输赢的时候要判断5个棋子是否相同。
游戏的核心来了:动态匹配输赢
这里我们能够实现的话,n子棋也是能够实现的。
我们拿五子棋来举例子:
那我们要怎么动态匹配输赢呢?
输赢能有多少种情况?,我们不知道,但是我们可以知道的是,五个连续的棋子,要么是在行连续,要么是在列连续,要么是在对角线上连续。
坐标我们设置成(i,j),棋盘的大小为 row*col 。
我们先来看 行:
每一行:五个连续的坐标它的行坐标没有变,变的是列坐标
我们可以这样写连续的五个行坐标:[ i ][ j ],[ i ][ j + 1 ],[ i ][ j + 2 ],[ i ][ j + 3 ],[ i ][ j + 4 ]
一看到这样的形式,是不是很像双循环呢?它就是双层循环
设置两个变量i,j,i控制行,j控制列。
我们在写循环时候呢,要注意到范围的问题。
写的时候最大列坐标不能超过我们的列的总数,即 j+4<col
我们来看代码
for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (j + 4 < col){if (board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2] && board[i][j + 2] == board[i][j + 3]&& board[i][j + 3] == board[i][j + 4] && board[i][j] != ' '){return board[i][j];}}}}
再来看列:
每一列:五个连续的坐标它的列坐标没有变,变的是行坐标,
同样的我们可以这样写出[ i ][ j ],[ i + 1 ][ j ],[ i + 2 ][ j ],[ i + 3 ][ j ],[ i + 4 ][ j ]
写的时候,同样的要注意到条件的设置
我们来看代码:
for (j = 0; j < col; j++){for (i = 0; i < row; i++){if (i + 4 < row){if (board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j] && board[i + 2][j] == board[i + 3][j] && board[i + 3][j] == board[i + 4][j] && board[i][j] != ' '){return board[i][j];}}}}
对角线左上-右下
最简单一组是[ 0 ][ 0 ],[ 1 ][ 1 ],[ 2 ][ 2 ],[ 3 ][ 3 ],[ 4 ][ 4 ]我们用它来举例。
我们已经知道它是双循环了,我们设想一下
j每加一,我们[0][0],[1][1],[2][2],[3][3],[4][4]这一组坐标,列坐标也会加+1,行不变,[0][1],[1][2],[2][3],[3][4],[4][5],[5][6]
i每加一,我们[0][0],[1][1],[2][2],[3][3],[4][4]这一组坐标,行坐标也会+1,列坐标不变。
[1][0],[2][1],[3][2],[4][3],[5][4]
综合上面的两种情况我们可以发现,不管怎么样,五个坐标之间的关系一定是这样的
[ i ][ j ],[ i + 1 ][ j + 1 ],[ i + 2 ][ j + 2 ],[ i + 3 ][ j + 3 ],[ i + 4 ][ j + 4]
写循环的时候我们同样得注意一下,条件的设置
for (i = 0; i < row; i++){if (i + 4 < row){for (j = 0; j < col; j++){if (j + 4 < col){if (board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2] && board[i + 3][j + 3] == board[i + 2][j + 2] && board[i + 3][j + 3] == board[i + 4][j + 4]&& board[i][j] != ' '){return board[i][j];}}}}}
对角线右上-左下
先看图:
我们可以发现,循环的时候,我们外层行是没变,变的是它的列,初始值不一样。
那我们设置循环的时候,i=0;j=col-1,去设置。(注意我们代码里j是数组的下标,是比我们列小1的)
我们也能很快的写出每个坐标的规律 [ i ][ j ],[ i + 1][ j - 1] ,[i + 2][j - 2],[i + 3][j - 3],[i + 4][j - 4]
书写时候条件需要主要到和上面都不同了。
for (i = 0; i < row; i++){if (i + 4 < row){for (j = col - 1; j >= 0; j--){if (j - 4 >= 1)//可以看看图就能够明白了{if (board[i][j] == board[i + 1][j - 1] && board[i + 1][j - 1] == board[i + 2][j - 2] && board[i + 3][j - 3] == board[i + 2][j - 2] && board[i + 4][j - 4] == board[i + 3][j - 3] && board[i][j] != ' '){return board[i][j];}}}}}
动态匹配输赢的完整代码
char Is_win(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;//行 5个元素 for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (j + 4 < col){if (board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2] && board[i][j + 2] == board[i][j + 3]&& board[i][j + 3] == board[i][j + 4] && board[i][j] != ' '){return board[i][j];}}}}//列 三元素for (j = 0; j < col; j++){for (i = 0; i < row; i++){if (i + 4 < row){if (board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j] && board[i + 2][j] == board[i + 3][j] && board[i + 3][j] == board[i + 4][j] && board[i][j] != ' '){return board[i][j];}}}} //对角线左上到右下for (i = 0; i < row; i++){if (i + 4 < row){for (j = 0; j < col; j++){if (j + 4 < col){if (board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2] && board[i + 3][j + 3] == board[i + 2][j + 2] && board[i + 3][j + 3] == board[i + 4][j + 4]&& board[i][j] != ' '){return board[i][j];}}}}}//对角线右上到左下for (i = 0; i < row; i++){if (i + 4 < row){for (j = col - 1; j >= 0; j--){if (j - 4 >= 1){if (board[i][j] == board[i + 1][j - 1] && board[i + 1][j - 1] == board[i + 2][j - 2] && board[i + 3][j - 3] == board[i + 2][j - 2] && board[i + 4][j - 4] == board[i + 3][j - 3] && board[i][j] != ' '){return board[i][j];}}}}}for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){if (board[i][j] == ' ')return 'C';}}return 'P';
}
当我们想要玩五子棋的时候,格子会非常多,要是我们在写的时候,可以有行号和列号的
话,游戏感会更好,大家可以试着写写,也是有些繁琐。
棋盘更改后的代码
void Display_board(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;//列号for(j=0;j<= col;j++)//等于不能少{if( j >= 10) //这里要写死,不能用col,在宏定义的时候我们改成不是两位数的话打印就有问题// 同样的我们行号那里也是得写死//if ( j > col)printf(" %d|", j);elseprintf(" %d |", j);}printf("\n");for (j = 0; j <= col; j++){printf("---");printf("|");}printf("\n");//打印行号//打印棋盘for (i = 0; i < row; i++){ if (i+1 >= 10 )printf(" %d|", i+1);elseprintf(" %d |", i+1);for (j = 0; j < col; j++)//这里没有等于{printf(" %c ", board[i][j]);printf("|");}printf("\n");if (i <= row - 1){for (j = 0; j <= col; j++){printf("---");printf("|");}printf("\n");}}
}
玩五子棋的时候呢,和电脑玩肯定没有挑战性,我们可以设置成双人小游戏。
在写一个玩家下棋的函数
五子棋的代码,和我们三子棋的差不多。
只要将我们的Is_win()换成动态的就可以了。
如果你想打行号和列号的话,在 打印棋盘 的函数,和原来的是有不同的。
有兴趣的可以写一个n子棋出来,大体差不多,最主要的区别在于Is_win()函数的设计
如果你看到这里了,很感谢你。头一次上万字,有人能看完,还是非常开心的。
这篇关于数组+函数+循环就能实现的三子棋和五子棋(动态匹配输赢)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!