Mike 发表于 2021-8-10 11:39:42

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展在我们学习完数组之后,我们完全可以利用数组相关知识来写一个微小型的游戏,比如说今天所说的——三子棋。
大纲:
文件组成
实现
完整代码展示
扩展
即:


一.文件组成:
在我们学习的过程中,我们要逐渐习惯多文件的书写方式,也就是模块化书写。
在本文中,笔者分为了三个文件来写,分别是:
1.game.h——实现游戏函数的声明
2.game.c——游戏函数的实现
3.test.c —— 测试及游戏函数的调用

二.实现
0.文件的初始化
在这里我们分别在我们所创建的 test.c 和 game.c 包含我们的头文件——game.h

1.菜单的实现
在菜单中,我们设置玩家可以选择的模式,play and quit
以及,菜单怎么样多次循环选择,菜单的容错处理。这里,我们利用 do-while 来实现。
#define _CRT_SECURE_NO_WARNINGS 1//加这一句话是因为笔者采用的是 VS 编译器,为了防止一些不必要的错误出现

#include "game.h"

void menu()//列出可供玩家选择的模式
{
    printf("**************************************************************\n");
    printf("*****************         1.play            ****************\n");
    printf("*****************         0.exit            ****************\n");
    printf("**************************************************************\n");
}

void play()
{

}


int main()
{
    int input;//在这里,我们利用玩家选择的模式来控制循环的终止
    do
    {
      menu();
      printf("请输入你的选择:");
      scanf("%d", &input);
      switch (input)
      {
      case 1://play
            play();
            break;
      case 0://退出
            printf("欢迎下次再来!\n");
            break;
      default://当玩家输入了非法字符,让其重新选择
            printf("输入错误,请重新输入!\n");
      }
    } while (input);//当input为0时,停止循环
   
    return 0;
}
运行效果:

现在,我们的菜单已经做好了,接下来要做的就是来打印我们的棋盘。
2.棋盘的打印
这里我们把打印函数的声明放在 game.h 文件里,把实现放在game.c 文件中。
在写代码之前,我们先来想一想在棋盘打印中,我们能不能直接打印空格——这肯定是不能的,因为这样,我们在屏幕上什么都看不见 (≧∇≦)ノ
game.c:
#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

void InitBoard(char board, int row, int col)//棋盘初始化
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
      for (j = 0; j < col; j++)
      {
            board = ' ';
      }
    }
}

void DisplayBoard(char board, int row, int col)//棋盘打印函数
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
      for (j = 0; j < col; j++)
      {
            printf(" %c ", board);
            if (j < col - 1)
            {
                printf("|");//分割列
            }
      }
      printf("\n");

      if (i < row - 1)
      {
            for (j = 0; j < 3; j++)
            {
                printf("---");//分割行
                if (j < col - 1)
                {
                  printf("|");
                }
            }
      }   
      printf("\n");
    }
}注:
为了避免文章赘余,test.c 以及 game.h不再表示
运行结果:

但是,我们这么写,会不会有问题?

值得注意的是,在这有人会把棋盘打印写成这个样子
void DisplayBoard(char board, int row, int col)//棋盘打印函数
{
    for (int i = 0; i < 3; i++)
    {
      printf(" %c | %c | %c \n", board, board, board);
      if (i < 2)
      {
            printf("---|---|---\n");//分割行
      }   
    }
}这样,无非还是上面那个问题,代码写死,无法扩展
所以,我们在这利用宏来实现,棋盘的大小随我们的宏来改变
因此在这我们给出头文件的部分
#pragma once

#include<stdio.h>

#define ROW 3//利用宏来实现棋盘的大小
#define COL 3

void InitBoard(char board, int row, int col);//棋盘初始化

void DisplayBoard(char board,int row, int col);//棋盘打印函数3.棋盘下子
1.玩家下子
在这里我们一共要注意几点:
1.在下子之前,我们要判断玩家所要下的位置是否在棋盘内
2.检测玩家要下的位置是否已有了棋子
3.下子之后,检查棋盘的输赢状况 (这个我们后面再说)
void PlayerMove(char board, int row, int col)//玩家下棋
{
    int x, y;
    printf("玩家走:\n");
    printf("请输入你所要落子的坐标:");
    scanf("%d%d", &x, &y);
    if (board!=' ')//坐标被占用
    {
      printf("该坐标已被占用,请重新下子!\n");
    }
    else if (!((x > 0 && x <= row) && (y > 0 && y <= col)))
    {
      printf("该坐标为非法坐标,请重新输入!\n");//坐标非法
    }
    else
    {
      board = '*';//玩家落子,暂时用 * 来表示
    }
}
2.电脑下子
再这里我们暂不深究,使用随机函数来生成一个坐标来下子
void ComputerMove(char board, int row, int col)//电脑下棋
{
    int x, y;
    printf("电脑走:");
    while (1)
    {
         x = rand() % row;
         y = rand() % col;
         if (board == ' ')
         {
             board = '#';//这里我们用 # 来表示电脑下棋
             break;
         }
    }
}
4.胜负的判定
在这里,我们用一个函数的返回值来表示输赢的各个情况。
#-----电脑赢
*-----玩家赢
C-----继续下子
F-----和局
int ISFULL(char board, int row, int col)
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
      for (j = 0; j < col; j++)
      {
            if (board == ' ')
                return 0;
      }
    }
    return 1;
}

char ISWIN(char board, int row, int col)
{
    int i = 0, j = 0;
for (i = 0; i < row; i++)
    {
      for (j = 0; j < col - 2; j++)
      {
            //判断横行
            if (board == board && board == board && board != ' ')
                return board;
            //判断主对角线
            else if (board == board && board == board && board != ' ')
                return board;
            //判断副对角线
            else if (board == board && board == board && board != ' ')
                return board;
      }
    }
    for (i = 0; i < row - 2; i++)
    {
      for (j = 0; j < col; j++)
      {
            //判断竖行
            if (board == board && board == board && board != ' ')
                return board;
      }
    }//判断是否满盘---放在最后是因为最后一步的判断
if (1 == ISFULL(board, row, col))
{
return 'F';
}
return 'C';
}
到这,我们的三子棋似乎已经编完了,先看一下运行结果:
    
在这个过程中,我们会发现电脑下的特别快(当然,这跟我们的懒惰有关……)所以我们在电脑下的步骤中加一个Sleep()函数来延长电脑所用时间
已即,我们可以下一次子就清一下屏,这样看起来比较舒服
所以
最后的代码部分:

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>

#define ROW 3//利用宏来实现棋盘的大小
#define COL 3

void InitBoard(char board, int row, int col);//棋盘初始化

void DisplayBoard(char board, int row, int col);//棋盘打印函数

void PlayerMove(char board, int row, int col);//玩家下棋

void ComputerMove(char board, int row, int col);//电脑下棋

char ISWIN(char board, int row, int col);//判断输赢

int ISFULL(char board, int row, int col);//判断棋盘是否已满game.h

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

void InitBoard(char board, int row, int col)//棋盘初始化
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
      for (j = 0; j < col; j++)
      {
            board = ' ';
      }
    }
}

//void DisplayBoard(char board, int row, int col)//棋盘打印函数
//{
//    for (int i = 0; i < 3; i++)
//    {
//      printf(" %c | %c | %c \n", board, board, board);
//      if (i < 2)
//      {
//            printf("---|---|---\n");
//      }   
//    }
//}

void DisplayBoard(char board, int row, int col)//棋盘打印函数
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
      for (j = 0; j < col; j++)
      {
            printf(" %c ", board);
            if (j < col - 1)
            {
                printf("|");//分割列
            }
      }
      printf("\n");

      if (i < row - 1)
      {
            for (j = 0; j < 3; j++)
            {
                printf("---");//分割行
                if (j < col - 1)
                {
                  printf("|");
                }
            }
      }   
      printf("\n");
    }
}

void PlayerMove(char board, int row, int col)//玩家下棋
{
    int x, y;
    printf("玩家走:\n");
    while (1)
    {
      printf("请输入你所要落子的坐标:");
      scanf("%d%d", &x, &y);
      if (!((x > 0 && x <= row) && (y > 0 && y <= col)))
      {
            printf("该坐标为非法坐标,请重新输入!\n");//坐标非法
      }
      else if (board != ' ')//坐标被占用
      {
            printf("该坐标已被占用,请重新下子!\n");
      }
      else
      {
            board = '*';//玩家落子,暂时用 * 来表示
            break;
      }
    }
    system("cls");
}

void ComputerMove(char board, int row, int col)//电脑下棋
{
    int x, y;
    printf("电脑走:\n");
    Sleep(1000);
    while (1)
    {
         x = rand() % row;
         y = rand() % col;
         if (board == ' ')
         {
             board = '#';//这里我们用 # 来表示电脑下棋
             break;
         }
    }
    system("cls");
}

int ISFULL(char board, int row, int col)
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
      for (j = 0; j < col; j++)
      {
            if (board == ' ')
                return 0;
      }
    }
    return 1;
}

char ISWIN(char board, int row, int col)
{
    int i = 0, j = 0;


    for (i = 0; i < row; i++)
    {
      for (j = 0; j < col - 2; j++)
      {
            //判断横行
            if (board == board && board == board && board != ' ')
                return board;
            //判断主对角线
            else if (board == board && board == board && board != ' ')
                return board;
            //判断副对角线
            else if (board == board && board == board && board != ' ')
                return board;
      }
    }
    for (i = 0; i < row - 2; i++)
    {
      for (j = 0; j < col; j++)
      {
            //判断竖行
            if (board == board && board == board && board != ' ')
                return board;
      }
    }

    //判断是否满盘---放在最后是因为最后一步的判断
    if (1 == ISFULL(board, row, col))
    {
      return 'F';
    }
    return 'C';
}game.c

#define _CRT_SECURE_NO_WARNINGS 1//加这一句话是因为笔者采用的是 VS 编译器,为了防止一些不必要的错误出现

#include "game.h"

void menu()//列出可供玩家选择的模式
{
    printf("**************************************************************\n");
    printf("*****************         1.play            ****************\n");
    printf("*****************         0.exit            ****************\n");
    printf("**************************************************************\n");
}

void play()
{
    int ret;
    char board = { 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(board, ROW, COL);
      {
            if (ret != 'C')
                break;
      }
    }
    switch (ret)
    {
      case 'F':
            printf("和局!\n");
            system("pause");
            system("cls");
            break;
      case '#':
            printf("电脑赢!\n");
            system("pause");
            system("cls");
            break;
      case '*':
            printf("玩家赢!\n");
            system("pause");
            system("cls");
            break;
      default:
            break;
    }
}


int main()
{
    srand((unsigned)time(NULL));//随机种子的初始化
    int input;//在这里,我们利用玩家选择的模式来控制循环的终止
    do
    {
      menu();
      printf("请输入你的选择:");
      scanf("%d", &input);
      switch (input)
      {
      case 1://play
            system("cls");
            play();
            break;
      case 0://退出
            printf("欢迎下次再来!\n");
            break;
      default://当玩家输入了非法字符,让其重新选择
            system("cls");
            printf("输入错误,请重新输入!\n");
      }
    } while (input);//当input为0时,停止循环
   
    return 0;
}test.c

三.扩展
1.五子棋的实现
五子棋的实现仅仅只改变了判断规则,其它方式都没变。
判断代码:
char ISWIN(char board, int row, int col)
{
    int i = 0, j = 0;

   
    for (i = 0; i < col; i++)
    {
      for (j = 0; j < col - 4; j++)
      {
            //判断横行
            if (board == board && board == board && board == board && board == board && board != ' ')
                return board;
            //判断对角线
            else if (board == board && board == board && board == board && board == board && board != ' ')
                return board;
            else if (board == board && board == board && board == board && board == board && board != ' ')
                return board;
      }
    }
    for (i = 0; i < col - 4; i++)
    {
      for (j = 0; j < col ; j++)
      {
            //判断竖行
            if (board == board && board == board && board == board && board == board && board != ' ')
                return board;
      }
    }
    //判断是否满盘
    if (1 == ISFULL(board, row, col))
    {
      return 'f';
    }
   
    return 'c';
}
2.玩家对战玩家
只需将电脑下子的部分,替换成玩家下子即可
完整五子棋-人人对战代码:

#pragma once
#include <stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>

#define ROW 10
#define COL 10

//初始化棋盘
void InitBoard(char board, int row, int col);

//打印棋盘
void DisplayBoard(char board, int row, int col);

//玩家走
void Player1Move(char board,int row, int col);

//玩家2走
void Player2Move(char board, int row, int col);

//电脑走
void ComputerMove(char board, int row, int col);

//判断输赢
char ISWIN(char board, int row, int col);

//判断棋盘是否已满
int ISFULL(char board, int row, int col);
game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

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

void DisplayBoard(char board, int row, int col)
{
    int i = 0, j = 0;
    for (i = 0; i < col; i++)
      printf("   %d", i + 1);
    printf("\n");
    for (i = 0; i < row; i++)
    {
      printf("%2d", i + 1);
      for (j = 0; j < col; j++)
      {
            printf(" %c ",board);
            if (j < col - 1)
                printf("|");
      }
      printf("\n");

      //打印分割行
      if (i < row - 1)
      {
            printf("");
            for (j = 0; j < col; j++)
            {
                printf("---");
                if (j < col - 1)
                  printf("|");
            }
            printf("\n");
      }
    }
}

void Player1Move(char board, int row, int col)
{
    int i = 0, j = 0;
    while (1)
    {
      printf("请输入玩家1要下的棋的坐标:");
      scanf("%d%d", &i, &j);
      //检查是否越界
      if (1 <= i && i <= row && j >= 1 && j <= col)
      {
            if (board != ' ')
                printf("此坐标已被占用!\n");
            else
            {
                board = '*';
                break;
            }
      }
      else
            printf("坐标非法访问!\n");
    }
    system("cls");
}

void Player2Move(char board, int row, int col)
{
    int i = 0, j = 0;
    while (1)
    {
      printf("请输入玩家2要下的棋的坐标:");
      scanf("%d%d", &i, &j);
      //检查是否越界
      if (1 <= i && i <= row && j >= 1 && j <= col)
      {
            if (board != ' ')
                printf("此坐标已被占用!\n");
            else
            {
                board = '#';
                break;
            }
      }
      else
            printf("坐标非法访问!\n");
    }
    system("cls");
}

//void ComputerMove(char board, int row, int col)
//{
//    int i = 0, j = 0;
//    printf("电脑走:\n");
//    while (1)
//    {
//      i = rand() % row;
//      j = rand() % col;
//      if (board == ' ')
//      {
//            board = '#';
//            break;
//      }
//    }
//    Sleep(1000);
//    system("cls");
//}

int ISFULL(char board, int row, int col)
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
      for (j = 0; j < col; j++)
      {
            if (board == ' ')
                return 0;
      }
    }
    return 1;
}

char ISWIN(char board, int row, int col)
{
    int i = 0, j = 0;

   
    for (i = 0; i < col; i++)
    {
      for (j = 0; j < col - 4; j++)
      {
            //判断横行
            if (board == board && board == board && board == board && board == board && board != ' ')
                return board;
            //判断对角线
            else if (board == board && board == board && board == board && board == board && board != ' ')
                return board;
            else if (board == board && board == board && board == board && board == board && board != ' ')
                return board;
      }
    }
    for (i = 0; i < col - 4; i++)
    {
      for (j = 0; j < col ; j++)
      {
            //判断竖行
            if (board == board && board == board && board == board && board == board && board != ' ')
                return board;
      }
    }
    //判断是否满盘
    if (1 == ISFULL(board, row, col))
    {
      return 'f';
    }
   
    return 'c';
}game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void menu()
{
    printf("*********************************************\n");
    printf("*******             1.play          *********\n");
    printf("*******             0.quit          *********\n");
    printf("*********************************************\n");
}

int choice()
{
    int input = 0;
    printf("请输入你的选择:");
    scanf("%d", &input);
    return input;
}

void game()
{
    char ret;
    char board = { 0 };
    InitBoard(board, ROW, COL);
    DisplayBoard(board, ROW, COL);
    while (1)
    {
      Player1Move(board, ROW, COL);

      DisplayBoard(board, ROW, COL);

      ret = ISWIN(board, ROW, COL);
      {
            if (ret != 'c')
                break;
      }

      Player2Move(board, ROW, COL);

      DisplayBoard(board, ROW, COL);

      ret = ISWIN(board, ROW, COL);
      {
            if (ret != 'c')
                break;
      }
    }
    switch (ret)
    {
    case 'f':
      printf("和局!\n");
      break;
    case '#':
      //printf("电脑赢!\n");
      printf("玩家2赢!\n");
      break;
    case '*':
      printf("玩家1赢!\n");
      break;
    default:
      break;
    }
}

void play()
{
    int input;
    do
    {
      menu();
      input = choice();
      system("cls");
      switch (input)
      {
      case 1:
            game();
            break;
      case 0:
            printf("谢谢使用,欢迎下次再来!\n");
            break;
      default:
            system("cls");
            printf("输入错误,请重新输入!\n");
            break;
      }
    } while (input);
}

int main()
{
    play();
    return 0;
}test.c
3.电脑下棋的探究
对此我们的算法肯定是需要极大的改进的
建议:
1.不在用随机函数代替机器大脑
2.电脑根据情况堵截(但是这样的话,最终的情况多是平局)
所以:
在这,我们还仍需更多的努力!
4.将game.c转为lib文件

然后出现:

此时我们会看到在我们的项目文件夹下的Debug文件夹下产生了:

接着,我们就可删除我们项目中的game.c了
然后再test.c中加入这样的一句话:

此时我们再点击运行,发现与之前并无不同!
5.打包为可安装文件
因为在VS2019中,微软似乎删除了这个组件,所以我们先需要去官网下载安装一下 : 点这
直接安装就行,尽管它是英文版,但是我们任可使用(学好英语的重要性!!!)
安装好之后,
下面是具体步骤:










之后我们就可以把我们所编写的游戏分享给我们的小伙伴,让其安装畅玩
关于三子棋的讲解便到此为止。
因笔者水平有限,若有错误之处,还望多多指正。

转载声明:
作者:HighLight_FanYa
由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。
本文版权归作者和博客共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。



文档来源:51CTO技术博客https://blog.51cto.com/guguguhuha/3335733
页: [1]
查看完整版本: C语言之三字棋的实现及扩展