三子棋真是太神奇啦~~~C语言三子棋小游戏详解,具体到每一步操作的解释说明,不信你学不会!

简介: 三子棋真是太神奇啦~~~C语言三子棋小游戏详解,具体到每一步操作的解释说明,不信你学不会!

前言

一、理清创建思路

创建几个文件来实现:

test.c //测试游戏逻辑

游戏的模块:

game.h //函数的声明

game.c //函数的实现

二、test.c代码块

**注意:**详细的分析具体到每一步是什么操作以及思路步骤的顺序,请打开下面的代码查看,按序号指引结合game.c和game.h代码块一步步理清操作思路哦🤗🤗

#define _CRT_SECURE_NO_WARNINGS 1
//13.将头文件移到game.h中,注释头文件
//#include<stdio.h>
//6.在代码前面包含头文件game.h,就可以用了
#include "game.h"
void menu() {
  printf("***************************************************\n");
  printf("*****************      1.play         *************\n");
  printf("*****************      0.exit         *************\n");
  printf("***************************************************\n");
}
void game() {
  //5.三子棋存放数据需要一个3*3的二维数组  char board[3][3] = { 0 };
  //如果在这里写成char board[3][3] = { 0 };的形式,以后扩大棋盘的时候再修改非常不方便
  //所以将棋盘的行和列定义变量ROW,COL;在game.h中声明,以后修改棋盘大小的时候在game.h中修改变量的大小即可
  char board[ROW][COL] = { 0 };
  //7.初始化棋盘
  //当开始的时候棋盘中放的是空格,下棋之后空格里面变成符号
  //创建一个初始化棋盘的函数,把棋盘,几行几列传进来
  InitBoard(board, ROW, COL);   //8.初始化函数的实现怎么实现?在game.h中声明,请见game.h
  //14.显示棋盘(在数字打印的基础上还要打印棋盘的框框)
  DisplayBoard(board, ROW, COL);
  char ret = 0;      //48.因为最后也要用到ret,所以最好把ret定义到外面
  while (1) {
    //20.下棋:一人下一步,每人下完打印棋盘,判断输赢
    //21.玩家下棋:实际上是要落子,落到棋盘board里面,传参的时候数组要传过去,几行几列也要给过来
    PlayerMove(board, ROW, COL);
    //25.打印棋盘
    DisplayBoard(board, ROW, COL);
    //35.判断输赢:判断数组里面是否有三行三列或者对角线上有三个相同的元素
    //还是判断数组,把数组传进去,还有几行几列
    //46.要知道返回值才知道谁赢,定义变量ret来接受返回值
    ret = IsWin(board, ROW, COL);    //如果ret返回的不是C的话,游戏就结束
    if (ret != 'C') {
      break;
    }
    //36.实际上还要判断四种结果:(1)电脑赢(2)玩家赢(3)平局(4)继续(如果不是玩家赢电脑赢平局,那么游戏继续)
    //如果电脑赢,返回'#'
    //如果玩家赢,返回'*'
    //如果平局.返回'Q'
    //如果继续,返回'C'
    //26.电脑下棋
    Computermove(board, ROW, COL);
    //打印棋盘
    DisplayBoard(board, ROW, COL);
    //47.判断输赢:同样的道理这里也接收一下判断输赢的结果
    ret = IsWin(board, ROW, COL);    //如果ret返回的不是C的话,游戏就结束
    if (ret != 'C') {
      break;
    }
 }
  //48.跳出上面的循环判断谁赢了,而不是在每次判断输赢的时候写上下面这段代码,避免了代码的冗余
  if ('*' == ret) {
    printf("玩家赢\n");
  }else if('#'==ret) {
    printf("电脑赢\n");
  }
  else if('Q'==ret){
    printf("平局\n");
  }
}
int main() {
  int input = 0;
  srand((unsigned int)time(NULL));  //30.使用rand()函数前必须调用srand();用时间戳作为随机数生成的起点,返回类型定义为unsigned int 
                                    //31.需要包含头文件,直接写到game.h中
  do                       //1.制作一个游戏,玩玩一把不够玩,再来一把,采用do while循环
  {
    menu();              //2.创建一个菜单函数,让它操控玩游戏和退出游戏的功能
    printf("请选择:>");
    scanf("%d", &input);
    switch (input) {     //3.根据input输入的值做出判断
    case 1:              //选择1进入游戏
      game();          //4.game函数来设计游戏
      break;
    case 0:              //选择0退出游戏
      printf("退出游戏\n");
      break;
    default:             //选择的既不是1又不是0弹出选择错误
      printf("选择错误,请重新选择\n");
      break;
    }
  } while (input);
  return 0;
}

三、game.h代码块

#pragma once
#define ROW 3    
#define COL 3
//9.函数的声明(在game.c中去写这个函数)
//初始化棋盘
void InitBoard(char board[ROW][COL], int row,int col);  //初始化数组里面的形参,棋盘用数组来接收,再给定行和列的形参,使用小写用于区分
//13.将头文件移到game.h中
#include<string.h>
#include<stdio.h>
//32.包含头文件
#include<stdlib.h>       //rand()头文件
#include<time.h>         //time头文件
//15.打印棋盘(棋盘传进来,几行几列)
void DisplayBoard(char board[ROW][COL], int row, int col);
//22.声明玩家下棋:(记得写void)
void PlayerMove(char board[ROW][COL], int row, int col);
//27.声明电脑下棋的函数:(记得写void)
void Computermove(char board[ROW][COL], int row, int col);
//37.声明判断输赢的函数:
char IsWin(char board[ROW][COL], int row, int col);

四、game.c代码块

#define _CRT_SECURE_NO_WARNINGS 1
//10.实现初始化函数的时候,要用到行和列即ROW,COL,所以要包含头文件
#include "game.h"
//11.(2.2)memset函数需要头文件
//#include<string.h> 
//12.通过观察test.c和game.c的头文件,我们发现其实只要把头文件全部写入game.h然后包含一下就都可以使用了,这样的写法更简单
// 接下来去把头文件全部写到game.h中,这里的话为了使读者看的更清晰,我就把test.c中的#include<stdio.h>注释了
//所以如果使用memset函数,那么将game.c中的#include<string.h>写在game.h中即可
//11.(1)写初始化函数方法一:
void InitBoard(char board[ROW][COL], int row, int col) {
  int i = 0;
  for (i = 0; i < row; i++) {   //i遍历多行
    int j = 0;
    for (j = 0; j < col; j++) {     //j遍历多列 
      board[i][j] = ' ';          //将每个格子放空格占位
    }
  }
//11.(2.1)写初始化函数方法一:
  //memset(&board[0)[0], ' ', row* col * sizeof(board[0][0]);   
  //解释:
  //memset(数组首地址,置为空格,数组的总大小(把这么多字节置为空格) );
}
 //16.写打印棋盘的函数
//即使没有下棋的时候也要把棋盘的格子打印出来
//棋盘:
//      一行数据
//      一行分割
//      一行数据
//      一行分割
//      一行数据
//      (一行分割)
//上面可以看成两个两个一组,最后一行分割不用打印,可以通过控制条件不用打印
//void DisplayBoard(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]);   //17.这里把列写死了,列写成三列了,当将game.h中的ROW,COL改为十行十列,打印出棋盘的运行结果是十行三列就是因为这里写死了
//                                       //18.优化一下,注释这一整段代码
//    if (i < row - 1)           //最后一组的时候不打印分割线
//      printf("---|---|---\n");
//
//  }
//}
//19.优化打印棋盘:
void DisplayBoard(char board[ROW][COL], int row, int col) {
  int i = 0;
  int j = 0;
  for (i = 0; i < row; i++) {
    int j = 0;
    for (j = 0; j < col; j++) {
      printf(" %c ", board[i][j]);            //数据行拆分:将" %c |"看成一组
      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");                               //分割的符号打印完换行
  }
}
//23.实现玩家下棋:
void PlayerMove(char board[ROW][COL], int row, int col) {
  int x=0;                             //行用x来控制   
  int y=0;                             //列用y来控制
  printf("玩家下棋>:\n");
  while(1){              //24.落棋的这个动作反复操作,所以用while循环,直到落棋正确break跳出
    printf("请输入要下棋的坐标:>\n");            //输入坐标要输入行和列,所以前面要定义两个变量
    scanf("%d%d", &x, &y);                //请玩家输入坐标,在玩家的认识里行和列都是从第一行第一列开始,当然也有乱输入坐标的玩家,这个时候就要对他们输入的进行判断
    if (x >= 1 && x <= row && y >= 1 && y <= col) {
      //判断玩家落棋的地方是不是空格,
      //但是
      //比如玩家输入的1 2,对应到数组里面并不是第一行第二列,数组的下标是从0开始的,所以玩家输入的行和列的值都要-1
      if (board[x - 1][y - 1] == ' ') {         //是空格的时候可以落棋
        board[x - 1][y - 1] = '*';
        break;
      }
      else {
        printf("坐标被占用,请重新输入\n");       //不是空格的时候进行提示   
      }
    }
    else {
      printf("坐标非法,请重新输入\n");
    }
  }
}
//28.实现电脑下棋的函数:
//电脑随机下棋
void Computermove(char board[ROW][COL], int row, int col) {
  int x = 0;                 //创建坐标
  int y = 0;
  printf("电脑下棋>:\n");
  while (1) {    //35.下棋的位置如果不是空格,重新生成就好了,不用提示,所以用while循环控制
    x = rand() % row;     //29.随机生成0-2的数字,%上row余数就是0-2,下面同理
    y = rand() % col;
    //33.生成的坐标所在的位置万一已经有棋就不行了,需要重新设置坐标,所以要进行判断
    if (board[x][y] == ' ') {
      board[x][y] = '#';         //34.下棋的位置如果是空格,就放置为#,break跳出,
      break;
    }
  }
}
//44.创建棋盘是否满了的函数:有空格说明没满,没空格说明满了,返回类型设置为int
int IsFull(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++) {
      if (board[i][j] == ' ') {         //如果是空格的话,返回0;不是的话返回1
        return 0;
      }
    }
  }
  return 1;
}
//38.实现判断输赢的函数:
//如果电脑赢,返回'#'
//如果玩家赢,返回'*'
//如果平局.返回'Q'
//如果继续,返回'C'
char IsWin(char board[ROW][COL], int row, int col) {
  int i = 0;
  for (i = 0; i < row; i++) {      //39.循环row次,判断三行
    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++) {      //40.循环col次,判断三列
    if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') {          //判断一列三个是否相等,但是这三个不能都是空格,所以要排除这种情况
      return board[0][i];      //如果三个一样满足赢得条件,返回这三个里面一个的符号,因为在前面已经约定好电脑赢和玩家赢的标志符号,这样做非常方便,这样的设计非常好
      }
    }
    //41.判断两条对角线
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {
      return board[0][0];
    }
    if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {
      return board[0][0];
    }
    //42.以上四种情况中的一种都是玩家赢或者是电脑赢,下面这种情况是平局情况
    //判断是否为平局:前面没人赢的情况下,棋盘满了,就是平局
    //也就是判断棋盘满了
    if (IsFull(board, row, col)) {       //43.在上面需要创建判断棋盘是否满了的函数
      return 'Q';
    }
    //45.如果前面都没有返回的话,游戏继续
    return 'C';
  }
}

五、简易版代码块

1.test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
  printf("*******************************\n");
  printf("********    1. play      ******\n");
  printf("********    0. exit      ******\n");
  printf("*******************************\n");
}
void game()
{
  //存放数据需要一个3*3的二维数组
  char board[ROW][COL] = {0};
  //初始化棋盘
  InitBoard(board, ROW, COL);
  //显示棋盘
  DisplayBoard(board, ROW, COL);
  char ret = 0;
  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;
    }
  }
  if ('*' == ret)
  {
    printf("玩家赢\n");
  }
  else if ('#' == ret)
  {
    printf("电脑赢\n");
  }
  else if ('Q' == ret)
  {
    printf("平局\n");
  }
}
int main()
{
  int input = 0;
  srand((unsigned int)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;
}

2.game.h

#pragma once
//头文件的包含
#include <string.h>
#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 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);

3.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(&board[0][0], ' ', row*col*sizeof(board[0][0]));
}
//void DisplayBoard(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]);
//    }
//    printf("\n");
//  }
//}
//void DisplayBoard(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 DisplayBoard(char board[ROW][COL], int row, int col)
{
  int i = 0;
  int j = 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 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;
  printf("电脑下棋>:\n");
  while (1)
  {
    x = rand() % row;
    y = rand() % col;
    if (board[x][y] == ' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}
int IsFull(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++)
    {
      if (board[i][j] == ' ')
      {
        return 0;
      }
    }
  }
  return 1;
}
//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局   - 'Q'
//继续   - 'C'
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[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 (IsFull(board, row, col))
  {
    return 'Q';
  }
  //游戏继续
  return 'C';
}

六、运行结果

(1)第一次测试

(2)第二次测试

总结

今天你学会制作这个小游戏了呢小伙伴们?💕

如果哪里写的有问题,欢迎大家帮我指正.

最后,如果对友友们有所帮助的话,可以留下关注点赞收藏哦~🥰💕❤️

相关文章
|
1月前
|
C语言 C++
【C语言】编写“猜数字“小游戏
【C语言】编写“猜数字“小游戏
|
2月前
|
定位技术 API C语言
C语言——实现贪吃蛇小游戏
本文介绍了一个基于Windows控制台的贪吃蛇游戏的实现方法。首先,需调整控制台界面以便更好地显示游戏。接着,文章详细描述了如何使用Win32 API函数如`COORD`、`GetStdHandle`、`GetConsoleCursorInfo`等来控制控制台的光标和窗口属性。此外,还介绍了如何利用`GetAsyncKeyState`函数实现键盘监听功能。文中还涉及了`&lt;locale.h&gt;`库的使用,以支持本地化字符显示。
57 1
C语言——实现贪吃蛇小游戏
|
2月前
|
存储 安全 算法
C 语言——实现扫雷小游戏
本文介绍了使用二维数组创建棋盘并实现扫雷游戏的方法。首先,通过初始化数组创建一个9x9的棋盘,并添加行列标识以便操作。接着,利用随机数在棋盘上布置雷。最后,通过判断玩家输入的坐标来实现扫雷功能,包括显示雷的数量和处理游戏胜利或失败的情况。文中提供了完整的代码实现。
43 1
C 语言——实现扫雷小游戏
|
1月前
|
C语言 定位技术 API
【C语言】实践:贪吃蛇小游戏(附源码)(二)
【C语言】实践:贪吃蛇小游戏(附源码)
【C语言】实践:贪吃蛇小游戏(附源码)(二)
|
1月前
|
C语言 开发者
C语言实现猜数字小游戏(详细教程)
C语言实现猜数字小游戏(详细教程)
|
5月前
|
C语言
C语言实现2048小游戏---粤嵌GE6818嵌入式系统实训
C语言实现2048小游戏---粤嵌GE6818嵌入式系统实训
|
1月前
|
C语言
【C语言】实践:贪吃蛇小游戏(附源码)(三)
【C语言】实践:贪吃蛇小游戏(附源码)
|
1月前
|
存储 API C语言
【C语言】实践:贪吃蛇小游戏(附源码)(一)
【C语言】实践:贪吃蛇小游戏(附源码)
|
1月前
|
C语言
C语言贪吃蛇小游戏来啦!
C语言贪吃蛇小游戏来啦!
28 0
|
4月前
|
存储 编译器 C语言