Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 敌方搜索问题_C#_.net_Artificial Intelligence - Fatal编程技术网

C# 敌方搜索问题

C# 敌方搜索问题,c#,.net,artificial-intelligence,C#,.net,Artificial Intelligence,我正在和一个AI对手使用对抗性搜索技术编写一个Connect4游戏,我有点碰壁了。我觉得我离解决方案不远了,但可能有一个问题,那就是我在转换观点(比如:我的评估分数是基于哪个参与者的观点),在某个地方漏掉一个负号或类似的东西 问题是,在我尝试过的变体中,AI选择在玩家连续三人时不阻止玩家,但在其他情况下,AI玩的是一个完美的游戏,或者他更喜欢阻止玩家,即使他有机会赢得游戏。搜索深度是偶数还是非偶数似乎也很重要,因为AI在6层搜索中是头戴裤子的,这很能说明问题 搜索 使用的算法是negamax,带

我正在和一个AI对手使用对抗性搜索技术编写一个Connect4游戏,我有点碰壁了。我觉得我离解决方案不远了,但可能有一个问题,那就是我在转换观点(比如:我的评估分数是基于哪个参与者的观点),在某个地方漏掉一个负号或类似的东西

问题是,在我尝试过的变体中,AI选择在玩家连续三人时不阻止玩家,但在其他情况下,AI玩的是一个完美的游戏,或者他更喜欢阻止玩家,即使他有机会赢得游戏。搜索深度是偶数还是非偶数似乎也很重要,因为AI在6层搜索中是头戴裤子的,这很能说明问题

搜索

使用的算法是negamax,带有alpha-beta修剪,实现如下:

private int Negamax(int depth, int alpha, int beta, Player player)
{
  Player winner;
  if (Evaluator.IsLeafNode(game, out winner))
  {
    return winner == player ? (10000 / depth) : (-10000 / depth);
  }

  if (depth == Constants.RecursionDepth)
  {
    return Evaluator.Evaluate(game, depth, player);
  }

  foreach (var move in moves)
  {
    int row;

    if (board.DoMove(move, player, out row))
    {
      var value = -Negamax(depth + 1, -beta, -alpha, (Player)1 - (int)player);

      board.UndoMove(move, row, player);

      if (value > alpha)
      {
        alpha = value;
        if (player == Player.AI)
        {
          bestColumn = move;
        }
      }

      if (alpha >= beta)
      {
        return alpha;
      }

    }
  }
  return alpha;
}
public static int Evaluate(Game game, int depth, Player player)
{
  var combinations = game.PlayerCombinations[player];

  int score = 0;

  for (int i = 0; i < combinations.Length; i++)
  {
    switch (combinations[i])
    {
      case 1:
        score += 1;
        break;
      case 2:
        score += 5;
        break;
      case 3:
        score += 15;
        break;
    }
  }

  return score;
}
我不怀疑问题出在这个函数中,但它可能是

评估

我的评估函数基于这样一个事实:在7x6板上,只有69种可能的方式可以获得四列。我有一个大约350项的查找表,其中包含行+列所属的每一列和每一行的硬编码信息。例如,对于第0行和第0列,该表如下所示:

//c1r1
table[0][0] = new int[3];
table[0][0][0] = 21;
table[0][0][1] = 27;
table[0][0][2] = 61;
这意味着第0列第0行是win组合21、27和61的一部分

我还有第二张表,其中包含了两位玩家在每一个胜利组合中的宝石数量。当我移动时,我会执行以下操作:

public bool DoMove(int column, Player p, out int row)
{
  row = moves[column];

  if (row >= 0)
  {
    Cells[column + row * Constants.Columns] = p;

    moves[column]--;

    var combinations = this.Game.PlayerCombinations[p];

    foreach (int i in TerminalPositionsTable.Get(column,row))
    {
      combinations[i]++;
    }

    return true;
  }
  else
  {
    return false;
  }
}
当然,对于
UndoMove
,情况正好相反

因此,在Player.Human对第0列第0行进行移动之后,表中的索引21、27和61处将填充值1。如果我在也是win组合27一部分的单元格中进行另一个移动,那么玩家组合表在索引27处增加到2

我希望我已经说清楚了,因为它在评估函数中被用来非常快速地确定一个球员离连续四个进球有多近

我怀疑问题所在的评估函数如下:

private int Negamax(int depth, int alpha, int beta, Player player)
{
  Player winner;
  if (Evaluator.IsLeafNode(game, out winner))
  {
    return winner == player ? (10000 / depth) : (-10000 / depth);
  }

  if (depth == Constants.RecursionDepth)
  {
    return Evaluator.Evaluate(game, depth, player);
  }

  foreach (var move in moves)
  {
    int row;

    if (board.DoMove(move, player, out row))
    {
      var value = -Negamax(depth + 1, -beta, -alpha, (Player)1 - (int)player);

      board.UndoMove(move, row, player);

      if (value > alpha)
      {
        alpha = value;
        if (player == Player.AI)
        {
          bestColumn = move;
        }
      }

      if (alpha >= beta)
      {
        return alpha;
      }

    }
  }
  return alpha;
}
public static int Evaluate(Game game, int depth, Player player)
{
  var combinations = game.PlayerCombinations[player];

  int score = 0;

  for (int i = 0; i < combinations.Length; i++)
  {
    switch (combinations[i])
    {
      case 1:
        score += 1;
        break;
      case 2:
        score += 5;
        break;
      case 3:
        score += 15;
        break;
    }
  }

  return score;
}
公共静态整数评估(游戏、整数深度、玩家)
{
var组合=游戏。玩家组合[玩家];
智力得分=0;
for(int i=0;i
所以我只是简单地循环了69个可能的赢的组合,然后根据是一块石头、一排两块石头还是三块石头给分数加上一个数字

在这整个对抗性搜索中,我仍然感到困惑的是,我是否应该关心哪个玩家在移动?我的意思是,我应该像这里一样传球给球员,还是应该总是从AI球员的角度来评估棋盘?我尝试了很多组合
aiScore-humanScore
,或者总是从
Player.AI
的角度来看,诸如此类。但我已经走到了死胡同,我尝试过的每一种组合都有很大的缺陷

因此:

  • 我的评估逻辑是否坚实
  • 我应该什么时候“切换透视图” 任何帮助都将不胜感激

    更新

    我已经在下面实现了Brennan的建议,虽然它确实有了很大的改进,但出于某种原因,除了最左边和最右边的两列之外,它不会阻止任何列上的三行,而且只有在搜索深度不均匀的情况下。人工智能即使在搜索深度上也是无敌的,但只有在深度8及以上时才是如此。然后它拒绝再次阻塞。这很能说明我可能非常接近,但仍然有一些关键的缺陷

    也许这与我设置的专栏有关,正如Brennan评论的那样,AI应该在专栏中添加一块石头,但我不知道还有什么时候可以设置它。仅将其设置为深度0不起作用

    更新2

    根据Brennan的更改编辑了代码

    更新3

    用完整的代码创建了一个Github回购。如果您不知道如何使用Git,只需从下载一个zip文件即可


    这是一个.NET4.0项目,运行它将在documents/logs目录中创建negamax算法的日志文件。该解决方案还包含一个测试项目,其中包含一个针对每个棋盘列的测试,当玩家有三个棋盘列时,AI是否选择阻止玩家。

    这些东西会让我的大脑受伤,所以我不确定这个答案是否正确,但接下来就是了

    在negamax中,分数总是相对于当前移动的玩家进行评估。如果是怀特的动作,那么高分对怀特来说是好的。如果是布莱克的动作,那么高分对布莱克有利。因此,如果你有一个叶子节点,分数是+inf还是-inf并不取决于该节点是白人还是黑人获胜,而是取决于你当前评估的玩家是否获胜。替换此项:

    return winner == Player.AI ? (10000 / depth) : (-10000 / depth);
    
    return player == Player.AI ? score : -score;
    
    为此:

    return winner == player ? (10000 / depth) : (-10000 / depth);
    
    return score;
    
    您的评估函数中也存在类似的问题。替换此项:

    return winner == Player.AI ? (10000 / depth) : (-10000 / depth);
    
    return player == Player.AI ? score : -score;
    
    为此:

    return winner == player ? (10000 / depth) : (-10000 / depth);
    
    return score;
    

    再说一遍,我不确定这是否正确。但我希望你尝试这两个改变,并让我知道它是否有效。我很好奇

    如果它没有阻止某些组合,听起来你的可能获胜列表中有一个缺陷

    我也看到了你的评估函数中的一个问题:它为那些没有获胜希望的动作提供了价值。假设你有xoo.x,你在玩o。你的套路上说在这里打15分是值得的,而事实上这值0分。任何已经包含两个玩家牌的赢模式对任何人都没有价值

    我发现在调试这类事情时,调试器的作用很小