万字详解: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





相关文章
|
4月前
|
机器学习/深度学习 C语言
九/十:《初学C语言》— 扫雷游戏实现和函数递归基础
【8月更文挑战第5天】本篇文章用C语言采用多文件编写实现了一个基础的扫雷游戏(附源码),并讲解了关于函数递归的基础概念及其相对应的习题练习(附源码)
46 1
九/十:《初学C语言》— 扫雷游戏实现和函数递归基础
|
6月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
2月前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
69 7
|
2月前
|
C语言
c语言回顾-函数递归(上)
c语言回顾-函数递归(上)
41 2
|
2月前
|
C语言
c语言回顾-函数递归(下)
c语言回顾-函数递归(下)
42 0
|
4月前
|
机器学习/深度学习 C语言
【C语言篇】递归详细介绍(基础概念习题及汉诺塔等进阶问题)
要保持最小的步数,每一次汉诺塔问题(无论是最初还是递归过程中的),如果此时初始柱盘子数为偶数,我们第一步是把最上面的盘子移动到中转柱,如果为奇数,我们第一步则是将其移动到目标柱。
100 0
【C语言篇】递归详细介绍(基础概念习题及汉诺塔等进阶问题)
|
4月前
|
C语言
C语言中的递归
C语言中的递归
|
5月前
|
存储 编译器 C语言
|
4月前
|
算法 编译器 C语言
【C语言】递归
【C语言】递归
27 0
|
6月前
|
C语言
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
32 1