C语言:三子棋(N字棋)--数组

目录

三子棋

 1.整体框架test.c

​编辑

显示菜单

玩家选择

2.游戏声明game.h

3.游戏逻辑geme.c

---初始化棋盘

---显示棋盘

 ---玩家下棋

 ---电脑下棋

---判断输赢

---打印结果

---游戏逻辑实现思路

 4.思考

 5.总结


我将三子棋拆分为两个模块,并将其分别写在3个文件当中:

---game.h :使用函数的声明

---game.c  :  游戏逻辑的实现

---test.c     :整体框架的实现   

代码连接:https://gitee.com/zhy156/c-code-practice-warehouse.git

三子棋

1.整体框架

2.游戏逻辑

 1.整体框架test.c

---使用do   while循环先让程序运行

---显示菜单

---玩家选择

C语言:三子棋(N字棋)--数组_第1张图片

显示菜单

调用函数打印出菜单

//菜单
void menu()
{
	printf("***************************\n");
	printf("************1.PLAY*********\n");
	printf("************0.EXIT*********\n");
	printf("***************************\n");

}

玩家选择

---使用scanf函数,将玩家输入的数字存储

---在使用switch语句,根据玩家输入的数字,来显示不同的效果:

    玩家输入1:游戏进行

    玩家输入0:游戏退出

    玩家输入其它:提示输入错误,并配合do{} while(input);循环让他能够重新输入(玩家输入非0,循环会一直进行,直到玩家输入0,选择退出)     

scanf("%d", &input);
switch (input)
{
 case 1:
		printf("玩游戏\n");//玩游戏
		game();
		break;
 case 0:
		printf("已退出\n");
		break;
default:
		printf("选择错误,请重新选择:\n");
		break;
}

2.游戏声明game.h

C语言:三子棋(N字棋)--数组_第2张图片

 ---根据游戏逻辑需要用到的函数对其进行声明

 ---使用对象式宏规定棋盘的行与列(方便修改)

3.游戏逻辑geme.c

定义一个二维数组来存放棋盘

---初始化棋盘

---显示棋盘

---玩家下棋

---电脑下棋

---判断输赢

---打印结果

这里许需要注意,当玩家或电脑下完一步棋就要对游戏结果进行判断因此:

---玩家下棋

---判断输赢

---显示棋盘

---电脑下棋

---判断输赢

---显示棋盘

定义一个二维数组来存放棋盘

char board[row][col]={0};

---初始化棋盘

使用多重循环遍历二维数组(棋盘),让每一个元素都变为' ' 

使用函数将其模块化

void Init_board(char board[row][col])
{
    int i, j;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            board[i][j] = ' ';
        }
    }
}

---显示棋盘

使用多重循环遍历棋盘的每一个元素并打印即可

但为了使棋盘跟具有识别性,我们对代码进行以下调整:

C语言:三子棋(N字棋)--数组_第3张图片

将其拆分为两行打印:1.元素行 2.分割行

---元素行:打印(" 元素 “),然后打印|   若列为第3列则不打印|

---分割行:打印("---"),然后打印|   若行为第3行则不打印,若列为第3列则不打印|

实现:通过遍历棋盘元素打印元素行,再加上限制条件来决定是否打印|  (使用if条件语句)

           打印玩元素行后再打印分割行,原理同上

        

代码如下:

//显示棋盘
void Display_board(char board[row][col])
{
    int i, j;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)//遍历所有元素并打印
        {
            printf(" %c ", board[i][j]);
            if (j < col - 1)//到了最后一列(col)选择不打印分割符‘|’
                printf("|");
        }
        printf("\n");
        if (i < row - 1)//到了最后一行选择不打印分割行
        {
            for (j = 0; j < col; j++)//在每一个元素下面打印分割行
            {
                printf("---");
                if (j < col - 1)//到了最后一列(col)选择不打印分割符‘|’
                    printf("|");
            }
            printf("\n");
        }
    }
}

 ---玩家下棋

---设置X,Y来存放玩家输入的坐标(使用scanf函数)

---判断玩家输入坐标的合法性(限制在棋盘范围内)

---判断玩家输入的坐标是否被占用(判断是否为‘ ’)

---若坐标合法就将数组中对应元素替换:‘*’

---若坐标不合法就提醒玩家重新输入(这里写一个死循环,若数组中有元素被替换就使用break跳出循环)

注意:数组的下标是以0开始,但一般人默认以1开始,所以在替换数组元素时,将X,Y减1

实现

判断玩家输入坐标合法性:使用条件语句和操作符来实现 if((X>0&&X0&&Y

判断玩家输入坐标是否被占用:if(board[X-1][Y-1]==' ')

坐标合法数组中元素替换:board[X-1][Y-1]='*'

//玩家下棋
void Play_move(char board[row][col])
{
    int x, y;
    printf("请输入你要下的坐标:");
    while (1)
    {
        scanf("%d%d", &x, &y);
        //判断坐标的合法性
        if ((x > 0 && x < row + 1) && (y > 0 && y < col + 1))
        {
            //判断该位置是否被占用
            if (board[x - 1][y - 1] == ' ')
            {
                board[x - 1][y - 1] = '*';
                break;
            }
            else
            {
                printf("该坐标已被占用,请重新输入:");
            }
        }
        else
        {
            printf("非法输入,请重新输入:");
        }
    }

}

 ---电脑下棋

电脑下棋和玩家下棋基本差不多,补充下面两点

---电脑随机下棋:使用时间戳来生成随机数(通过srand()设置起点并调用rand()函数生成)

---将生成的随机数限制在棋盘范围内:将得到的数字放入X,Y当中并使其%row,%col即可

例如3子棋,将得到的数字%row(3),将得到0,1,2三个数字,列同理

//电脑下棋
void Computer_move(char board[row][col])
{

    while (1)
    {
        int x = rand() % 3;
        int y = rand() % 3;
        if (board[x][y] == ' ')
        {
            board[x][y] = '#';
            break;
        }
    }
}

---判断输赢

我们先来思考:下棋在没出结果之前肯定是要一直下的,因此:

while(1)

{

        //玩家下棋

        //电脑下棋

}

那什么时候退出这个循环呢?我们设置一个Is_Wind()函数来判断,并让它返回一个结果给我们,我们先进行以下规定:

---玩家赢:*

---电脑赢:#

---平局:P

---继续:C

我们设置一个字符类型的ret来存放Is_Wind()函数的返回结果,如果返回的字符不是‘C’就一直循环下去:

while(1)

{

        //玩家下棋

        //ret = 判断输赢(我们设置char函数,使其判断后返回一个字符给我们并放在ret中)

        if(ret !='C')

       {

                break;//说明不再继续,跳出循环

        }  

        //显示棋盘(若未出结果,显示以下棋盘,方便继续下棋)   

        电脑下棋同理   

        

}

那么游戏判断输赢的具体规则是什么呢?

C语言:三子棋(N字棋)--数组_第4张图片

棋盘个元素的表示:board[x][y]

三子棋的话只要有3个相同的元素在一条线上就能判断输赢,并且不能是' ',我们将它拆分为3块

---判断行:board[0][0]==board[0][1]==board[0][2]&&board[0][1]!=' '

 接下来我们使用循环去判断3次即可

---判断列:board[0][0]==board[1][0]==board[2][0]&&board[1][0]!=' '

 同上,使用循环判断3次

---判断对角线

        左对角线:board[0][0]==board[1][1]==board[2][0]&&board[1][1]!=' '

        右对角线:board[0][0]==board[1][1]==board[2][2]&&board[1][1]!=' '

---上面已经判断完赢的情况,接下来判断平局与继续的情况

继续:当棋盘元素中至少有一个元素为空

平局:当棋盘所有元素不为空的时候

我们设置一个判断是否为满的函数:Is_Full,在这个函数中我们去遍历棋盘的所有元素,

如果发现有一个为' ',就返回0,ret为'C'

如果遍历完发现没有元素为‘ ’,就返回1,ret为'P'

我们在外面根据返回的结果来决定ret 为‘P’还是'C'

判断棋盘是否满了:

int Is_Full(char board[row][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;
}

Is_Wind代码如下:

char Is_win(char board[row][col])
{
    // 判断行
    int i, j;
    int num = 0;
    char ret = 'C ';
    for (i = 0; i < row; i++)
    {
        if (board[i][0]==board[i][1]==board[i][2]&&board[i][1]!=' ')
        {              
            ret = board[i][j];    
            return ret;                      
        }
    }

    // 判断列
    for (j = 0; i < col; j++)
    {
        if (board[0][j]==board[1][j]==board[2][j]&&board[1][j]!=' ')
        {              
            ret = board[i][j];    
            return ret;                      
        }
    }

    // 判断对角线
    if (board[0][0]==board[1][1]==board[2][0]&&board[1][1]!=' ')//左对角线
    {              
        ret = board[i][j];    
        return ret;                      
    }
    if (board[0][0]==board[1][1]==board[2][2]&&board[1][1]!=' ')//右对角线
    {              
        ret = board[i][j];    
        return ret;                      
    }
    
    //判断棋盘是否满了
    num = Is_Full(board);//如果棋盘满了返回1,没满返回0
    if (num == 1)
        ret = 'P';//满了返回P
    else
        ret = 'C';//没满ret为'C',继续游戏

    return ret;
}

---打印结果

此时游戏判断结果已出,跳出while循环,ret结果确定

我们定义一个函数,将ret传递过去,根据ret的结果来打印

//打印结果
void Print_ret(char ret)
{
    if (ret == '*')
        printf("玩家胜利:\n");

    else if (ret == '#')
        printf("电脑胜利\n");

    else if (ret == 'P')
        printf("平局\n");
    else
    {
        printf("游戏继续\n");
    }

}

---游戏逻辑实现思路

C语言:三子棋(N字棋)--数组_第5张图片

 4.思考

代码能否进行优化?如果我想要实现N子棋,代码那部分可以修改?

---我们最开始使用了对象式宏去定义了棋盘的行与列,只需要修改其后面的数值就能实现N子棋的一部分

---我们显示棋盘的函数是将其拆分的,元素行与分割行,row,col有多大就会打印多大的棋盘

---需要优化的为Is_wind()函数:

我们仅仅写了三子棋的判断:

1.判断行2.判断列3.判断对角线

但我们写的是相对具体的下标,若要实现N子棋像之前那样写的话会造成冗余

因此:我们来找下规律

---判断在一条线上是否有N个元素相等,我们可以两两比较

例如:判断a,b,c三个数是否相等,我们可以判断a与b是否相等,b与c是否相等若都相等说明a,b,c三个数相等

我们访问棋盘中的元素是通过下标来访问的

----因此我们可以使用多重循环遍历来改变下标从而得到:每一行上的元素,每一列上的元素,对角线上的元素

----我们引出计数器count=0;如果这相邻的两个元素相等,count就+1,直到count=N-1.

说明已经比较了N-1次,并且相邻两个元素都相等,就能说明这N个元素是相等的

对比上面:a,b,c 引出count,a和b相等,count+1,b和c相等,count+1;比较完后count=2(N-1),此时a,b,c三个数相等

注意:在Is_Wind函数中我们要进行多组判断(行,列,对角线)截至条件是count==N-1

因此在进行下一组判断时需要将count重置为0,并且保证相等的元素不为‘ ’

C语言:三子棋(N字棋)--数组_第6张图片

//判断输赢
char Is_win(char board[row][col])
{
    // 判断行
    int i, j;
    int count = 0;
    int num = 0;
    char ret = 'C ';
    for (i = 0; i < row; i++)
    {
        count = 0;  // 每次判断一行时需要将 count 重置为 0
        for (j = 0; j < col - 1; j++)
        {
            if (board[i][j] == board[i][j + 1] && board[i][j] != ' ')
            {
                count++;
                if (count == row - 1)
                {
                    ret = board[i][j];
                    return ret;
                }
            }
        }
    }

    // 判断列
    for (j = 0; j < col; j++)
    {
        count = 0;  // 每次判断一列时需要将 count 重置为 0
        for (i = 0; i < row - 1; i++)
        {
            if (board[i][j] == board[i + 1][j] && board[i][j] != ' ')
            {
                count++;
                if (count == col - 1)
                {
                    ret = board[i][j];
                    return ret;
                }
            }
        }
    }

    // 判断对角线
    count = 0;  // 检查正对角线之前需要将 count 重置为 0
    for (i = 0; i < row - 1; i++)
    {
        if (board[i][i] == board[i + 1][i + 1] && board[i][i] != ' ')
        {
            count++;
            if (count == row - 1)
            {
                ret = board[i][i];
                return ret;
            }
        }
    }

    count = 0;  // 检查反对角线之前需要将 count 重置为 0
    for (i = 0; i < row - 1; i++)
    {
        if (board[i][row - i - 1] == board[i + 1][row - i - 2] && board[i][row - i - 1] != ' ')
        {
            count++;
            if (count == row - 1)
            {
                ret = board[i][row - i - 1];
                return ret;
            }
        }
    }

    //平局
    num = Is_Full(board);//如果棋盘满了返回1,没满返回0
    if (num == 1)
        ret = 'P';//满了返回P
    else
        ret = 'C';//没满ret为'C',继续游戏

    return ret;

}

5.总结

实现三子棋所运用到的知识:

-

---数组来存放棋盘   board[row][col]

---循环来读取数组中的元素    for,while,do__while

---使用函数来实现各个功能,实现模块化

---使用#define 定义常量,规定棋盘大小

---使用条件语句与操作符的搭配来达成限制条件  if(),==,!=,&&,||

---使用循环与break的搭配,可实现重复输入,达到条件后跳出循环

####整体框架test.c

---使用do{}while();循环来确保程序至少运行一次

---使用scanf()函数来读取用户所输入的数字

---使用选择语句:switch语句来对不同的输入值进行不同操作

注:do{}while();中while判断条件运用得当可以实现良好的逻辑

三子棋的实现当中,以玩家输入的input作为判断循环的截至条件:前面打印出的菜单给玩家提供选择,1.PLAY 0.EXIT 输入0循环结束退出,输入非0,循环一直运行下去,若输入非1,配合switch语句,提醒玩家重新输入,

####游戏逻辑game.c

//初始化棋盘

---通过多重循环遍历数组中的元素并将其赋为‘ ’

//玩家下棋-

--scanf()函数来读取玩家输入的坐标

--条件语句来限制输入范围(搭配关系运算符和逻辑运算符:==,!=,&&)

--while循环与break的使用(若玩家非法输入让其重新输入,直到坐标合法棋盘中有元素被替换)

//电脑下棋

---rand()函数生成随机数

//判断输赢

--使用函数模块化,多重循环读取数组元素,条件语句与操作符,

--条件语句搭配函数所返回的值来进行相关操作。

//打印结果

//使用函数,条件语句,所传递的参数来实现不同结果

####游戏声明game.h

---对要使用的函数进行声明,增加可读性,并在game.h中定义好row与col

---引用头文件,外部文件,一些函数写好后具有外部链接属性,想要使用它们需要对其引用

你可能感兴趣的:(C语言实战,c语言,笔记,开发语言)