【️C语言-游戏设置】---三子棋(N满)

简介: 【️C语言-游戏设置】---三子棋(N满)

📘游戏基本介绍:

48bd7cd7dcef5cfe93c4fa559402f6cc_4be14780d54040dab67baec81bf2dc28.png


嘻嘻,想必大家都玩过吧,有时上课玩,下课玩儿,玩的不亦乐乎。哈哈!


规则:双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者。


让我们重温经典,从新拾起我们儿时的回忆。把它带入C世界。


📙基本功能分配:

📕A.游戏相关定义–game.h

简单介绍:

  • game.h这个头文件就像海上巨轮的总舵,总舵发出一个个的指令 ,指挥着船员合理进行工作。game.h也是如此“指挥”着game.c从而实现游戏的运行。

这里包含几个关键的点,让我们一起来看看吧😁


- 头文件集合地:

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

这里(游戏)包含了我们会使用的几个头文件,我们可以把这些头文件放在game.h里,这样我们在其他板块引用game.h即可使用其他文件。


- 引用方式:

#include"game.h"
//这里用的是" "而不是<>哦!
  • define的整数定义:
#define ROW 3
#define COL 3
#define WZQ 3

这也是三子棋盘可以变化为N子棋盘的原因,所以这个相当于一个很不错的优化。


- 关键函数的定义:

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

//打印棋盘
void print_board(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 who_is_win(char board[ROW][COL], int row, int col);


在这里定义关键函数,在我看来其实是梳理程序的一部,建立游戏实现框架,我尝试把这个部分屏蔽,并不影响游戏的运行。


好吧,总部大佬我们已经知道了,接下来我们来看看游戏躯干吧!


game.h


#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
#define WZQ 3


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

//打印棋盘
void print_board(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 who_is_win(char board[ROW][COL], int row, int col);


📕B. 测试的逻辑–test.c

  • test.c我认为它是游戏的躯干,主题,毕竟主函数在这不是嘛(虽然很细)。好吧,我们来慢慢认识它吧。
int main()
{
  //游戏测试
  test();

  return 0;

}

a.游戏菜单–menu函数

  • 这个菜单与游戏设计的menu一样,直接上链接
void menu()
{
  printf("******************************\n");
  printf("********  1、play   **********\n");
  printf("********  0、eixt   **********\n");
  printf("******************************\n");
} 

b.游戏总框架–test函数:

1.游戏可持续性的实现

  • 这里我们为了是程序实现可持续性,用到了do-while循环来实现。在通过选项判断是否循环,或者进入下个部分。
  • 通过上面菜单,我们有三种选择:
  • 1.进行游戏,进入game()函数。但未脱离循环,函数执行完,仍在循环内。
  • 2.退出游戏,脱离循环,结束程序。
  • 3.非法输入,强行输入其他字符,无法识别,重新输入,继续循环。

- 综上,我们会选择采取Switch语句并且我们可以确定while的判断条件为input输入是否为0。

—————————————————————————代码如下——————————————————————

void test()
{
  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);
}
  • 这里也是 游戏实现框架–game函数与我们简要讲的game.c有着逻辑关系

2.game函数框架:

1.game()函数来理清框架,把游戏逻辑全部理清,是游戏实现的索引。我们用具代码来看看吧。

2.在我看来其实和test.c很相像。

3.具体内容我们就不在这里赘述,我们在game.c中来看吧!

void game()
{

  char board[ROW][COL];
  //初始化棋盘
  init_board(board,ROW,COL);
  //打印棋盘
  print_board(board,ROW,COL);
  //玩家下棋
  char ret = 0;
  while (1)
  {
    //玩家下棋
    player_move(board, ROW, COL);
    print_board(board, ROW, COL);
    //判断输赢
    ret = who_is_win(board, ROW, COL);
    if (ret != 'C')
    {
      break;
    }
    //电脑下棋
    computer_move(board, ROW, COL);
    print_board(board, ROW, COL);
    //判断输赢
    ret = who_is_win(board, ROW, COL);
    if (ret != 'C')
    {
      break;
    }
  }
  switch (ret)
  {
  case '#':
    printf("电脑胜利\n");
    break;
  case '*':
    printf("玩家胜利\n");
    break;
  default:
    printf("平局\n");
    break;
  }
  //游戏继续:'C'
  //电脑胜利:'#'
  //玩家胜利:'*'
  //平局:'P'

}

test.c

#include"game.h"

void menu()
{
  printf("******************************\n");
  printf("********  1、play   **********\n");
  printf("********  0、eixt   **********\n");
  printf("******************************\n");
} 
void game()
{

  char board[ROW][COL];
  //初始化棋盘
  init_board(board,ROW,COL);
  //打印棋盘
  print_board(board,ROW,COL);
  //玩家下棋
  char ret = 0;
  while (1)
  {
    //玩家下棋
    player_move(board, ROW, COL);
    print_board(board, ROW, COL);
    //判断输赢
    ret = who_is_win(board, ROW, COL);
    if (ret != 'C')
    {
      break;
    }
    //电脑下棋
    computer_move(board, ROW, COL);
    print_board(board, ROW, COL);
    //判断输赢
    ret = who_is_win(board, ROW, COL);
    if (ret != 'C')
    {
      break;
    }
  }
  switch (ret)
  {
  case '#':
    printf("电脑胜利\n");
    break;
  case '*':
    printf("玩家胜利\n");
    break;
  default:
    printf("平局\n");
    break;
  }
  //游戏继续:'C'
  //电脑胜利:'#'
  //玩家胜利:'*'
  //平局:'P'

}

void test()
{
  //随机数生成器
  srand((unsigned int) time(NULL));
  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);
}

int main()
{
  //游戏测试
  test();

  return 0;

}

📕C.游戏实现–game.c

  • game.c这里就是游戏核心的运行,也是游戏实现运行的关键。来吧,来吧,看看真正的实体吧😁

📗a.棋盘的建立:

这里棋盘建立主要是二维数组,实现的细节很多,好好听哦。


1.棋盘初始化–init函数

这里我们的是思路是建立所需棋盘大小并且还可以更改,这里大家联想到了什么。

对咯,就是game.h里的#define定义整数喽,棋盘需要变化只需要改变定义整数大小即可。


创建一个二维数组:


输入‘空格’完成初始化。

void init_board(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.棋盘打印–print函数

棋盘打印有很多细节,让我给大家慢慢道来!


首先我们要知道棋盘如何构成:

3e6898111c5b5d160fdfd7826d0a0951_d2650686bd2f4e4aa448d6f5cca23635.png


我们可以把棋盘拆分成两种类型来解释:

1.第一层是由“ | | ”构成的,我们还要拆分变为是由三个“ |”组成,但少了最后一个“|”

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

2.同样的道理,第二层我们可以细分为由三个“—|"组成,同样少了最后一个”|“这里就不插入代码筹字数咯。

3.细心的朋友一定发现了,我们最后一行也少了一排“—”。代码同理。

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

OK,棋盘已经搭建好了,那便来战胜负吧。


📗b.下棋:

1.玩家下棋–player函数

玩家下棋我们就自定义个函数:void player_move(char board[ROW][COL], int row, int col);

函数需要注意哪些问题呢,下面两点值得注意:


1)程序员与玩家区别

棋盘的自述:


大家好,我是棋盘YYDS,由我来为大家介绍下,程序员和玩家眼中的我。

玩家眼中,我是1,2,3并排走的,坐标范围都1-3.

可是程序员眼中,一眼看穿我的本质,明白我是一个二维数组,所以程序员能够找出我的真实坐标。0,1,2.

但我想让两边都认识我,都不矛盾,那就写个函数吧。


从棋盘的自述中,我们明白了两者的区别,很明显发现,两者认识只是差一,故此,我们便用函数更改

        printf("请输入下棋坐标:>");
    int x = 0;
    int y = 0;
    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;
      }

2)判断输入是否合规

好了,意见统一,但要合法吧!有两法要记住。


~判断是否在范围限制内;

~判断是否输入重复;

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.电脑下棋–computer函数

引入void computer_move(char board[ROW][COL], int row, int col);


1)生成范围内的随机数;

//随机数生成器
  srand((unsigned int) time(NULL));
//**生成器放在主函数哦**

随机数生成了,范围咋办,别急,来答案了。

//这里row==col,都等于3;
    int x = rand() % row;
    int y = rand() % col;
//取余一个数会得到比这个数小的结果,从而限定范围。

2)判断落子位置是否重复;

有空白就输入,没空白就算喽。

if (board[x][y] == ' ')
    {
      board[x][y] = '#';
      break;
    }

📗c.胜负判断–is_win函数

引入char who_is_win(char board[ROW][COL], int row, int col);


1.玩家/电脑胜利:

这里我是采用了计数的方法,大家可以借鉴借鉴哈。

并且玩家和电脑判断高度相似,我就拿玩家举例子喽。

电脑判断只需把‘*’改为‘#’。


1)行列判断


  int i = 0;
  for (i = 0; i < row; i++)
  {
    int j = 0;
    int count1 = 0, count2 = 0; 
    //玩家处判断
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == '*')
      //行判断
        count1++;
      if (board[j][i] == '*')
      //列判断
        count2++;
      
    }
    switch (count1)
    {
    case WZQ:
      return '*'; 
    }
    switch (count2)
    {
    case WZQ:
      return '*';
    }
    

2)对角线判断

    int i = 0;
  for (i = 0; i < row; i++)
  {
    int j = 0;
    int count3 = 0, count4 = 0;
    for (j = 0; j < col; j++)
    {
            if (board[j][j] == '*')
            //这里判断的是主对角线
        count3++;
      if (board[j][ROW - 1 - j] == '*')
      //判断的是副对角线
        count4++;
    }   
         switch (count3)
    {
    case WZQ:
      return '*';
    }
    switch(count4)
    {
    case WZQ:
      return '*';
    }
}   

综上,其实我们可以把两种判断放在一起,通用一个循环,并且要注意一个点,就是count的归零。尤其小心哈


2.平局:

引入static int is_full(char board[ROW][COL], int row, int col);


~full_board函数

核心: 度过上面两个判断,判断是否还有空格,若无,则平局。

int i = 0;
  int count = 0;
  for (i = 0; i < row; i++)
  {
    int j = 0;
    for (j = 0; j < col; j++)
    {
      if (board[i][j] != ' ')
      //这里是所有格子,判断是否有空格子
        count++;  
    }
  }
  if (count == ROW*COL)//这里是所有格子数量
    return 1;
  else
    return 0;

3.游戏继续:

上面所有都结束了,那只能是继续喽。


判断结束,返回字符,在宣布结果。


好了,游戏就讲完喽,三个代码分享如下:


game.c

#include"game.h"

void init_board(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 print_board(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)
{
  printf("玩家下棋:\n");
  while (1)
  {
    printf("请输入下棋坐标:>");
    int x = 0;
    int y = 0;
    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 computer_move(char board[ROW][COL], int row, int col)
{
  printf("电脑下棋:\n");
  while (1)
  { 
    int x = rand() % row;
    int y = rand() % col;
    if (board[x][y] == ' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}

static int is_full(char board[ROW][COL], int row, int col)
{
  int i = 0;
  int count = 0;
  for (i = 0; i < row; i++)
  {
    int j = 0;
    for (j = 0; j < col; j++)
    {
      if (board[i][j] != ' ')
        count++;  
    }
  }
  if (count == ROW*COL)
    return 1;
  else
    return 0;
  
}

char who_is_win(char board[ROW][COL], int row, int col)
{
  //玩家胜利或者电脑胜利:判断行n列n斜杠n
  
  int i = 0;
  for (i = 0; i < row; i++)
  {
    int j = 0;
    int count1 = 0, count2 = 0; 
    int count3 = 0, count4 = 0;
    //玩家处判断
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == '*')
        count1++;
      if (board[j][i] == '*')
        count2++;
      if (board[j][j] == '*')
        count3++;
      if (board[j][ROW - 1 - j] == '*')
        count4++;
    }
    switch (count1)
    {
    case WZQ:
      return '*'; 
    }
    switch (count2)
    {
    case WZQ:
      return '*';
    }
    switch (count3)
    {
    case WZQ:
      return '*';
    }
    switch(count4)
    {
    case WZQ:
      return '*';
    }

    //电脑处判断
    count1 = 0, count2 = 0;
    count3 = 0, count4 = 0;
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == '#')
        count1++;
      if (board[j][i] == '#')
        count2++;
      if (board[j][j] == '#')
        count3++;
      if (board[j][ROW - 1 - j] == '#')
        count4++;
    }
    switch (count1)
    {
    case WZQ:
      return '#';
    }
    switch (count2)
    {
    case WZQ:
      return '#';
    }
    switch (count3)
    { 
    case WZQ:
      return '#';
    }
    switch (count4)
    {
    case WZQ:
      return '#';
    }
  }

  if (is_full(board, ROW, COL) == 1)
      return 'P';
    
   return 'C';
}

篇幅太长咯,抱歉了哈!

最后美图奉上:

遥望彩霞,奔赴梦想,我们都在,一起加油(兄弟们)!!!

相关文章
|
8天前
|
存储 C语言 开发者
C语言实战 | Flappy Bird游戏
【7月更文挑战第4天】Flappy Bird是由越南开发者制作的简单却极具挑战性的游戏,玩家需控制小鸟穿越水管障碍。游戏涉及角色初始化、显示和更新。小鸟和水管结构体存储数据,使用变量和数组。初始化小鸟和水管,显示背景、小鸟和水管,更新小鸟位置及碰撞检测。代码示例展示了小鸟和水管的状态管理,当小鸟与管道碰撞或触地时,游戏结束。游戏的成功在于其独特的虐心体验。
20 0
C语言实战 | Flappy Bird游戏
|
2天前
|
存储 编译器 C语言
C语言实战 | “贪吃蛇”游戏
【7月更文挑战第5天】在C语言实战中,本文档介绍了如何构建一个简单的“贪吃蛇”游戏。游戏的核心是控制蛇移动并增长,当吃掉食物时,蛇的身体变长。数据结构使用固定大小的数组表示蛇的位置,变量存储食物位置和蛇的长度。初始化后,利用非阻塞式`getKey()`函数实现WASD键盘控制蛇的运动方向。虽然蛇的边界检测和吃食物后的增长尚未详细说明,但提到了这些问题作为练习留给读者解决,并预告将在后续章节讨论模块化编程以简化复杂代码。
10 0
C语言实战 | “贪吃蛇”游戏
|
10天前
|
存储 数据管理 C语言
C语言实战 | 使用链表完成“贪吃蛇”游戏
【7月更文挑战第1天】整体思维,即系统思维,强调以整体视角理解事物。在编程中,结构体体现这种思想,将相关变量打包处理。示例展示了如何用链表而非数组实现“贪吃蛇”游戏,链表提供了更灵活的动态数据管理。一系列代码图片详细描绘了链表结构体在游戏中的应用,包括节点定义、移动、碰撞检测等,凸显了使用链表的优势和代码的清晰组织。
17 0
C语言实战 | 使用链表完成“贪吃蛇”游戏
|
17天前
|
算法 编译器 C语言
猜数字游戏C语言代码实现
猜数字游戏C语言代码实现
|
14天前
|
存储 C语言
C语言实战 | “贪吃蛇”游戏重构
在程序设计中,模块化思维至关重要,尤其对于复杂项目,它帮助分解任务,便于团队协作。以“贪吃蛇”游戏为例,游戏涉及两个角色:蛇和食物。使用数组存储蛇的位置,变量存储食物位置。游戏流程分为初始化、显示和更新数据。初始化时,食物位置随机,蛇的位置根据数组设定。显示数据则根据这些信息在屏幕上呈现角色。更新数据时,处理蛇的移动和增长以及食物的生成和消失。类似地,通过模块化方法可开发“打砖块”游戏,涉及球、球拍和砖墙,每个角色都有相应数据结构和更新逻辑。通过这种方式,游戏开发就像搭建积木,遵循框架逐步实现。
21 0
C语言实战 | “贪吃蛇”游戏重构
|
16天前
|
C语言
【海贼王编程冒险 - C语言海上篇】怎样用C语言实现简单的扫雷游戏?
【海贼王编程冒险 - C语言海上篇】怎样用C语言实现简单的扫雷游戏?
8 1
|
16天前
|
C语言
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
9 1
|
17天前
|
存储 安全 Serverless
扫雷游戏C语言代码实现——万字长文超详细,手把手教你实现,新手也能学会
扫雷游戏C语言代码实现——万字长文超详细,手把手教你实现,新手也能学会
|
14天前
|
C语言
C语言实现猜数字游戏:代码详解与函数解析
C语言实现猜数字游戏:代码详解与函数解析
12 0
|
14天前
|
存储 C语言
C语言实战 | “俄罗斯方块”游戏重构
摘要(Markdown格式): 在之前的游戏中,全局变量的过度使用导致存储浪费和低代码通用性。以“贪吃蛇”为例,显示功能依赖全局变量,限制了函数的复用。通过参数传递代替全局变量,如在“俄罗斯方块”等游戏中控制物体运动的函数,可提升代码重用性和模块化。重构过程中,即使小到变量命名和代码精简的改进,也能逐步带来程序质量的显著提升。
12 0