【C语言】自定义棋盘大小实现三子棋

简介: 【C语言】自定义棋盘大小实现三子棋

一、整个程序的实现思路及设计

实现思路

1.打印一个简单的菜单供玩家选择如下:

2.玩家下棋

3.电脑下棋

4.判断输赢

5.循环菜单是否要继续游戏

使用多文件的方式编写

  • test.c文件游戏的框架
  • game.c文件游戏的实现
  • game.h文件游戏函数的声明

二、test.c文件的设计

在主函数(main)中的设计

int main()
{
  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);
  return 0;
}

使用menu()函数来打印菜单;

void menu()
{
  printf("***********************************\n");
  printf("************    1.paly     ********\n");
  printf("************    0.exit     ********\n");
  printf("***********************************\n");
}

使用do…while循环打印菜单;

使用srand()函数来随机生成电脑下的棋子;

使用switch根据玩家的选择来进行不同的实现;

游戏的实现放在game函数中;

void game()
{
  //char board[3][3] = { 0 };
  初始化棋盘
  //InitBoard(board, 3, 3);
  //定义一个数组用来存放数据
  char board[ROW][COL] = { 0 };
  //初始化棋盘
  InitBoard(board, ROW, COL);
  //打印棋盘
  DispalyBoard(board, ROW, COL);
  char ret = '0';
  while (1)
  {
    //玩家下棋
    PlayerMove(board, ROW, COL);
    //打印棋盘
    DispalyBoard(board, ROW, COL);
    ret=IsWin(board, ROW, COL);
    if (ret!='c')
    {
      break;
    }
    //电脑下棋
    ComputerMove(board, ROW, COL);
    //打印棋盘
    DispalyBoard(board, ROW, COL);
    ret=IsWin(board, ROW, COL);
    if (ret != 'c')
    {
      break;
    }
  }
  if (ret=='*')
  {
    printf("玩家赢\n");
  }
  else if (ret == '#')
  {
    printf("电脑赢\n");
  }
  else
  {
    printf("平局\n");
  }
}

整个test.c文件的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
  printf("***********************************\n");
  printf("************    1.paly     ********\n");
  printf("************    0.exit     ********\n");
  printf("***********************************\n");
}
void game()
{
  //char board[3][3] = { 0 };
  初始化棋盘
  //InitBoard(board, 3, 3);
  //定义一个数组用来存放数据
  char board[ROW][COL] = { 0 };
  //初始化棋盘
  InitBoard(board, ROW, COL);
  //打印棋盘
  DispalyBoard(board, ROW, COL);
  char ret = '0';
  while (1)
  {
    //玩家下棋
    PlayerMove(board, ROW, COL);
    //打印棋盘
    DispalyBoard(board, ROW, COL);
    ret=IsWin(board, ROW, COL);
    if (ret!='c')
    {
      break;
    }
    //电脑下棋
    ComputerMove(board, ROW, COL);
    //打印棋盘
    DispalyBoard(board, ROW, COL);
    ret=IsWin(board, ROW, COL);
    if (ret != 'c')
    {
      break;
    }
  }
  if (ret=='*')
  {
    printf("玩家赢\n");
  }
  else if (ret == '#')
  {
    printf("电脑赢\n");
  }
  else
  {
    printf("平局\n");
  }
}
int main()
{
  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);
  return 0;
}

三、game.h文件的设计

此文件主要用来声明函数如下:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//版本一没有使用对象宏来定义,以后修改更加的麻烦,
//void InitBoard(char board[3][3], int row, int col);
//初始化棋盘,放置空格
void InitBoard(char board[ROW][COL], int row, int col);
//声明打印棋盘的函数
void DispalyBoard(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);

四、game.c文件的设计

1.使用InitBoard()函数初始化棋盘

void InitBoard(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++)
    {
      board[i][j] = ' ';
    }
  }
}

2.使用DispalyBoard()函数打印棋盘

版本一:

void DispalyBoard(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]);
    }
    printf("\n");
  }
}

版本二:

void DispalyBoard(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)
  {
    //打印数据
    printf(" %c | %c | %c ", board[i][0], board[i][1], board[i][2]);
    printf("\n"); 
    //打印分割行
    if (i<row-1)
    {
      printf("---|---|---");
      printf("\n");
    }
  }
}

版本三:

void DispalyBoard(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)
    {
      int j = 0;
      for ( j = 0; j < col; j++)
      {
        printf("---");
        //最后一个|不打印所以也需要判断
        if (j<col-1)
        {
          printf("|");
        }
      }
      printf("\n");
    }
  }
}

就以上三个版本来说:

我觉得最好的就是第三个版本,因为要是想改变棋盘的大小只需要在game.h中修改对象宏的大小即可,前两个版本都有一定的缺陷,我建议使用第三个版本来实现棋盘的打印,这样可以使代码的可读性和后期的修改更好。

3.使用PlayerMove()函数实现玩家下棋

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");
    }
  }
}

以上的代码进行了两次判断:

  • 第一次是判断坐标输入是否合法(也就是坐标是否在棋盘的范围)
  • 第二次是判断坐标是否被占用,要是被占用就使用循环重新输入;

4.使用ComputerMove()函数实现电脑下棋

void ComputerMove(char board[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  x = rand() % row;
  y = rand() % col;
  printf("电脑下棋\n");
  while (1)
  {
    if (board[x][y]==' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}

其中使用了rand()函数它的作用是生成随机的值

  • 使用rand()%row是来控制随机值的大小
  • 同时还要再主函数中使用srand()函数来声明
  • 再game.h头文件中还要引用两个头文件(stdlib.h和time.h)

5.使用IsWin()函数实现结果的判断

版本一:

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][1]== board[i][2]&& board[i][0]!=' ')
    {
      return board[i][0];
    }
  }
  //列
  for (i = 0; i < col; 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[1][1] != ' ')
  {
    return board[1][1];
  }
  //平局
  if (IsFull(board,row,col)==1)
  {
    return 'Q';
  }
  //继续
  return 'c';
}

版本二:

char IsWin(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++) {
      //不为空字符进入判断
      if (board[i][j] != ' ') {
        //如果列上连续的三个值相等就返回,注意j+2不能超过列
        if (j < col - 2 && board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2]) {
          return board[i][j];
        }
        //如果行上连续的三个值相等就返回,注意i+2不能超过行
        else if (i < col - 2 && board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j]) {
          return board[i][j];
        }
        //如果左斜线上连续的三个值相等就返回
        else if (i < row - 2 && j < col - 2 && board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2]) {
          return board[i][j];
        }
        //如果右斜线上连续的三个值相等就返回
        else if (i >= 2 && j <= i && board[i][j] == board[i - 1][j + 1] && board[i - 1][j + 1] == board[i - 2][j +2]) {
          return board[i][j];
        }
      }
    }
  }
  //平局
  if (IsFull(board, row, col) == 1)
  {
    return 'Q';
  }
  //继续
  return 'c';
}

以上的两个版本我更加喜欢第二个版本:

  • 当棋盘的大小改变的时候,也可以进行判断不需要再次修改代码
  • 版本一只适合3*3的棋盘

综上考虑个人觉得第二个版本更加的好

6.使用IsFull()函数判断棋盘是否下满

int IsFull(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++)
    {
      if (board[i][j]==' ')
      {
        return 0;
      }
    }
  }
  return 1;
}

下面是game.c完整的代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘
void InitBoard(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++)
    {
      board[i][j] = ' ';
    }
  }
}
//版本1
//void DispalyBoard(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]);
//    }
//    printf("\n");
//  }
//}
//版本二
//void DispalyBoard(char board[ROW][COL], int row, int col)
//{
//  int i = 0;
//  for (i = 0; i < row; i++)
//  {
//    打印数据
//    printf(" %c | %c | %c ", board[i][0], board[i][1], board[i][2]);
//    printf("\n"); 
//    打印分割行
//    if (i<row-1)
//    {
//      printf("---|---|---");
//      printf("\n");
//    }
//    
//  }
//}
//版本三
void DispalyBoard(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)
    {
      int j = 0;
      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;
  x = rand() % row;
  y = rand() % col;
  printf("电脑下棋\n");
  while (1)
  {
    if (board[x][y]==' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}
//判断棋盘是否下满
int IsFull(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++)
    {
      if (board[i][j]==' ')
      {
        return 0;
      }
    }
  }
  return 1;
}
//判断结果的第二个版本
char IsWin(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++) {
      //不为空字符进入判断
      if (board[i][j] != ' ') {
        //如果列上连续的三个值相等就返回,注意j+2不能超过列
        if (j < col - 2 && board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2]) {
          return board[i][j];
        }
        //如果行上连续的三个值相等就返回,注意i+2不能超过行
        else if (i < col - 2 && board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j]) {
          return board[i][j];
        }
        //如果左斜线上连续的三个值相等就返回
        else if (i < row - 2 && j < col - 2 && board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2]) {
          return board[i][j];
        }
        //如果右斜线上连续的三个值相等就返回
        else if (i >= 2 && j <= i && board[i][j] == board[i - 1][j + 1] && board[i - 1][j + 1] == board[i - 2][j +2]) {
          return board[i][j];
        }
      }
    }
  }
  //平局
  if (IsFull(board, row, col) == 1)
  {
    return 'Q';
  }
  //继续
  return 'c';
}


相关文章
|
存储 编译器 C语言
【C语言】自定义数据类型:枚举+共用体
【C语言】自定义数据类型:枚举+共用体
39 0
|
1月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
4月前
|
编译器 C语言
C语言枚举:深入探索下标默认值、自定义值及部分自定义情况
C语言枚举:深入探索下标默认值、自定义值及部分自定义情况
|
存储 编译器 C语言
【C语言】构造数据类型(自定义数据类型)相关知识点
【C语言】构造数据类型(自定义数据类型)相关知识点
|
C语言
【C 语言】二级指针内存模型 ( 指针数组 | 二维数组 | 自定义二级指针 | 将 一、二 模型数据拷贝到 三 模型中 并 排序 )
【C 语言】二级指针内存模型 ( 指针数组 | 二维数组 | 自定义二级指针 | 将 一、二 模型数据拷贝到 三 模型中 并 排序 )
218 0
【C 语言】二级指针内存模型 ( 指针数组 | 二维数组 | 自定义二级指针 | 将 一、二 模型数据拷贝到 三 模型中 并 排序 )
|
存储 C语言
【C 语言】二级指针案例 ( 字符串切割 | 返回 自定义二级指针 作为结果 | 每个 一级指针 指向不同大小内存 | 精准分配每个 一级指针 指向的内存大小 )
【C 语言】二级指针案例 ( 字符串切割 | 返回 自定义二级指针 作为结果 | 每个 一级指针 指向不同大小内存 | 精准分配每个 一级指针 指向的内存大小 )
110 0
【C 语言】二级指针案例 ( 字符串切割 | 返回 自定义二级指针 作为结果 | 每个 一级指针 指向不同大小内存 | 精准分配每个 一级指针 指向的内存大小 )
|
C语言
【C 语言】二级指针案例 ( 字符串切割 | 返回 自定义二级指针 作为结果 )
【C 语言】二级指针案例 ( 字符串切割 | 返回 自定义二级指针 作为结果 )
115 0
【C 语言】二级指针案例 ( 字符串切割 | 返回 自定义二级指针 作为结果 )
|
C语言
【C 语言】二级指针 内存模型图 ( 指针数组 | 二维数组 | 自定义二级指针内存 )
【C 语言】二级指针 内存模型图 ( 指针数组 | 二维数组 | 自定义二级指针内存 )
184 0
【C 语言】二级指针 内存模型图 ( 指针数组 | 二维数组 | 自定义二级指针内存 )
|
存储 C语言
【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 抽象业务逻辑函数 )
【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 抽象业务逻辑函数 )
119 0
【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 抽象业务逻辑函数 )
|
存储 C语言
【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 通过 交换指针指向的内存数据 方式进行排序 )
【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 通过 交换指针指向的内存数据 方式进行排序 )
117 0
【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 通过 交换指针指向的内存数据 方式进行排序 )