玩转C语言---手把手教你写“三子棋”游戏项目(内含源码)超详细!!!

        在前面我们学习了C语言的一些基础知识,包括:选择、循环、函数体以及数组,在这些知识得到掌握后。我们要不断地练习与应用,今天我们结合之前所学内容,给大家写一个C语言的基础游戏项目,用来练习之前所学的知识;主要练习的内容包括:If选择语句、switch选择语句、for循环、while循环、do···while循环、数组、函数体的相关知识。这节我们会完整的带大家写这个代码,并且对每一步进行逻辑上的分析。

目录

一、游戏说明与分析

二、项目源码

三、运行结果

​四、分块详细说明


一、游戏说明与分析

游戏说明:

        需要构造一个3×3(或者更大的 )棋子,开始键盘为空,玩家和电脑依次往棋盘中下入棋子,当下入后需要判断是否获胜,获胜的条件是:一方的棋子连城横线或者竖线或者斜线,让棋盘填满还没构成获胜条件时,则判定为平局。

 

         在明白了游戏的规则后,我们需要对游戏的整个思路做出分析,这里绘制了一个游戏的流程图,大家可以查看。

二、项目源码

         首先,我们分析这段代码会很长,所以我们不能直接将所有的代码全部写在一个文件中,因为代码的功能很多,所以我们分别用不同的文件来存放里面的内容,在此,我们分三个文件来:

        1、main.c主要存放主函数,其他函数的调用以及整个逻辑框架;

        2、main.h主要存放函数的声明;

        3、game.c主要存放不同功能函数的定义;

接着,我们将源码供上:

1、main.c文件

#include "main.h"//包含函数声明的文件

int main()//定义主函数
    {
        Is_play();//调用判断是否进入游戏
         return 0;
    }

void game ()//进入游戏
    {

        char b;
    char board[ROW][COL]={0};//定义数组
    Initialize_board(board,ROW,COL);//1、初始化棋盘;使棋盘内容为空;
    Display_board(board,ROW,COL); //2、打印棋盘;
    while(1)
  
      {
    
        Player_chess(board,ROW,COL);//3、玩家下棋;
        Display_board(board,ROW,COL);//4、打印棋盘
        b = Is_win(board,ROW,COL);//5、判断是否继续
        if(b!='c')//判断是否赢了,或者平局的可能;
        break;//有赢了,或者平局的可能,跳出循环;
        Computer_chess(board,ROW,COL);//6、没有可能,计算机接着下棋
        Display_board(board,ROW,COL);//7、打印棋盘
        b = Is_win(board,ROW,COL); //8、判断输赢
        if(b!='c')//判断是否赢了,或者平局的可能;
        break;//有赢了,或者平局的可能,跳出循环;
     }
    
         if(b =='*')//根据判断输赢的条件得到的返回值,判断是谁赢;
          {
              printf("玩家赢!\n");//顽疾赢;
          }
          else if(b =='#')
          {
             printf("电脑赢!\n");//电脑赢;
          }
          else
          {
              printf("平局!\n");//平局;
          }

2、main.h文件

#include <stdio.h>//包含头文件
#define ROW 3//定义常量,棋盘的行数
#define COL 3//定义常量,棋盘的列数


void User_interface();//声明主界面函数
void Is_play();//声明判断是否玩游戏函数
void game();//声明进入游戏函数
void Initialize_board(char board[ROW][COL],int x,int y);//声明棋盘初始化函数
void Display_board(char board[ROW][COL],int x,int y);//声明打印棋盘函数
void Player_chess(char board[ROW][COL],int x,int y);//声明玩家下棋函数
void Computer_chess(char board[ROW][COL],int x,int y);//声明电脑下棋函数
char Is_win(char board[ROW][COL],int x,int y);//声明判断是否取胜函数
int is_full(char board[ROW][COL],int x,int y);//声明是否棋盘下满函数

3、game.c文件

#include "main.h"//包含声明函数


//函数1、定义打印主界面函数
void User_interface()
    {
        printf("***************************\n");
        printf("***** 1.play  0.exit*******\n");
        printf("***************************\n");
    }

//函数2、定义选择界面函数
void Is_play()
    {
       int a;
       do
       {
            User_interface();//引用打印主界面函数
            printf("请选择:");
            scanf("%d",&a);//输入数字
            switch(a)//根据输入的数字判断执行什么操作
           {
            case 1://第1种:进入游戏
                {
                    game();
                    break;
                }
            case 0://第二种:退出游戏
                {
                    printf("退出游戏\n");
                    break;
                }
            default://第三种输入错误
                printf("输入错误,请重新输入\n");
                break;
            }
       }
       while (a);//一直循环
    }
//函数3、定义棋盘初始化函数
void Initialize_board(char board[ROW][COL],int x,int y)//棋盘初始化是一个二维数组
    {
        int i ,j;
        for(i=0;i<x;i++)
          {
            for(j=0;j<y;j++)
            {
                board[i][j]=' ';//让棋盘中的元素都为空字符
            }
           }
    }

//函数4、定义打印棋盘函数
void  Display_board(char board[ROW][COL],int x,int y)//打印棋盘函数是将棋盘的初始状态打印出来
    {
        int i,j;
        for(i=0;i<x;i++)
           {
               for(j=0;j<y;j++)
                {
                    printf(" %c ",board[i][j]);
                    if(j<y-1)
                    printf("|");
                }
                printf("\n");

                if(i<x-1)
                    {
                     for(j=0;j<y;j++)
                        {
                        printf("---");
                         if(j<y-1)
                         printf("|");
                        }
                     printf("\n");
                    }
            }
      }

//函数5、定义玩家输入函数
void Player_chess(char board[ROW][COL],int x,int y)
{
    int i ,j;
    printf("玩家下棋>\n");
       while(1)
        {
             printf("请输入坐标>\n");//玩家输入坐标
             scanf("%d %d",&i,&j);
        if(i>0&&i<=x&&j>0&&j<=y)//判断输入的坐标知否可行
            {
                if(board[i-1][j-1]==' ')
                {
                  board[i-1][j-1]='*';
                  break;
                }
                printf("坐标已输入,请重新输入>\n");
            }
        else
            printf("坐标非法,请重新输入>\n");
       }
}
//函数6、定义计算机输入函数
void Computer_chess(char board[ROW][COL],int x,int y)
{
     int i,j;
     printf("电脑下棋>\n");
     while(1)
      {
          i=rand()%x;//产生随机坐标的行数
          j=rand()%y;//产生随机坐标的列数
       
         if(board[i][j]==' ')//判断产生的坐标是否满足要求
            {
              board[i][j]='#';//满足下入
              break;
            }//不满足重新产生
      }
}
//函数7、判断棋盘是否下满函数
int isfull(char board[ROW][COL],int x,int y)
{
    int i=0,j=0;
    for(i=0;i<x;i++)
    {
        for(j=0;j<y;j++)
        {
            if(board[i][j]==' ')
            {
                return 0;//棋盘没满,返回函数值0
            }
        }

    }
     return 1;//棋盘下满,返回函数值1
}

//函数8、定义判断是否取胜函数
 char Is_win(char board[ROW][COL],int x,int y)
 {
  int i,j,a; 
//8.1、判断取胜条件1:是否行相等; 
     for(i=0;i<x;i++)
        {
          for(j=0;j<y-1;j++)
             {
              if(board[i][j]==board[i][j+1]&&board[i][j]!=' ')
                 a=1;
              else
                {
                  a=0;
                  break;
                }
             }
            if(a==1)
            return  board[i][j];
         }

//8.2、判断取胜条件2:是否列相等;   
  for(j=0;j<y;j++)
  {
    for(i=0;i<x-1;i++)
       {
          if(board[i][j]==board[i+1][j]&&board[i][j]!=' ')
                 a=1;
          else
                 {
                    a=0;
                    break;
                 }
        }
      if(a==1)
      return  board[i][j];
   }
    
//8.3、判断取胜条件3:是否左斜线相等;
for(i=0;i<x-1;i++)
{
    if(board[i][i]==board[i+1][i+1]&&board[i][i]!=' ')
           a=1;
    else
        {
           a=0;
           break;
         }
}
     if(a==1)
     return  board[i][i];
//8.4、判断取胜条件4:是否右斜线相等;
for(i=0;i<x-1;i++)
{
    if(board[i][x-1-i]==board[i+1][x-2-i]&&board[i][x-1-i]!=' ')
      a=1;
    else
      {
          a=0;
          break;
       }
}
      if(a==1)
      return  board[i][x-1-i];
 //8.5没有满足获胜条件,判断是否棋盘已满
        if(isfull(board,x,y))//调用函数7、判断棋盘是否下满函数
        {
         return 'q' ;//已满,平局条件
        }
       //
       return 'c';//不满,游戏继续
  }

         以上是实现游戏的代码,大家使用时,创建一个项目,在工程底下创建三个文件,分别为main.c、main.h、game.c,将上述代码依次复制进去即可运行。

三、运行结果

下面截图是匀性的结果:

四、分块详细说明

 1、进入主界面并选择

      在进入主界面后,我们有三种选项,分别是进入游戏,退出游戏以及输入输入错误选项;输入1进入游戏函数,输入0是退出游戏,输入其他是再次进入主界面重新选择,因此我们需要一个do··while循环,其作用就是,先打印依次主界面,然后,根据选择判断不同的情况,循环条件为死循环,只有在进入游戏或者退出游戏时,采用break跳出循环。

        其中调用的打印主界面函数,也是比较简单,只需要打印三行代码,没有返回值,所以采用void空函数定义:

代码:根据选择判断

void Is_play()
    {
       int a;
       do
       {
            User_interface();//引用打印主界面函数
            printf("请选择:");
            scanf("%d",&a);//输入数字
            switch(a)//根据输入的数字判断执行什么操作
           {
            case 1://第1种:进入游戏
                {
                    game();
                    break;
                }
            case 0://第二种:退出游戏
                {
                    printf("退出游戏\n");
                    break;
                }
            default://第三种输入错误
                printf("输入错误,请重新输入\n");
                break;
            }
       }
       while (a);//一直循环

 代码:主界面

void User_interface()
    {
        printf("***************************\n");
        printf("***** 1.play  0.exit*******\n");
        printf("***************************\n");
    }

2、初始化棋盘

        在进入游戏后,主函数进入游戏函数模块game(),首先要进行数组初始化,这里的数组是一个ROW,COL的二位数字,ROW,COL是我们在main.h中定义棋盘行数与列数,因为我们要将下的棋子放在这个数组中去,所以我们首先要对这个二维数组的每个元素初始化,即让他们每一个元素为空格字符;

        这个函数参数分别是主函数定义的二维数组的数组名,以及数组的行数与列数,利用两层for循环,完成对所有元素的初始化;

void Initialize_board(char board[ROW][COL],int x,int y)//棋盘初始化是一个二维数组
    {
        int i ,j;
        for(i=0;i<x;i++)
          {
            for(j=0;j<y;j++)
            {
                board[i][j]=' ';//让棋盘中的元素都为空字符
            }
           }
    }

3、打印棋盘

        在初始化完成后,我们在下棋之前要先有一个棋盘的样子,如下图所示:

        此处的难点在与如果构建这些下划线以及竖线。我们分析每一个元素底下都有一行下划线,除过最后一行元素,每一列的元素右边也都有一个下划线,除过最后一列元素,因此我们可以借助之前的二维数组,对棋盘进行打印。在上图中,二位数组只存在于其数行中间空格中。

void  Display_board(char board[ROW][COL],int x,int y)//打印棋盘函数是将棋盘的初始状态打印出来
    {
        int i,j;
        for(i=0;i<x;i++)
           {
               for(j=0;j<y;j++)
                {
                    printf(" %c ",board[i][j]);//打印棋子行,注意二维数组元素前后各有一个空格。
                    if(j<y-1)
                    printf("|");//打印棋子右边的竖线
                }
                printf("\n");

                if(i<x-1)
                    {
                     for(j=0;j<y;j++)
                        {
                        printf("---");//打印棋子下行的下划线
                         if(j<y-1)
                         printf("|");
                        }
                     printf("\n");
                    }
            }
      }

4、玩家下棋

        在进行完棋盘初始化后,首先由玩家下棋,玩家只需要输入坐标即可,需要注意的是,坐标的行与列是要比数组下标的行与列大1,因为数组的下标是从0开始,因此,与要注意输入的坐标与数组的下标之间的差距,我们定义玩家下入棋子的符号为“*”。在玩家下入棋子后,我们需要做出以下判断,输入的坐标范围是否在棋盘的坐标范围内,如果不在,就需要重新进行输入,如果在范围内,还需要判断所输入的坐标内是否有棋子,如果没有棋子,为空格才能下入,如果有棋子还是需要重新输入坐标。

void Player_chess(char board[ROW][COL],int x,int y)
{
    int i ,j;
    printf("玩家下棋>\n");
       while(1)
        {
             printf("请输入坐标>\n");//玩家输入坐标
             scanf("%d %d",&i,&j);
        if(i>0&&i<=x&&j>0&&j<=y)//判断输入的坐标知否可行
            {
                if(board[i-1][j-1]==' ')
                {
                  board[i-1][j-1]='*';
                  break;
                }
                printf("坐标已输入,请重新输入>\n");
            }
        else
            printf("坐标非法,请重新输入>\n");
       }
}

        在玩家下完棋后,对棋盘进行更新打印,需要进行判断,此时判断的是  ,玩家是否赢了,如果没赢,也没平局就继续由电脑下棋,反之,需要跳出循环,判断是赢了还是平局。

    while(1)
  
      {
    
        Player_chess(board,ROW,COL);//3、玩家下棋;
        Display_board(board,ROW,COL);//4、打印棋盘
        b = Is_win(board,ROW,COL);//5、判断是否继续
        if(b!='c')//判断是否赢了,或者平局的可能;
        break;//有赢了,或者平局的可能,跳出循环;
        Computer_chess(board,ROW,COL);//6、没有可能,计算机接着下棋
        Display_board(board,ROW,COL);//7、打印棋盘
        b = Is_win(board,ROW,COL); //8、判断输赢
        if(b!='c')//判断是否赢了,或者平局的可能;
        break;//有赢了,或者平局的可能,跳出循环;
     }
    

5、电脑下棋

        玩家下完棋后,并且判断没有平局和玩家赢后,就需要由电脑下棋,电脑下棋的方式与玩家基本一致,就是往空格数组中填入元素#,然而我们需要一个产生随机数的函数rand,让其%行数与列数,作为新的坐标填入数组中,除余后的数子肯定在棋盘的坐标范围内,我们只需要再次判断所输入的坐标内是否有棋子,如果没有棋子,为空格才能下入,如果有棋子还是需要重新产生随机数,随意此处也需要用一个循环。

void Computer_chess(char board[ROW][COL],int x,int y)
{
     int i,j;
     printf("电脑下棋>\n");
     while(1)
      {
          i=rand()%x;//产生随机坐标的行数
          j=rand()%y;//产生随机坐标的列数
       
         if(board[i][j]==' ')//判断产生的坐标是否满足要求
            {
              board[i][j]='#';//满足下入
              break;
            }//不满足重新产生
      }
}

6、判断是否获胜

        在进行棋子填充后,我们需要对棋盘中的棋子进行判断,判断其是否满足获胜要求,根据前面所说,获胜的情况有1、有一行相同;2、有一列相同;3、左斜线相同;4、右斜线相同。所以在定义判断是否取胜函数,我们需要判断以下几种可能

6.1行相同

        依次判断每一行,在每行中,每个元素是否相同并且不能等于空格,我们采用两两对比,如果有一个不相同,就跳出验证下一行,如果有一行相等,就给函数返回这一行的元素,后面根据返回的字符判断是谁赢了。

for(i=0;i<x;i++)
        {
          for(j=0;j<y-1;j++)
             {
              if(board[i][j]==board[i][j+1]&&board[i][j]!=' ')//依次判断两个元素是否相同
                 a=1;//如果相同a=1
              else
                {
                  a=0;//如果不相同a=1并跳出这一行
                  break;
                }
             }
            if(a==1)//如果一行判断完,a始终为1,就返回其中相同的字符给函数
            return  board[i][j];
         }

6.2行相同

          依次判断每一列,在每列中,每个元素是否相同并且不能等于空格,我们采用两两对比,如果有一个不相同,就跳出验证下一列,如果有一列相等,就给函数返回这一列的元素,后面根据返回的字符判断是谁赢了。

for(j=0;j<y;j++)
  {
    for(i=0;i<x-1;i++)
       {
          if(board[i][j]==board[i+1][j]&&board[i][j]!=' ')//依次判断某列两个元素是否相同
                 a=1;//如果相同就令a=1
          else
                 {
                    a=0;//不同就令a=0并且跳出这列判断
                    break;
                 }
        }
      if(a==1)//如果判断完某列a始终为1,就将相同的字符返回函数
      return  board[i][j];
   }

6.3判断左斜线

        依次判断左斜线上的元素,由于棋盘为方形,所以左斜线上元素的[][]下标是相同的,所以都用[i][i]来表示,如果有一个不相同,就跳出验证,如果全部相等,就给函数返回这一列的元素,后面根据返回的字符判断是谁赢了。

for(i=0;i<x-1;i++)
{
    if(board[i][i]==board[i+1][i+1]&&board[i][i]!=' ')
           a=1;
    else
        {
           a=0;
           break;
         }
}
     if(a==1)
     return  board[i][i];

  6.4判断右斜线

        依次判断右斜线上的元素,由于棋盘为方形,所以右斜线上元素的下标是用[i][y-j]来表示,如果有一个不相同,就跳出验证,如果全部相等,就给函数返回这一列的元素,后面根据返回的字符判断是谁赢了。

for(i=0;i<x-1;i++)
{
    if(board[i][x-1-i]==board[i+1][x-2-i]&&board[i][x-1-i]!=' ')
      a=1;
    else
      {
          a=0;
          break;
       }
}
      if(a==1)
      return  board[i][x-1-i];

6.5判断是否为平局或者游戏继续

      当上述判断结束,并没有符合获胜的条件时,现在需要判断是否平局,如果不是平局,就证明游戏应当继续,而判断是否为平局,就是判断棋盘中的元素是否全部满了,如果满了就说明是平局,如果不满就说明不是平局。

依次判断棋盘中是否还有空格:

int isfull(char board[ROW][COL],int x,int y)
{
    int i=0,j=0;
    for(i=0;i<x;i++)
    {
        for(j=0;j<y;j++)
        {
            if(board[i][j]==' ')
            {
                return 0;//棋盘没满,返回函数值0
            }
        }

    }
     return 1;//棋盘下满,返回函数值1
}

        根据是否有空格判断是平局还是游戏继续,返回给函数不同的字符;

 if(isfull(board,x,y))//调用函数7、判断棋盘是否下满函数
        {
         return 'q' ;//已满,平局条件
        }
       //
       return 'c';//不满,游戏继续

7、判断是谁获

        根据第六步判断条件,我们可以得到不同的函数返回值(字符),根据不同的返回值,我们判断是谁最终获胜。

 if(b =='*')//根据判断输赢的条件得到的返回值,判断是谁赢;
          {
              printf("玩家赢!\n");//顽疾赢;
          }
          else if(b =='#')
          {
             printf("电脑赢!\n");//电脑赢;
          }
          else
          {
              printf("平局!\n");//平局;
          }

        以上便是整个游戏代码的分块详细说明,能够对于整个 代码的框架与逻辑做出很好的分析。如果想运用到4、5···等更多行,只需要在头文件的定义中,改变行数与列数的定义即可;

写在后面:

        这是一篇很长的博客,对于这个项目做出了很细致的讲解,你能够耐着性子看完这篇博客是值得点赞的,通过这篇的学习,我相信大家对于相关的知识(If选择语句、switch选择语句、for循环、while循环、do···while循环、数组、函数体等)有了不错的历练。当然,代码的写法不唯一,这种写法也可能不是最简便的,本人能力有限,还望大佬多多指正,如果有什么问题,欢迎大家一起讨论,共同进步。创作不易,还望大家多多点赞支持;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值