【C语言】实现三子棋(具体步骤解析与代码)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【C语言】实现三子棋(具体步骤解析与代码)

1 : 三子棋游戏具体分析



1: 创造多个文件,三个文件分别是 (test.c,game.c,game.h)能更好处理各个文件之间的逻辑并且便于日后代码维护,也使得代码的可读性提高

2: 创造一个3*3的棋牌,同一形状连成(三格)即获胜,横排、竖 排、对角线

3: 进入游戏后先打印菜单提示

4:如果游戏想重复玩,使用do…while循环(根据输入值来确定后续的游戏进程)


2 : 实现游戏具体步骤



2:1:主函数搭建整体框架


先把这个框架在main函数搭建起来


int main()
{
  int input = 0;
  do
  {
    menu();
    printf("请选择->");
    scanf("%d", &input);
    switch(input)
    {
    case 1:
      game();
      break;
    case 0:
      break;
    default:
      printf("选择错误,请重新选择\n");
      break;
    }
  } while (input);
  system("pause");
  return 0;
}


2.2:创建游戏初始界面 (进入\退出)


0作为exit,对应switch csae0, 来退出游戏,do…while不在循环


void menu()
{
  printf("*****   1--play *****\n");
  printf("*****   0--exit *****\n");
}


2.3:创造棋牌


创建一个名为board的二维数组,宏定义了ROW,COL,来创造棋牌大小

使用宏定义的原因:

1.代码可读性更好。

2.如果将来要修改棋盘尺寸,代码修改会很方便。


//创了数据多少行,多少列
  char board[ROW][COL]={0};


2.4:搭建棋牌


将二维数组元素全部初始化为空格


//搭建棋牌
void Initboard(char board[ROW][COL], int row, int col)
{
  //库函数memset来初始化
  //如何算数组总大小:row*col计算个数 board[0][0]计算该元素占用多少个字节
  memset(&board[0][0], ' ', row*col*sizeof(board[0][0]));
}


2.5:棋牌显示


/显示棋牌
void Display(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++)
    {
      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");
  }
}


2.6:玩家下棋


玩家输入行列坐标表示下棋,玩家下棋成功用 ‘*’ 来表示。

玩家下棋要在指定范围坐标里 玩家下坐标要从1开始,

若玩家输入的坐标非法或者被占用时,需要重新输入,则需要一个循环。


//玩家下棋
void player(char board[ROW][COL], int row, int  col)
{
  int x = 0;
  int y = 0;
  printf("玩家请走\n");
  while (1)
  {
    printf("请选择下标->");
    scanf("%d %d", &x, &y);
    //因为是玩家下,所以坐标要从1开始,1对应我们数组下标0
    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");
    }
  }
}


2.7:电脑下棋


电脑下棋,x,y是个随机数,所以要在主函数中调用srand((unsigned int)time(NULL))

若电脑生成随机值坐标非法或占用时,需要重新输入,则需要一个循环。


void Comperter(char board[ROW][COL], int row, int  col)
{
  int x = 0;
  int y = 0;
  while (1)
  {
    x = rand() % row;//模上row x范围在0-row-1之间
    y = rand() % col;//模上col x范围在0-col-1之间
    if (x >= 0 && x < row && y >= 0 && y < col)//判断坐标是否合法
      if (board[x][y] == ' ')//是否是空坐标
      {
        board[x][y] = '#';//下棋成功
        break;
      }
  }
}


2.8:判断输赢


判断输赢

玩家赢 返回*

电脑赢 返回#

平局 返回 Q

继续 返回 C


1:判定是否和棋

调用 Isdraw函数。如果数组中有元素为’ ‘,没满,返回1。如果全不为’ ',就是满了,返回0

由于Isdraw函数只是写给Iswin函数的,只需要在game.c这个文件内使用,所以加上static修饰

2:判定电脑玩家胜利

判定所有的行,列,对角线是否为相同形状,如果是返回其中一个坐标,返回 * 代表玩家赢, 返回# 电脑赢


是否平局
static int Isdraw(char board[ROW][COL], int row, int col)
{
  int i = 0;
  int j = 0;
  //返回0 继续Q
  //返回1 平局C
  for (i = 0; i < row; i++)
  {
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == ' ')//如还有空坐标就是要继续
        return 1;
    }
  }
  return 0;
}
char Iswin(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)
  {
      //竖三个
      if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
        return board[1][i];
      //行三个
      else if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
        return board[i][1];
      //对角
      else if (board[0][0] == board[1][1] && board[1][1] == board[1][2] && board[1][1] != ' ')
        return board[1][1];
      //对角
      else if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
        return board[1][1];
  }
  //判断是否平局还是继续
  if (i == row)
  {
    //返回0 继续Q
      //返回1 平局C
    int ret = Isdraw(board, row, col);
    if (ret ==1)
      return 'C';
    else
      return 'Q';
  }
}


3 : 三子棋所有代码文件



3.1 test.c


整体框架


#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
  printf("*****   1--play *****\n");
  printf("*****   0--exit *****\n");
}
void game()
{
  char ret = 0;
  //创了数据多少行,多少列
  char board[ROW][COL]={0};
  //初始化棋牌:
  Initboard(board, ROW, COL);
  //显示棋牌棋牌
  Display(board, ROW, COL);
  //下棋
  while (1)
  {
    //玩家下棋
    player(board, ROW, COL);
    Display(board, ROW, COL);
    //判断输赢
    ret = Iswin(board, ROW, COL);
    //当棋局状态不是C时说明游戏结束了,就跳出循环,接着根据不同情况打印不同的结果。
    if (ret != 'C')
    {
      break;
    }
    //电脑下棋
    Comperter(board, ROW, COL);
    Display(board, ROW, COL);
    ret = Iswin(board, ROW, COL);
    //当棋局状态不是C时说明游戏结束了,就跳出循环,接着根据不同情况打印不同的结果。
    if (ret != 'C')
    {
      break;
    }
  }
  if (ret == '*')
  {
    printf("玩家赢\n");
  }
  else if (ret == '#')
  {
    printf("电脑赢\n");
  }
  else
    printf("平局\n");
}
int main()
{
  srand((unsigned int)time(NULL));
  int input = 0;
  do
  {
    menu();
    printf("请选择->");
    scanf("%d", &input);
    switch(input)
    {
    case 1:
      game();
      break;
    case 0:
      break;
    default:
      printf("选择错误,请重新选择\n");
      break;
    }
  } while (input);
  system("pause");
  return 0;
}


3.2 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] = ' ';
  //  }
  //}
  //库函数memset来初始化
  //如何算数组总大小:row*col计算个数 board[0][0]计算该元素占用多少个字节
  memset(&board[0][0], ' ', row*col*sizeof(board[0][0]));
}
//显示棋牌
//void Display(char board[ROW][COL], int row, int  col)
//{
//  //打印固定写死了,要在优化
//  int i = 0;
//  int j = 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)
//      printf("---|---|---\n");
//    
//  }
//}
//显示棋牌
void Display(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++)
    {
      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 player(char board[ROW][COL], int row, int  col)
{
  int x = 0;
  int y = 0;
  printf("玩家请走\n");
  while (1)
  {
    printf("请选择下标->");
    scanf("%d %d", &x, &y);
    //因为是玩家下,所以坐标要从1开始,1对应我们数组下标0
    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 Comperter(char board[ROW][COL], int row, int  col)
{
  int x = 0;
  int y = 0;
  while (1)
  {
    x = rand() % row;//模上row x范围在0-row-1之间
    y = rand() % col;//模上col x范围在0-col-1之间
    if (x >= 0 && x < row && y >= 0 && y < col)//判断坐标是否合法
      if (board[x][y] == ' ')//是否是空坐标
      {
        board[x][y] = '#';//下棋成功
        break;
      }
  }
}
是否平局
static int Isdraw(char board[ROW][COL], int row, int col)
{
  int i = 0;
  int j = 0;
  //返回0 继续Q
  //返回1 平局C
  for (i = 0; i < row; i++)
  {
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == ' ')//如还有空坐标就是要继续
        return 1;
    }
  }
  return 0;
}
//判断输赢
char Iswin(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)
  {
      //竖三个
      if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
        return board[1][i];
      //行三个
      else if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
        return board[i][1];
      //对角
      else if (board[0][0] == board[1][1] && board[1][1] == board[1][2] && board[1][1] != ' ')
        return board[1][1];
      //对角
      else if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
        return board[1][1];
  }
  //判断是否平局还是继续
  //判断是否平局还是继续
  if (i == row)
  {
    //返回1 平局Q
      //返回0 继续C
    int ret = Isdraw(board, row, col);
    if (ret ==1)
      return 'Q';
  }
  return 'C';
}
}


3.3 game.h


#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define ROW 3
#define COL 3
//程序化棋牌
void Initboard(char board[ROW][COL], int row, int col);
//显示棋牌
void Display(char board[ROW][COL], int row ,int  col);
//玩家下棋
void player(char board[ROW][COL], int row, int  col);
//电脑下棋
void Comperter(char board[ROW][COL], int row, int  col);
//判断输赢
//玩家赢 返回*
//电脑赢 返回#
//平局  返回 Q
//继续  返回 C
char Iswin(char board[ROW][COL], int row, int col);
目录
相关文章
|
20天前
|
存储 C语言 C++
【c语言】运算符汇总(万字解析)
今天博主跟大家分享了c语言中各种操作符的功能、使用方法以及优先级和结合性,并且与大家深入探讨了表达式求值的两个重要规则--算数转换和整形提升。学习这些知识对我们的C语言和C++学习都有着极大的帮助。
86 2
|
6天前
|
前端开发 JavaScript
React 步骤条组件 Stepper 深入解析与常见问题
步骤条组件是构建多步骤表单或流程时的有力工具,帮助用户了解进度并导航。本文介绍了在React中实现简单步骤条的方法,包括基本结构、状态管理、样式处理及常见问题解决策略,如状态管理库的使用、自定义Hook的提取和CSS Modules的应用,以确保组件的健壮性和可维护性。
41 17
|
12天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
71 14
|
16天前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
37 8
|
16天前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
110 6
|
17天前
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
16天前
|
存储 网络协议 算法
【C语言】进制转换无难事:二进制、十进制、八进制与十六进制的全解析与实例
进制转换是计算机编程中常见的操作。在C语言中,了解如何在不同进制之间转换数据对于处理和显示数据非常重要。本文将详细介绍如何在二进制、十进制、八进制和十六进制之间进行转换。
27 5
|
16天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
24 5
|
15天前
|
PHP 开发者 容器
PHP命名空间深度解析:避免命名冲突与提升代码组织####
本文深入探讨了PHP中命名空间的概念、用途及最佳实践,揭示其在解决全局命名冲突、提高代码可维护性方面的重要性。通过生动实例和详尽分析,本文将帮助开发者有效利用命名空间来优化大型项目结构,确保代码的清晰与高效。 ####
18 1
|
16天前
|
安全 搜索推荐 Unix
【C语言】《回调函数》详细解析
回调函数是指一个通过函数指针调用的函数。它允许将一个函数作为参数传递给另一个函数,并在特定事件发生时执行。这种技术使得编程更加灵活,可以动态决定在何时调用哪个函数。
32 1

推荐镜像

更多