三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了,但是三子棋在很多时候会出现和棋的局面。
这个问题也困扰了我很久,如果我和朋友一起玩的话,肯定是和棋,但人工智障我有概率能赢呀,计算机的运行是由人预先编制的,很简单,因为我还没学到Alphago那种强人工智能,只能欺负欺负简单的人工智障了。(ps.关于计算机的运行为什么是人预先编制的后期我会出一篇博客,关注我,不迷路~~)
~~~~~~~~~~~~~~~~~~~~~~~~~~正文开始~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
要实现此游戏,需要创建几个模块,当然是为了方便啦,一个头文件和两个源文件。
void menu() {
printf("\n");
printf("这是个三子棋的游戏,冒险者你准备好了吗?\n");
printf("如果你准备好了请扣个1,没准备好请扣个0\n");
printf("1.直接进入\n");
printf("0.那退出吧\n");
}
void test() {
int input = 0;
do //这里是为了实现游戏的重复进行
{
int input = 0;
menu();
printf("请选择:>");
scanf("%d", &input); //玩家输入1或者0显示玩不玩游戏
switch (input) { //switch是分支选择是否开始玩游戏
case 1:
printf("开始游戏\n");
game(); //game()是这个游戏的程序开始玩的函数
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误\n");
break;
}
} while (input); //i是输入的是1或者0,此时,非0的意思是继续游戏,0是退出游戏
}
int main() {
menu();
test();
return 0;
}
大家到这里就有疑惑了,那个game();是个什么东东,这找了半天也没发现这个game();的表达式呀!客官且听我分析,在该文件中还有一个未定义的game();在抬头,后面会有模块写出game()函数的。
首先,在运用一个函数的时候,不仅仅要定义一个函数,而且还要声明它,就好比你辛辛苦苦写完了一本实验报告,但是忘记给老师了,老师就在实验报告这一栏的评分打零分。显而易见,三子棋的棋盘是一个3*3的二维数组。(ps.函数为何要声明,是因为计算机运行这个代码是从上往下逐级取指令,在后期我也会出一篇博客讲关于函数栈帧和函数声明和定义的博客)
代码如下:
#define ROW 3 //采用宏定义行(ROW),这是因为可以更改宏定义后面的数字使得玩N字棋
#define COL 3 //采用宏定义列(COL),理由如上
void chu_shi_hua_board(char board[ROW][COL], int row, int col) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] = ' '; //此时是定义棋盘里面的所有格子为空
}
}
}
此时大家可能有疑惑了,为什么这个函数就凭空出现了呢?大家不要着急,此函数的声明在头文件game.h.c中显示,详情看下面的代码。
那定义完棋盘了以后,发现,怎么打印了一堆空格?所以,接下来来进行在每个空格中打印分割线吧!
void da_yin_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++) { //i遍历行数
int j = 0;
for (j = 0; j < col; j++) { //j遍历列数
printf(" %c ", board[i][j]);
if (j < col - 1) { //最后一行多了一个|,用判断语句j
printf("|");
}
}
printf("\n");
if (i < row - 1) { //最下面多了个---
for (j = 0; j < col; j++) {
printf("---");
if (j < col - 1) { //最右边多了个|
printf("|");
}
}
printf("\n");
}
}
}在这里插入代码片
看到这里,大家可能又有疑惑了,这串代码什么意思?那就让我在下面放上两串代码:
printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
printf("---|---|---\n");
很轻松的看的出来,board[i][j]对应的是初始化中的空格,刚好对应“—”中的最中间的那个“-”,其中的if(j 在这里解释一下棋盘是以什么组合形式打印的: 正确的组合: 闲话不多说,直接上代码: 此处随机数解释过于多,简单介绍为模3是模出来余数,范围是0到2,后续我会出一篇博客来向大家介绍一下随机数! 如何判断输赢是此游戏最难的一关,大家要仔细看!四种情况:玩家赢,计算机赢,平局,游戏没结束(继续)。 哈哈哈,虽然那么多且听下篇分析,但这个game();里面的内容没有忘记!上代码! game.h.c test.c game.c 人工智障怎么那么笨啊!玩了那么多把它就是赢不了! 这个代码看似很完善,可是,有一个小bug,没有处理出来当电脑先下棋还是玩家先下棋,欲知后事如何,且看下篇博客~~~~~~~~ 客官,都读到这边了,给个三连吧 ~
错误例子:
这个组合形式有点诡异,如果你仔细一想,这怎么把顶上三个组合在一起了?如果宏定义定的行列不是3呢?是5呢?是10呢?结果发现只有三列!是不对滴!
以一个大格子和一个竖线所组合的为一个组合,是正确滴!(三)玩家下棋
void wan_jia_turn_board(char board[ROW][COL], int row, int col) {
printf("玩家下棋\n");
while (1) { //while(1)就是个死循环,永远满足条件,永远循环,因为1(非0)为真
printf("请输入要下棋的坐标点位:>");
int x = 0;
int y = 0;
scanf("%d %d", &x, &y); //点的坐标
if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { //程序小白不知道数组是从0开始的,故从1开始,下面要减去1
if (board[x - 1][y - 1] == ' ') {
board[x - 1][y - 1] = '*'; //空格就代表只有个空格,加上玩家下的棋子
break;
}
else {
printf("坐标点位被占用,请重新输入\n");
}
}
else {
printf("输入非法\n");
}
}
}
(四)计算机下棋
void ji_suan_ji_turn_board(char board[ROW][COL], int row, int col) {
printf("计算机下棋\n");
while (1) {
int x = rand() % row; //x为rand模上个row范围为(0到2)
int y = rand() % col; //范围也是(0到2)
if (board[x][y] == ' ') {
board[x][y] = 'o';
break;
}
}
}
(五)判断输赢
//判断棋盘是不是满了(欲知static为何物,之后博客来讲解)
static 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++) {
return 0;
}
}
return 1;
}
//判断谁赢
char shei_ying(char board[ROW][COL], int row, int col) {
//情况过于多,横相等,竖相等,两个斜对角线中任一一条对角线相等
int i = 0;
//三列相等
for (i = 0; i < row; i++) {
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') { //三列当中任一一列是相等的且任一一个元素为空格都不行
return board[0][i]; //细节是返回的元素正好是判断电脑还是玩家赢的符号
}
}
//三行相等
for (i = 0; i < col; i++) {
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') {
return board[i][0];
}
}
// \样的对角线相等
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 (is_full(board, row, col) == 1) {
return 'P';
}
//继续,谁都没赢,也没平局
return'C';
}
(六)game();
void game() {
char ret = 0;
//先声明一个二维数组
char board[ROW][COL];
//初始化棋盘使其为全空格
chu_shi_hua_board(board, ROW, COL);
//打印棋盘的横线与竖线
da_yin_board(board, ROW, COL);
while (1) { //死循环,玩家和计算机交替下棋
//玩家下棋
wan_jia_turn_board(board, ROW, COL);
//打印棋盘的横线与竖线
da_yin_board(board, ROW, COL);
//判断谁赢了的函数
ret = shei_ying(board, ROW, COL);
if (ret != 'C') {
break; //跳出此次循环
}
//计算机下棋的函数
ji_suan_ji_turn_board(board,ROW,COL);
//打印棋盘的横线与竖线
da_yin_board(board, ROW, COL);
//判断输赢
ret = shei_ying(board, ROW, COL);
if (ret != 'C') {
break; //跳出此次循环
}
}
if (ret == 'o') {
printf("电脑赢\n");
}
else if (ret == '*'){
printf("玩家赢\n");
}
else if (ret == 'P') {
printf("平局\n");
}
}
//电脑赢:o
//玩家赢:*
//平局:P
//游戏继续:C
代码汇总
#pragma once
#define ROW 3 //采用宏定义行(ROW),这是因为可以更改宏定义后面的数字使得玩N字棋
#define COL 3 //采用宏定义列(COL),理由如上
//所需要用的库函数(库函数也是由其他函数创立的,涉及到了函数栈帧的创建与销毁)
#include
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h.c"
void menu() {
printf("\n");
printf("这是个三子棋的游戏,冒险者你准备好了吗?\n");
printf("如果你准备好了请扣个1,没准备好请扣个0\n");
printf("1.直接进入\n");
printf("0.那退出吧\n");
}
void game() {
char ret = 0;
//先声明一个二维数组
char board[ROW][COL];
//初始化棋盘使其为全空格
chu_shi_hua_board(board, ROW, COL);
//打印棋盘的横线与竖线
da_yin_board(board, ROW, COL);
while (1) { //死循环,玩家和计算机交替下棋
//玩家下棋
wan_jia_turn_board(board, ROW, COL);
//打印棋盘的横线与竖线
da_yin_board(board, ROW, COL);
//判断谁赢了的函数
ret = shei_ying(board, ROW, COL);
if (ret != 'C') {
break; //跳出此次循环
}
//计算机下棋的函数
ji_suan_ji_turn_board(board,ROW,COL);
//打印棋盘的横线与竖线
da_yin_board(board, ROW, COL);
//判断输赢
ret = shei_ying(board, ROW, COL);
if (ret != 'C') {
break; //跳出此次循环
}
}
if (ret == 'o') {
printf("电脑赢\n");
}
else if (ret == '*'){
printf("玩家赢\n");
}
else if (ret == 'P') {
printf("平局\n");
}
}
//电脑赢:o
//玩家赢:*
//平局:P
//游戏继续:C
void test() {
srand(time(NULL));
int input = 0;
do //这里是为了实现游戏的重复进行
{
menu();
printf("请选择:>");
scanf("%d", &input); //玩家输入1或者0显示玩不玩游戏
switch (input) { //switch是分支选择是否开始玩游戏
case 1:
/*printf("开始游戏\n");*/
game(); //game()是这个游戏的程序开始玩的函数
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误\n");
break;
}
} while (input); //i是输入的是1或者0,此时,非0的意思是继续游戏,0是退出游戏
}
int main() {
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h.c"
//初始化空的棋盘
void chu_shi_hua_board(char board[ROW][COL], int row, int col) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] = ' '; //此时是定义棋盘里面的所有格子为空
}
}
}
//打印棋盘当中的横线和竖线,使得棋盘更加简洁明了
void da_yin_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++) { //i遍历行数
int j = 0;
for (j = 0; j < col; j++) { //j遍历列数
printf(" %c ", board[i][j]);
if (j < col - 1) { //最后一行多了一个|,用判断语句j
总结
话回正题,此串代码虽然很长很复杂,但是思路很清晰,按照思路来,终有一刻会敲好的,在设计这个代码的时候,要进行分区设计,这样子会让程序更加清晰,还有很多复杂的需要细节的小程序块需要大家多多注意,只要肯吃苦,代码就能敲好!亟须解决的问题
~谢谢