目录
前言
注意看,这个男人叫小帅,现在,他正在敲代码实现和机器人下三子棋,终于小帅敲好了代码,他当即与机器人下了一局,然而小帅发现,这机器人竟然是......
一、创建文件项目
时间回到小帅写代码时的第一步,小帅首先创建项目以text.c为核心源文件,通过调用game.c和game.h内的函数实现三子棋的所有功能。
二、三子棋具体实现步骤
1.呈现菜单,并实现玩家可一直游玩游戏
1:代表进入游戏
0:代表退出游戏
菜单函数的代码如下(示例):
//text.c源文件
#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);//请玩家选择进入游戏or退出游戏
switch (input) //采用switch语句较为便捷
{
case 1:
printf("三子棋\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择:\n");
break;
}
} while (input);//0为假,非0为真,即进入游戏输入1,1作为判断条件为真,游戏继续
}
int main()
{
test();
return 0;
}
效果:
2.选择进入游戏后,打印棋盘
小帅的目标:打印九宫格简易棋盘,并且能够在每个指定格子内存放棋子(即数据)
2.1初始化棋盘
小帅的“头脑风暴”1: 要在九宫格内存放棋子还要打印在屏幕上,我应该用3*3大小的二维数组 ,将每个数组元素发在九宫格的每个格子中间,但是呢,要注意,如果我只将棋盘的形状打印屏幕上,后续玩家在指定下棋时棋子是不能覆盖原先屏幕上的符号的,所以我在打印棋盘时就应该保留各个棋子应存放的空间,并存放空格来等待玩家或电脑下的棋子。emm,就是这样!
为了使后续棋盘大小不局限于 3行3列 大小,小帅宏定义ROW 、 COL,如果后续想要改动棋盘大小仅需改变宏定义的ROW 、COL大小即可。宏定义名称与值时替换关系。
加上初始化函数的代码如下(示例):
//game.h头文件
#define ROW 3
#define COL 3
#include<stdio.h>
void InitBoard(char board[ROW][COL], int row, int col);
//game.c源文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"//调用自定义头文件
void InitBoard(char board[ROW][COL], int row, int col)//定义初始化棋盘函数
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
//text.c源文件
#include "game.h"//调用自定义的头文件
void game()//定义game函数
{
//初始化棋盘,用函数实现
//传入数组、行数、列数,以便初始化
InitBoard(board, ROW, COL);//数组内全部空格
}
void test()
{
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);
}
int main()
{
test();
return 0;
}
2.2打印棋盘
小帅发现
棋盘第一行为:
空格 %c 空格 | 空格 %c 空格 | 空格 %c 空格
找最小重复单元,发现为:空格 %c 空格 |,要注意的是第三个 | 不需要打印,用if判断即可
第二行为:
- - - | - - - | - - -
找最小重复单元,发现为:- - - |,同样的,要注意的是第三个 | 不需要打印,用条件控制一下即可
并且最后一行无需打印: - - - | - - - | - - -
将以上推断结果封装为打印函数
加上打印函数的代码如下(示例):
//game.h头文件
#define ROW 3
#define COL 3
#include<stdio.h>
void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
//game.c源文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)//定义//初始化棋盘函数
{
int i = 0, 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++)
{
//1.打印一行数据
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);//空格 棋子 空格
if (j < col - 1)
printf("|");
}
printf("\n");
//2.打印分割行
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if(j<col-1)
printf("|");
}
printf("\n");
}
}
}
//text.c源文件
#include "game.h"//调用自定义的头文件
void game()//定义game函数
{
//初始化棋盘,用函数实现 并传入数组、行数、列数,以便初始化
InitBoard(board, ROW, COL);//数组内全部空格
//打印棋盘
DisplayBoard(board, ROW, COL);
}
void test()
{
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);
}
int main()
{
test();
return 0;
}
最后每当玩家或电脑下棋后都要更新打印一次棋盘
3.打印棋盘后,玩家和电脑开始轮番下棋
3.1 实现玩家下棋,棋子为 *
小帅的“头脑风暴”2:
下棋实质上是为二维数组赋值,而我们赋值时就有了诸多限制:
- 我要下的格子内是否已经被下过了?
- 非编程玩家是否知道二维数组第一行和第一列下标是从0开始?
- 玩家给出的行和列是否超过所给棋盘的大小了?
无所谓,小帅会出手!
所以,我们要解决以上问题
- 判断要下的格子内是否已经被下过了,可以判断该数组元素是否为空格,因为我们之前已经初始化所有数组内容为空格,一旦有棋子落下,该数组元素就会被赋以棋子符号,就不为空格。
- 玩家默认第一行和列都从1开始,那么我们将玩家输入的行列数减一即可
- 这里我们就要有限制条件,玩家给出的行和列应小于等于棋盘的行列数,并且大于等于1。如果不合法,进行提示。
玩家下棋函数的代码如下(示例):
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
printf("玩家走:>\n");
while (1)
{
printf("请输入要下的坐标:>");
scanf("%d%d", &x, &y);
//判断xy坐标的合法性
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");
}
}
}
将玩家下棋函数声明、函数调用、函数定义分别放至game.h、test.c中的game()函数中、game.c中即可。这里为了避免篇幅过长将最后展现全部代码。
3.2 电脑下棋,棋子为 #
小帅让电脑随机下棋,使用srand(time(NULL)),将rand()%row赋值给电脑下棋的行数,将rand()%col赋值给电脑下棋的列数。
电脑下棋函数的代码如下(示例):
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
printf("电脑走:>\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
将电脑下棋函数声明、函数调用、函数定义分别放至game.h、test.c中的game()函数中、game.c中即可,将srand(time(NULL))放至test.c的test()函数中。这里为了避免篇幅过长将最后展现全部代码。
4.判断输赢
小帅的头脑风暴3:
定义判断输赢函数,在玩家和电脑每次下棋后都要判断一次输赢
- 电脑赢返回 #
- 玩家赢返回 *
- 平局返回 Q
- 游戏继续返回 C
玩家或电脑赢需要满足三个棋子相同并且连成一条线,注意排除空格情况:
- 同一行
- 同一列
- 对角线
平局情况只需要判断棋盘是否还剩有空格即可
判断输赢函数代码如下(示例):
//返回1表示棋盘满了
//返回0表示棋盘没满
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
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;
int j = 0;
//横三行
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == board[i][j+1] && board[i][j+1] == board[i][j+2] && board[i][j] != ' ')
{
return board[i][j];
}
}
}
//竖三列
for (i = 0; i < col; i++)
{
for (j = 0; j < row; j++)
{
if (board[j][i] == board[j+1][i] && board[j+1][i] == board[j+2][i] && board[j][i] != ' ')
{
return board[j][i];
}
}
}
//两个对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[0][0];
}
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
{
return board[1][1];
}
//判断是否平局
if (1 == IsFull(board, ROW, COL))
{
return 'Q';
}
else
return 'C';
}
将输赢判断函数声明、函数调用、函数定义分别放至game.h、test.c中的game()函数中、game.c中即可。无所谓,小帅会继续出手!
5.整理代码
//game.h头文件
#define ROW 3
#define COL 3
#include<stdio.h>
#include<stdlib.h>
#include<time.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);
//告诉我们四种状态
//玩家赢--'*
//电脑赢--'#
//平局----'Q'
//继续----'C'
char Iswin(char board[ROW][COL], int row, int col);
//game.c源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0, 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++)
{
//1.打印一行数据
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
printf("|");
}
printf("\n");
//2.打印分割行
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, y = 0;
printf("玩家走:>\n");
while (1)
{
printf("请输入要下的坐标:>");
scanf("%d%d", &x, &y);
//判断xy坐标的合法性
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, y = 0;
printf("电脑走:>\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//返回1表示棋盘满了
//返回0表示棋盘没满
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
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;
int j = 0;
//横三行
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == board[i][j+1] && board[i][j+1] == board[i][j+2] && board[i][j] != ' ')
{
return board[i][j];
}
}
}
//竖三列
for (i = 0; i < col; i++)
{
for (j = 0; j < row; j++)
{
if (board[j][i] == board[j+1][i] && board[j+1][i] == board[j+2][i] && board[j][i] != ' ')
{
return board[j][i];
}
}
}
//两个对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[0][0];
}
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
{
return board[1][1];
}
//判断是否平局
if (1 == IsFull(board, ROW, COL))
{
return 'Q';
}
else
return 'C';
}
//text.c源文件
#define _CRT_SECURE_NO_WARNINGS
//测试三子棋游戏
#include "game.h"
#include<stdio.h>
void menu()
{
printf("*****************************\n");
printf("************1.play***********\n");
printf("************0.exit***********\n");
printf("*****************************\n");
}
//游戏的整个算法实现
void game()
{
char ret = 0;
//数组-存放走出的棋盘信息
char board[ROW][COL] = { ' ' };//数组内全部空格
//初始化棋盘,用函数实现
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
//下棋
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 (ret == 'Q')
{
printf("平局\n");
}
}
void test()
{
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);
}
int main()
{
test();
return 0;
}
效果
总结
小帅的代码结束,发现自己实现的电脑竟然是个人工智障(给它机会它都不中用的那种),所以我们发现这里有诸多待优化的点,例如:
- 判断三连子时,棋盘大小不为3*3,那该如何判断对角线三连子?
- 我们是否可以优化电脑,赋以人性,让它有玩家的思维赢下玩家?
小帅把问题留给我们,这次...他腾不出手了
最后,如果小伙伴们有优化的方案,请在评论区留言(ps:抱大佬的腿),新手创作,实属不易,如果满意,还请给个免费的赞,三连也不是不可以(流口水幻想),嘿!那我们下期再见喽,拜拜!