三子棋游戏(巨详细)

时间:2023-01-24 22:58:00

游戏规则

三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利。

准备阶段

建立头文件game.h(函数声明)

建立源文件game.c(函数定义)、test.c(测试模块)

框架构建

test.c

要使游戏能反复游玩,创建游戏选择要循环

//菜单
void menu()
{
printf("********************\n");
printf("***1.play 0.exit***\n");
printf("********************\n");
}
//选择模式
void test()
{
int input=0;
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);
}
//对game ()的完善
void game()
{

}

分析

对game()的完善

1.创建储存信息的二维数组  并初始化

重要分析:我们在下棋的时候 需要对玩家和电脑所下的棋子有所标记(记录),所以我们需要一个二维数组“char board[3][3]”来储存棋子的信息

void game()
{
char board[3][3]={0};
}

完善二维数组

目的:为了将来我们对棋盘的控制 也许是五子棋 也许是九子棋,所以我们要对char board[3][3]={0}中的[3][3]进行优化 使将来改变棋盘的时候更好修改

game.h

#include<stdio.h>
#define ROW 3
#define COL 3

此时对二维数组来说就是

test.h

char board[ROW][COL]={0};

并且在test.h中引入头文件#include "game.h"

进一步分析:发现:在打印棋盘的时候 棋盘最开始是空的 可以打印“  ”(空格)

(棋盘上打印空格对棋盘进行初始化)










2.初始化棋盘

初始化棋盘:本质上就是初始化我们的数组,因为刚才我们需要的数组的作用就是用来储存棋子的信息的

建立初始化棋盘的函数

重要分析:要对数组进行初始化我们需要把数组传到一个函数里面去进行初始化;要访问数组的每行每列 我们也需要把行和列传过去进行初始化

test.h

InitBoard(board,ROW,COL);//初始化棋盘

game.h   进行函数声明

void InitBoard(char board[ROW][COL],int row,int col);

其中board[ROW][COL]作为真正的数组 int row,int col属于传过来的行和列

game.c

初始化棋盘的空格

void InitBoard(char board[ROW][COL],int row,int col)
{
//初始化棋盘的大空格
}

并且引入头文件#include "game.h"

初始化

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]=' ';//空格
}
}
}

此时的test.c部分

#include<stdio.h>
#include "game.h"
void menu()
{
printf("********************\n");
printf("***1.play 0.exit***\n");
printf("********************\n");
}

void game()
{
char board[ROW][COL]={0};//不打印空格就是0
InitBoard(board,ROW,COL);//初始化棋盘
}

void test()
{
int input=0;
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.打印棋盘

本质上就是更清晰的打印数组

    |    |    

---|---|---

    |    |    

---|---|---

    |    |    

(上图为理想效果图)

以第一个空格数据为例就是打印:空格-内容-空格

以第一行为例就是打印:大空格 | 大空格 | 大空格    (大空格指的是“空格-内容-空格”)(大空格也可以说成是数据)

下一行就是打印:---|---|---     (暂且称为称为分隔符)

.......以此循环

开始操作

test.h

DisplayBoard(board,ROW,COL);

game.h   进行函数声明

void DisplayBoard(char board[ROW][COL],int row,int col);

game.c

目的:1.打印一行的数据 ;2.打印分隔符

把第一行和分隔符看成一个整体(一组),只不过最后一组没了分隔符

    |    |    

---|---|---

注意引入头文件#include "game.h"#include<stdio.h>

粗糙版:

void DisplayBoard(char board[ROW][COL],int row,int col)
{
int i=0;
for(i=0;i<row;i++)//控制打印几组
{
printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]);//行
if(i<row-1)//若row=3,则只打印前两次的分隔符 第三组就不打印才能满足
{
printf("---|---|---");//分隔符
}
}

缺陷:如果将ROW COL的值改变 进而改变棋盘的行和列 棋盘的列不会满足所需的COL的要求

优化:(关键)

继续把第一行和分隔符看成一个整体(一组)

第一行: 数据 | 数据 | 数据 (其中“ 数据 |”又作为一个整体)

第二行:---|---|---     (分隔符)

void DisplayBoard(char board[ROW][COL],int row,int col)
{
int i=0;
for(i=0;i<row;i++)//一行一行开始
{
//1.先打印一行的数据

int j=0;//列(数据|数据|数据,每个“数据”就是列)
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");//分隔符的换行
}

}
}

注意for(i=0;i<row;i++)的顺序 要把整个循环走完才进行下一次的整个循环,其中打印分隔符的时候没有脱离循环

此时即使是改变ROW COL 的值 棋盘也会跟着改变

game.h

#include<stdio.h>
#define ROW 5
#define COL 5

三子棋游戏(巨详细)

此时的test.c部分

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include "game.h"
void menu()
{
printf("********************\n");
printf("***1.play 0.exit***\n");
printf("********************\n");
}

void game()
{
char board[ROW][COL]={0};//不打印空格就是0
InitBoard(board,ROW,COL);//初始化棋盘
DisplayBoard(board,ROW,COL); //打印棋盘
//等待下棋
}

void test()
{
int input=0;
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;
}

4.开始下棋

由于人与计算机要你一步我一步的下棋 所以我们认为下棋是个循环

(这里我们让玩家先下棋)

test.c

while(1)
{
//首先实现玩家下棋PlayerMove();就是把输入的数字放到数组某个坐标上去
PlayerMove(board,ROW,COL);
//电脑下棋

}

玩家下棋

game.h   函数声明

void PlayerMove(char board[ROW][COL],int row,int col);

game.c

具体解析在代码里

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);
//玩家输入的坐标可能超出了我们本身棋盘的大小 所以我们需要对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");
}
}
}

玩家走完后我们需要知道棋盘在玩家走后的样子 所以继续打印一个棋盘

test.c

while(1)
{
//首先实现玩家下棋PlayerMove();就是把输入的数字放到数组某个坐标上去
PlayerMove(board,ROW,COL);//玩家下棋
DisplayBoard(board,ROW,COL); //打印棋盘

//电脑下棋
}

电脑下棋

test.c

while(1)
{
//首先实现玩家下棋PlayerMove();就是把输入的数字放到数组某个坐标上去
PlayerMove(board,ROW,COL);//玩家下棋
DisplayBoard(board,ROW,COL); //打印棋盘

ComputerMove(board,ROW,COL);//电脑下棋
DisplayBoard(board,ROW,COL); //打印棋盘
}

game.h   函数声明

void ComputerMove(char board[ROW][COL],int row,int col);

game.c

void ComputerMove(char board[ROW][COL],int row,int col)
{
int x=0;
int y=0;
printf("电脑走:\n");//随即走
//生成随机rand()坐标
while(1)
{
x=rand()%ROW;//模上一个行 ;以题为例 模3就是0 1 2
y=rand()%COL;//模上一个列 ;以题为例 模3就是0 1 2
if(board[x][y]==' ')
{
board[x][y]='#';
}
break;
}
}

注:随机数的生成函数是rand(),在rand()之前一定要使用srand(),并且引用头文件#include<stdlib.h>,对于srand()参数的随机变量可以以时间的改变性为参照,即:srand((unsigned int)time(NULL));,并且引用头文件#include<time.h>(详细用法可查看“​​猜数字游戏(详细)​​”)

test.c

#include<stdio.h>
#include "game.h"
#include<stdlib.h>
#include<time.h>
void test()
{
int input=0;
srand((unsigned int)time(NULL));
......
}

game.c

void ComputerMove(char board[ROW][COL],int row,int col)
{
int x=0;
int y=0;
printf("电脑走:\n");//随即走
//生成随机rand()坐标
while(1)
{
x=rand()%ROW;//模上一个行 ;以题为例 模3就是0 1 2
y=rand()%COL;//模上一个列 ;以题为例 模3就是0 1 2
if(board[x][y]==' ')
{
board[x][y]='#';
}
break;
}
}

test.c

void game()
{
//数组-存放玩家和电脑走出的棋盘信息
char board[ROW][COL]={0};//不打印空格就是0
InitBoard(board,ROW,COL);//初始化棋盘
DisplayBoard(board,ROW,COL); //打印棋盘
while(1)
{
//首先实现玩家下棋PlayerMove();就是把输入的数字放到数组某个坐标上去
PlayerMove(board,ROW,COL);//玩家下棋
DisplayBoard(board,ROW,COL); //打印棋盘

//判断玩家是否赢IsWin();

ComputerMove(board,ROW,COL);//电脑下棋
DisplayBoard(board,ROW,COL); //打印棋盘

//判断电脑是否赢IsWin();

}
}

5.判断输赢

发现:如果要实现函数IsWin(); 

要返回四种状态:玩家赢“*”,电脑赢“#”,平局“ Q”,继续"C"

test.c

char ret=0来接收四种状态的返回值

void game()
{
char ret=0
//数组-存放玩家和电脑走出的棋盘信息
char board[ROW][COL]={0};//不打印空格就是0
InitBoard(board,ROW,COL);//初始化棋盘
DisplayBoard(board,ROW,COL); //打印棋盘
while(1)
{
//首先实现玩家下棋PlayerMove();就是把输入的数字放到数组某个坐标上去
PlayerMove(board,ROW,COL);//玩家下棋
DisplayBoard(board,ROW,COL); //打印棋盘

ret=IsWin();
//判断玩家是否赢IsWin();

ComputerMove(board,ROW,COL);//电脑下棋
DisplayBoard(board,ROW,COL); //打印棋盘

ret=IsWin();
//判断电脑是否赢IsWin();

}
}

判断框架

test.c

while(1)
{
//首先实现玩家下棋PlayerMove();就是把输入的数字放到数组某个坐标上去
PlayerMove(board,ROW,COL);//玩家下棋
DisplayBoard(board,ROW,COL); //打印棋盘

ret=IsWin(board,ROW,COL);
//判断玩家是否赢IsWin();
if(ret!=='C')
{
break;//游戏继续 给电脑下棋机会
}

ComputerMove(board,ROW,COL);//电脑下棋
DisplayBoard(board,ROW,COL); //打印棋盘

ret=IsWin();
//判断电脑是否赢IsWin();
if(ret!=='C')
{
break;//游戏继续 跳出总循环 判断是否有输赢
}

}

if(ret=='*')
{
printf("玩家赢\n");
}

else if(ret=='#')
{
printf("电脑赢\n");
}

else
{
printf("平局\n");
}

game.h    函数声明

char IsWin(char board[ROW][COL],int row,int col);

game.c

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][0]==board[i][2]&&board[i][0]!=' ')//避免还没开始下棋就有空格的情况 //board[i][0]!=' '只要判断的时候有一个不是空格就可以全部满足要求了
{
return board[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];
}
}

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[0][2]!=' ')//斜三
{
return board[0][2];
}

//以上三种情况有任何一个地方有返回 那么就有一方赢了
//如果没有 则说明要么平局要么继续

//判断平局:没有人赢 棋盘满了
if(1==IsFull(board,ROW,COL))//等待创建平局的函数
//平局//返回1表示棋盘满了 返回0表示棋盘没满
{
return 'Q';
}
return 'C';//没有人赢 棋盘没满
}

平局情况函数

game.h   函数声明

int IsFull(char board[ROW][COL],int row,int col);

game.c

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 0;
}
}
}
return 1;
}

整合函数

game.h

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
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);
char IsWin(char board[ROW][COL],int row,int col);
int IsFull(char board[ROW][COL],int row,int col);

game.c

#include"game.h"
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;
for(i=0;i<row;i++)//一行一行开始
{
//1.先打印一行的数据

int j=0;//列(数据|数据|数据,每个“数据”就是列)
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;//玩家的横坐标
int y=0;//玩家的纵坐标
printf("玩家走:\n");
while(1)
{
printf("请输入要下的坐标:");
scanf("%d%d",&x,&y);
//玩家输入的坐标可能超出了我们本身棋盘的大小 所以我们需要对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;
printf("电脑走:\n");//随即走
//生成随机rand()坐标
while(1)
{
x=rand()%ROW;//模上一个行 ;以题为例 模3就是0 1 2
y=rand()%COL;//模上一个列 ;以题为例 模3就是0 1 2
if(board[x][y]==' ')
{
board[x][y]='#';
}
break;
}
}


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][0]==board[i][2]&&board[i][0]!=' ')//避免还没开始下棋就有空格的情况 //board[i][0]!=' '只要判断的时候有一个不是空格就可以全部满足要求了
{
return board[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];
}
}

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[0][2]!=' ')//斜三
{
return board[0][2];
}

//以上三种情况有任何一个地方有返回 那么就有一方赢了
//如果没有 则说明要么平局要么继续

//判断平局:没有人赢 棋盘满了
if(1==IsFull(board,ROW,COL))
{
return 'Q';
}
return 'C';//没有人赢 棋盘没满
}

//返回1表示棋盘满了 返回0表示棋盘没满
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 0;
}
}
}
return 1;
}

去掉文字注释:

#include"game.h"
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;
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");
}

}
}
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;
printf("电脑走:\n");
while(1)
{
x=rand()%ROW;
y=rand()%COL;
if(board[x][y]==' ')
{
board[x][y]='#';
}
break;
}
}
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][0]==board[i][2]&&board[i][0]!=' ')//避免还没开始下棋就有空格的情况 //board[i][0]!=' '只要判断的时候有一个不是空格就可以全部满足要求了
{
return board[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];
}
}

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[0][2]!=' ')//斜三
{
return board[0][2];
}
if(1==IsFull(board,ROW,COL))
{
return 'Q';
}
return 'C';
}
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 0;
}
}
}
return 1;
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include "game.h"
#include<stdlib.h>
#include<time.h>
void menu()
{
printf("********************\n");
printf("***1.play 0.exit***\n");
printf("********************\n");
}

void game()
{
//数组-存放玩家和电脑走出的棋盘信息
char board[ROW][COL]={0};//不打印空格就是0
InitBoard(board,ROW,COL);//初始化棋盘
DisplayBoard(board,ROW,COL); //打印棋盘
while(1)
{
//首先实现玩家下棋PlayerMove();就是把输入的数字放到数组某个坐标上去
PlayerMove(board,ROW,COL);//玩家下棋
DisplayBoard(board,ROW,COL); //打印棋盘

ret=IsWin(board,ROW,COL);
//判断玩家是否赢IsWin();
if(ret!=='C')
{
break;//游戏继续 给电脑下棋机会
}

ComputerMove(board,ROW,COL);//电脑下棋
DisplayBoard(board,ROW,COL); //打印棋盘

ret=IsWin();
//判断电脑是否赢IsWin();
if(ret!=='C')
{
break;//游戏继续 跳出总循环 判断是否有输赢
}

}

if(ret=='*')
{
printf("玩家赢\n");
}

else if(ret=='#')
{
printf("电脑赢\n");
}

else
{
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;
}

去掉文字注释:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include "game.h"
#include<stdlib.h>
#include<time.h>
void menu()
{
printf("********************\n");
printf("***1.play 0.exit***\n");
printf("********************\n");
}

void game()
{
char board[ROW][COL]={0};
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();
if(ret!=='C')
{
break;
}
}
if(ret=='*')
{
printf("玩家赢\n");
}
else if(ret=='#')
{
printf("电脑赢\n");
}
else
{
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;
}