C语言实现三字棋~步骤详解与代码

简介: C语言实现三字棋~步骤详解与代码

三子棋是什么?


三子棋是一个很古老的民间传统游戏,玩起来也非常便捷。三子棋也叫做OOXX棋,井字棋等,用“井”字分出3×3的格子,双方轮流下棋子(可以用O或者X来区别),只要将自己的棋子连成直线就赢了。

0a2653c851af460fa595bd959398a8f1.png


三子棋实现的具体思路


1.创建一个游戏菜单让玩家选择开始or结束游戏

2.创建一个棋盘并初始化

3.将初始化后的棋盘打印

4.玩家落子


游戏实现的具体步骤


🍑菜单界面

菜单界面有两种选择:

1.玩游戏

0.退出游戏


代码实现📝


void menu()
{
  printf("------三子棋游戏-----\n");
  printf("--------------------\n");
  printf("-------1.play-------\n");
  printf("-------0.exit-------\n");
  printf("---------------------\n");
}


🍑主函数的构建


当我们游戏结束时候,仍想继续玩,即要用到循环 (●—●)


代码实现📝


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


🍑三子棋的实现


我们把代码分别放在三个文件,test.c,game.c,game.h

test.c-----测试游戏的逻辑

game.c-----游戏的实现

game.h-----函数的声明和符号的定义


🍅创建棋盘


棋盘:使用3行3列的二维数组来表示,元素类型是char。


void game()
{
  //存放下棋的数据
  char board[ROW][COL]= {0};
}


🍅初始化棋盘


这里使用了宏定义有以下的优点:

1.若要修改棋盘大小,代码的修改会很方便

2.提高的代码的可读性


在game.h头文件中声明这个初始化函数


#define ROW 3
#define COL 3
//初始化棋盘
void initBoard(char board[ROW][COL], int row, int col);


然后在game.c的函数里实现


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] = ' ';
  }
  }
}

0a2653c851af460fa595bd959398a8f1.png

ps:后面创建的函数也一样,要先在game.h中声明,再到game.c函数中实现


🍅打印棋盘


1)按照上面的代码打印,菜单界面又弹出来了,要注意的是这里棋盘并不是没有打印出来,而是我们用" "来表示空白,所以看到的才是一片黑色,接下来我们尝试美化它。变成下面图片的样子↓


0a2653c851af460fa595bd959398a8f1.png

void DisplayBoard(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)
  {
    for (j = 0; j < col; j++)
    {
    printf("---");
    if (j < col - 1)
      printf("|");
    }
    printf("\n");
  }
  }
}


🍅玩家落子


1.我们当然知道在二维数组里面,数组的下标是从0开始的,但作为一个小白玩家,他们不知道数组的下标。所以对于玩家来说,行和列都是从1开始的。

2.判断玩家输入的坐标是否合法(即是否超出了棋盘范围)

3.玩家落子前要判断该坐标是否为空坐标。


void player_move(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] = 'X';
    break;
    }
    else
    {
    printf("该坐标被占用,请重新输入\n");
    }
  }
  else
  {
    printf("输入的坐标非法,请重新输入\n");
  }
  }
}


🍅电脑落子


我们选择用时间戳来生成一个随机数,并把随机数的范围控制在0~3以内,

生成随机数我在这篇博客里详细讲过,大家可以看到这边来生成随机数 ~详细解析,看完对下面的理解更加深刻哦~


srand((unsigned)time(NULL));
1
void computer_move(char board[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  printf("电脑下棋:>\n");
  while (1)
  {
  x = rand() % row;//0~2
  y = rand() % col;//0~2
  if (board[x][y] == ' ')
  {
    board[x][y] = 'O';
    break;
  }
  }
}


🍅判断输赢


当玩家或者电脑每下一步棋的时候,我们就定义一个函数is_win,判断一下谁赢了;

·

当双方下的棋遍布棋盘的时候,且双方都没有赢,则为平局;

·

1、玩家赢 —> 返回X

.

2、电脑赢---->返回O

.

3、平局----->返回Q

.

4、继续---->返回C

那怎么样才算赢呢?接下来举个例子


当三行或者三列,以及两条对角线都落满相同的棋子时,那就就赢了;


如图这条斜线落满了相同的棋子,那我们就可以返回中间的红色框框的值


如果值为X,那么就是玩家胜利;

如果值为O,那么就是电脑胜利;


int if_full(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 is_win(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][1] != ' ')
  {
    return board[i][1];
  }
  }
  //判断列
  for (i = 0; i < col; i++)
  {
  if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
  {
    return board[1][i];
  }
  }
  //判断对角线
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
  {
  return board[1][1];
  }
  if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
  {
  return board[1][1];
  }
  //判断平局
  if (if_full(board, row, col) == 1)
  {
  return'Q';
  }
  //游戏继续
  return'C';
}


🍅主函数判断输赢


玩家和电脑下棋的思路可以这样理解

当玩家每下一步棋时,就进行输赢判断;

当电脑每下一步棋时,也进行输赢判断;

但玩家和电脑的下棋必须通过循环来实现;

当ret不等于C的时候,则跳出循环;

跳出循环后,将is_win的返回值进行比较


int ret = 0;
  //存放下棋的数据
  char board[ROW][COL]= {0};
  //初始化棋盘全为空格
  initBoard(board, ROW, COL);
  //打印棋盘
  DisplayBoard(board, ROW, COL);
  while (1)
  {
  //玩家下棋
  player_move(board, ROW, COL);
  DisplayBoard(board, ROW, COL);
  //判断输赢
  ret = is_win(board, ROW, COL);
  if (ret != 'C')
  {
    break;
  }
  //电脑下棋
  computer_move(board, ROW, COL);//随机下棋
  DisplayBoard(board, ROW, COL);
  //判断输赢
  ret = is_win(board, ROW, COL);
  if (ret != 'C')
  {
    break;
  }
  }
  if (ret == 'X')
  {
  printf("你居然赢了\n");
  }
  else if (ret == 'O')
  {
  printf("不会吧不会吧,不会有人输给电脑吧\n");
  }
  else 
  {
  printf("打成平局啦\n");
  }


🍅总代码


game.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//初始化棋盘
void initBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void player_move(char board[ROW][COL], int row, int col);
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);
//判断输赢
char is_win(char board[ROW][COL], int row, int col);


game.c

#define _CRT_SECURE_NO_WARNINGS
#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] = ' ';
  }
  }
}
void DisplayBoard(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)
  {
    for (j = 0; j < col; j++)
    {
    printf("---");
    if (j < col - 1)
      printf("|");
    }
    printf("\n");
  }
  }
}
void player_move(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] = 'X';
    break;
    }
    else
    {
    printf("该坐标被占用,请重新输入\n");
    }
  }
  else
  {
    printf("输入的坐标非法,请重新输入\n");
  }
  }
}
void computer_move(char board[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  printf("电脑下棋:>\n");
  while (1)
  {
  x = rand() % row;//0~2
  y = rand() % col;//0~2
  if (board[x][y] == ' ')
  {
    board[x][y] = 'O';
    break;
  }
  }
} 
int if_full(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 is_win(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][1] != ' ')
  {
    return board[i][1];
  }
  }
  //判断列
  for (i = 0; i < col; i++)
  {
  if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
  {
    return board[1][i];
  }
  }
  //判断对角线
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
  {
  return board[1][1];
  }
  if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
  {
  return board[1][1];
  }
  //判断平局
  if (if_full(board, row, col) == 1)
  {
  return'Q';
  }
  //游戏继续
  return'C';
}


test.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void menu()
{
  printf("------三子棋游戏-----\n");
  printf("--------------------\n");
  printf("-------1.play-------\n");
  printf("-------0.exit-------\n");
  printf("---------------------\n");
}
void game()
{
  int ret = 0;
  //存放下棋的数据
  char board[ROW][COL]= {0};
  //初始化棋盘全为空格
  initBoard(board, ROW, COL);
  //打印棋盘
  DisplayBoard(board, ROW, COL);
  while (1)
  {
  //玩家下棋
  player_move(board, ROW, COL);
  DisplayBoard(board, ROW, COL);
  //判断输赢
  ret = is_win(board, ROW, COL);
  if (ret != 'C')
  {
    break;
  }
  //电脑下棋
  computer_move(board, ROW, COL);//随机下棋
  DisplayBoard(board, ROW, COL);
  //判断输赢
  ret = is_win(board, ROW, COL);
  if (ret != 'C')
  {
    break;
  }
  }
  if (ret == 'X')
  {
  printf("玩家赢了\n");
  }
  else if (ret == 'O')
  {
  printf("电脑赢了\n");
  }
  else 
  {
  printf("平局\n");
  }
}
int main()
{
  int input = 0;
  srand((unsigned)time(NULL));
  do
  {
  menu();
  printf("请选择:>");
  scanf("%d", &input);
  switch (input)
  {
  case 1:
    game();
    break;
  case 0:
    printf("退出游戏\n");
    break;
  default:
    printf("选择错误\n");
    break;
  }
  } while (input);
  return 0;
}


🌟 总结

看一遍不一定会,但自己手打一遍肯定有用,一起打天下吧!


相关文章
|
28天前
|
存储 安全 数据管理
C语言之考勤模拟系统平台(千行代码)
C语言之考勤模拟系统平台(千行代码)
48 4
|
4月前
|
NoSQL 编译器 程序员
【C语言】揭秘GCC:从平凡到卓越的编译艺术,一场代码与效率的激情碰撞,探索那些不为人知的秘密武器,让你的程序瞬间提速百倍!
【8月更文挑战第20天】GCC,GNU Compiler Collection,是GNU项目中的开源编译器集合,支持C、C++等多种语言。作为C语言程序员的重要工具,GCC具备跨平台性、高度可配置性及丰富的优化选项等特点。通过简单示例,如编译“Hello, GCC!”程序 (`gcc -o hello hello.c`),展示了GCC的基础用法及不同优化级别(`-O0`, `-O1`, `-O3`)对性能的影响。GCC还支持生成调试信息(`-g`),便于使用GDB等工具进行调试。尽管有如Microsoft Visual C++、Clang等竞品,GCC仍因其灵活性和强大的功能被广泛采用。
145 1
|
19天前
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
25天前
|
存储 安全 物联网
C语言物联网开发之设备安全与代码可靠性隐患
物联网设备的C语言代码安全与可靠性至关重要。一是防范代码安全漏洞,包括缓冲区溢出和代码注入风险,通过使用安全函数和严格输入验证来预防。二是提高代码跨平台兼容性,利用`stdint.h`定义统一的数据类型,并通过硬件接口抽象与适配减少平台间的差异,确保程序稳定运行。
|
19天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
48 1
|
2月前
|
存储 搜索推荐 C语言
深入C语言指针,使代码更加灵活(二)
深入C语言指针,使代码更加灵活(二)
|
2月前
|
存储 程序员 编译器
深入C语言指针,使代码更加灵活(一)
深入C语言指针,使代码更加灵活(一)
|
2月前
|
C语言
深入C语言指针,使代码更加灵活(三)
深入C语言指针,使代码更加灵活(三)
深入C语言指针,使代码更加灵活(三)
|
2月前
|
IDE Unix 开发工具
1.6使用C语言的7个步骤后续
在编程过程中,首先通过不同环境下的方法运行程序,如输入可执行文件名或点击图标。接着测试程序运行情况,查找并修正可能出现的错误,即调试。创建程序后还需根据需求进行维护与修改,如修正错误或添加新功能。整个流程非线性,需反复迭代。
70 8
|
2月前
|
编译器 C语言
1.6 使用C语言的7个步骤
在开发C程序时,首先要明确程序目标,即程序需要处理的信息、计算和输出;接着设计程序实现的方法,包括用户界面、程序结构等;随后用C语言编写程序代码;最后通过编译器将源代码转换为特定机器语言的可执行文件,同时链接C库中的标准函数,形成最终可运行的程序。此过程需根据具体编程环境调整相关设置和操作。
65 7