万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(二)

简介: 我们可以通过创建并定义符号常量NUMBER,来作为判断是否胜利的标准。如三子棋中,令NUMBER为3,则这八个方向中有任意一个方向达成3子连珠,则连珠的这个棋子所代表的玩家获胜。

万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(一)

+https://developer.aliyun.com/article/1522007?spm=a2c6h.13148508.setting.18.439a4f0evqNcHz


三、功能优化:会堵棋的电脑角色


说明见代码注释

void update_computer_move(char board[ROW][COL], int row, int col)
{
  printf("电脑下棋!\n");
  int x = y = 0;
  int i = j = 0;
  
  //检查每行,判断是否需要堵棋
  for (i = 0; i < row; i++)
  {
    int count = 0;  //用于标记棋数
    
    //数每一行玩家下了多少个棋子
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == '*')
        count++;
    }
    
    //当某一行玩家已经下了两个棋-->要堵棋
    if (count == 2)
    {
      for (j = 0; j < col; j++)
      {
        if (board[i][j] == ' ')
        {
          board[i][j] = '#';
          return;   //落子,函数结束
        }
      }
    }
  }
  
  //检查每列,判断是否需要堵棋
  //思路与上面完全相同
  for (j = 0; j < col; j++)
  {
    int count = 0;
    for (i = 0; i < row; i++)
    {
      if (board[i][j] == '*')
        count++;
    }
    
    //若一列已经有两个棋-->堵棋
    if (count == 2)
    {
      for (i = 0; i < row; i++)
      {
        if (board[i][j] == ' ')
        {
          board[i][j] = '#';
          return;   //落子,函数结束
        }
      }
    }
  }
  
  int diagonal_1 = 0;   //标记主对角线
  //检查主对角线,判断是否需要堵棋
  for (i = 0; i <row; i++)
  {
    if (board[i][i] == '*')
      diagonal_1++;
  }
  if (diagonal_1 == 2)
  {
    for (i = 0; i <row; i++)
    {
      if (board[i][i] == ' ')
      {
        board[i][i] = '#';
        return;   //落子,函数结束
      }
    }
  }
  
  int diagonal_2 = 0;   //标记副对角线
  //检查副对角线,判断是否需要堵棋
  for (i = 0, j = col - 1; i < row && j >= 0; i++, j--)
  {
    if (board[i][j] == '*')
      diagonal_2++;
  }
  if (diagonal_2 == 2)
  {
    for (i = 0, j = col - 1; i < row && j >= 0; i++, j--)
    {
      if (board[i][j] == ' ')
      {
        board[i][j] = '#';
        return;
      }
    }
  }
  
  //若不符合堵棋的条件-->随机落子
  while (1)
  {
    x = rand() % row;
    y = rand() % col;
    if (board[x - 1][y - 1] == ' ')
    {
      board[x - 1][y - 1] = '#';
      return;
    }
  }
}


四、 功能优化:递归实现 N 子棋动态输赢判断


从三子棋到N子棋(N行N列N子),所要作出的调整无非只有两个:


1. 动态打印棋盘(见上,通过改变符号常量ROW和COL来更改棋盘大小)


2. 动态判断输赢(不用if 一一列出最终棋盘上可能的情况再一一判断谁胜谁负)


1. 思路


示意图如下(忽略图画得很丑这个事实),向八个方向进行递归,检查并统计该棋子在递归方向有多少个。


我们可以通过创建并定义符号常量NUMBER,来作为判断是否胜利的标准。如三子棋中,令NUMBER为3,则这八个方向中有任意一个方向达成3子连珠,则连珠的这个棋子所代表的玩家获胜。


之所以要用递归解决,是因为我们每次有落子后,都需要判断是否有NUMBER个连着的相同棋子。以向左递归,symbol = '*'  为例,思路如下:


判断该棋子左侧的棋子是否是'*' ?


       -不是,return  symbol_count(结束函数)


       -是,计数器 symbol_count++。判断再左边的棋子是否是'*'?(……不断重复该问题,以此类推)


递归思路

找重复:不断向左遍历判断该位子上的棋子是否是symbol


找变化:棋子的坐标(不断向左移动)


找出口:发现第一个棋盘上不是该棋子的位置(return symbol_count)


最终判断某一方向上的symbol_count是否等于NUMBER,若等于,则该棋子胜出。




2. 代码展示


is_Same.c


//symbol  棋子符号,'*'为玩家,'#'为电脑
//x、y为坐标参数
 
//左
int is_Same_left(char board[ROW][COL], char symbol, int x, int y)
{
  int symbol_count = 0;
  if((y-1 >= 0) && (board[x][y-1] == symbol))
  {
    symbol_count ++;
    symbol_count += is_Same_left(board, symbol, x, y-1);
  }
  return symbol_count;
}
 
//右
int is_Same_right(char board[ROW][COL], char symbol, int x, int y)
{
  int symbol_count = 0;
  if((y+1 < COL) && (board[x][y+1] == symbol))
  {
    symbol_count ++;
    symbol_count += is_Same_right(board, symbol, x, y+1);
  }
  return symbol_count;
}
 
//上
int is_Same_over(char board[ROW][COL], char symbol, int x, int y)
{
  int symbol_count = 0;
  if((x-1 >= 0) && (board[x-1][y] == symbol))
  {
    symbol_count ++;
    symbol_count += is_Same_over(board, symbol, x-1, y);
  }
  return symbol_count;
}
 
//下
int is_Same_down(char board[ROW][COL], char symbol, int x, int y)
{
  int symbol_count = 0;
  if((x+1 < ROW) && (board[x+1][y] == symbol))
  {
    symbol_count ++;
    symbol_count += is_Same_down(board, symbol, x+1, y);
  }
  return symbol_count;
}
 
 
//主对角线上半部分
int is_Same_diagonal1_over(char board[ROW][COL], char symbol, int x, int y)
{
  int symbol_count = 0;
  if((x-1 >= 0) && (y-1 >= 0) && (board[x-1][y-1] == symbol))
  {
    symbol_count ++;
    symbol_count += is_Same_diagonal1_over(board, symbol, x-1, y-1);
  }
  return symbol_count;
}
 
//主对角线下半部分
int is_Same_diagonal1_down(char board[ROW][COL], char symbol, int x, int y)
{
  int symbol_count = 0;
  if((x+1 < ROW) && (y+1 < COL) && (board[x+1][y+1] == symbol))
  {
    symbol_count ++;
    symbol_count += is_Same_diagonal1_down(board, symbol, x+1, y+1);
  }
  return symbol_count;
}
 
//副对角线上半部分
int is_Same_diagonal2_over(char board[ROW][COL], char symbol, int x, int y)
{
  int symbol_count = 0;
  if((x+1 < ROW) && (y-1 >= 0) && (board[x+1][y-1] == symbol))
  {
    symbol_count ++;
    symbol_count += is_Same_diagonal2_over(board, symbol, x+1, y-1);
  }
  return symbol_count;
}
 
//副对角线下半部分
int is_Same_diagonal2_down(char board[ROW][COL], char symbol, int x, int y)
{
  int symbol_count = 0;
  if((x-1 >= 0) && (y+1 < COL) && (board[x-1][y+1] == symbol))
  {
    symbol_count ++;
    symbol_count += is_Same_diagonal2_down(board, symbol, x-1, y+1);
  }
  return symbol_count;
}

test_is_Win.c


int is_Win(char borad[ROW][COL], char symbol, int x, int y)
{
  int cnt1,cnt2,cnt3,cnt4;
  
  cnt1 = cnt2 = cnt3 = cnt4 = 1;
  
  //左右
  cnt1 += is_Same_left(borad, symbol, x, y);
  cnt1 += is_Same_right(borad, symbol, x, y);
  
  //上下
  cnt2 += is_Same_over(borad, symbol, x, y);
  cnt2 += is_Same_down(borad, symbol, x, y);
  
  //主对角线
  cnt3 += is_Same_diagonal1_over(borad, symbol, x, y);
  cnt3 += is_Same_diagonal1_down(borad, symbol, x, y);
  
  //副对角线
  cnt4 += is_Same_diagonal2_over(borad, symbol, x, y);
  cnt4 += is_Same_diagonal2_down(borad, symbol, x, y);
 
  //判断胜利
  //NUMBER 为自己定义的符号常量,用于设置多少个棋子算赢
  if ((cnt1 == NUMBER) || (cnt2 == NUMBER) || (cnt3 == NUMBER) || (cnt4 == NUMBER))
  {
    return 2; //返回2,代表赢
  }
  else
  {
    return is_full(borad, ROW, COL);  //判断是否平局了
  }
}


五、总结


二维数组的使用;for 循环嵌套;递归判断棋子是否一样


(懒得写了,自己总结)


六、课设大作业资源


已上传为资源,有需要者可点击链接下载资源。


资源下载 :C课设大作业 -- 三子棋

https://download.csdn.net/download/wyd_333/86265917





相关文章
|
15小时前
|
C语言
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
4 1
|
4天前
|
C语言
C语言--函数递归与迭代
C语言--函数递归与迭代
|
11天前
|
机器学习/深度学习 算法 C语言
详细介绍递归算法在 C 语言中的应用,包括递归的基本概念、特点、实现方法以及实际应用案例
【6月更文挑战第15天】递归算法在C语言中是强大力量的体现,通过函数调用自身解决复杂问题。递归涉及基本概念如自调用、终止条件及栈空间管理。在C中实现递归需定义递归函数,分解问题并设定停止条件。阶乘和斐波那契数列是经典应用示例,展示了递归的优雅与效率。然而,递归可能导致栈溢出,需注意优化。学习递归深化了对“分而治之”策略的理解。**
28 7
TU^
|
11天前
|
机器学习/深度学习 C语言
C语言之函数递归
C语言之函数递归
TU^
13 1
|
15天前
|
数据库 C语言
C语言进阶 文件操作知识(上)
C语言进阶 文件操作知识(上)
12 3
|
15天前
|
存储 C语言
C语言进阶 文件操作知识(下)
C语言进阶 文件操作知识(下)
14 2
|
20小时前
|
存储 C语言 C++
【C语言刷题系列】水仙花数的打印及进阶
【C语言刷题系列】水仙花数的打印及进阶
|
1天前
|
C语言
【C语言】:递归题
【C语言】:递归题
12 0
|
4天前
|
C语言
C语言----递归--n的k次方
C语言----递归--n的k次方
|
4天前
|
C语言
C语言---函数递归
C语言---函数递归