【问题标题】:Transforming game tree to the list of players' behavior将博弈树转换为玩家行为列表
【发布时间】:2022-01-19 05:18:10
【问题描述】:

考虑一个游戏,其中N 玩家争夺一些奖品

玩家编号为1N,拥有A1...AN 的金额并在一个圆圈中移动

玩家1第一个行动

他可以下注a1<=A1或将转牌传给玩家2

如果玩家1 下注,那么玩家2 可以下更大的赌注a2>a1,鉴于a2<=A2,或者放弃

考虑到a3<=A3,下一个玩家可以下更大的赌注a3>a2,或者传球等等

如果棋手返回1,他可以将钱加到他的赌注中,以便(a1+added money)>ai,其中ai是最后一次赌注,因为a1+added money<=A1

每位玩家每场游戏仅限2 操作

游戏描述包括

  • 每个项目包含player ID,他的位置(1...N)和他的初始金额的玩家列表

  • 每个项目包含player IDaction(下注/pass)和amount(如果有)的有序操作列表

例子:

玩家名单:

PlayerId Position Amount
P1 1 60
P2 2 140
P3 3 90
P4 4 200

动作列表:

PlayerId Action Amount
P1 bet 20
P2 pass
P3 raise 30
P4 raise 40
P1 raise 60
P3 pass
P4 pass

将游戏描述作为输入,我想将其转换为 N 个项目的列表,其中每个项目描述玩家在游戏中的行为:

  • 玩家 ID,
  • 可以先下注(如果玩家有机会开始下注,则为 true,否则为 false),
  • 先下注(如果玩家开始下注则为 true,否则为 false)
  • Could Raise(如果玩家有机会加注之前的赌注则为 true,否则为 false),
  • 加注(如果玩家有机会加注之前的赌注则为 true,否则为 false),
  • 可以再加注(如果玩家有机会再加注则为真,否则为假),
  • 重新加注(如果玩家先下注、被加注并重新加注,则为 true,否则为 false),
  • 玩家活跃的次数(下注、加注或再加注)
  • 玩家在游戏中投入的总金额

上述示例的输出应该是:

  • P1,真,真,假,假,真,真,2,60
  • P2,假,假,假,假,假,假,0,0
  • P3,假,假,真,真,真,假,1、30
  • P4,假,假,真,真,真,假,1、40

理想情况下,代码将遍历操作列表一次,并通过使用每个操作更新玩家的统计对象来构建所需的结果。谁能推荐一个算法来解决这个问题?

【问题讨论】:

  • MCTS(蒙特卡罗树搜索)。或者,更简单一点,只是在游戏树上进行基本的深度搜索
  • @draz 这位 OP 不打算制作游戏 AI,他们问的是如何将整个游戏的动作列表汇总到每个玩家的统计数据列表中
  • hmm,如果你已经收集了玩家的行为,不需要计算,其实我看不出检索信息的问题(?)

标签: c# algorithm


【解决方案1】:

首先,定义一个匹配所需输出数据的类:

class PlayerStatistics
{
    public string PlayerId { get; set; }
    public bool CouldBetFirst { get; set; }
    public bool BetFirst { get; set; }
    public bool CouldRaise { get; set; }
    public bool Raised { get; set; }
    public bool CouldReraise { get; set; }
    public bool Reraised { get; set; }
    public int NumberOfActions { get; set; }
    public decimal AmountPlaced { get; set; }
}

您知道有多少玩家,并且您知道每个玩家都会有一个这些统计对象,因此您可以制作一个列表或数组。

现在是聚合。您拥有已采取的操作的列表,但您也关心对于您的统计数据可能的操作。这意味着您必须在某处重玩游戏并跟踪游戏状态,这样您才能知道玩家是否真的有足够的钱来下注、加注等。

class PlayerState
{
    public string PlayerId { get; set; }
    public bool Passed { get; set; }
    public decimal AmountAvailable { get; set; }
    public decimal AmountPlaced { get; set; }
}

// These variables make up the game state
decimal highestAmountPlaced = 0;
int leadingPlayerIndex = -1;
var playerStates = new PlayerState[players.Count];

// Initialize the player states using the info in the list of players
for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
{
    playerStates[playerIndex].PlayerId = players[playerIndex].PlayerId;
    playerStates[playerIndex].AmountAvailable = players[playerIndex].Amount;
}

每个玩家只能执行两个动作。这意味着您可以简单地循环播放玩家列表 2 次。如果有人在此之前获胜,循环可能会提前退出。这里的关键是在一个比循环更大范围的变量中跟踪当前动作,这样我们就可以在每个循环中重放动作。

int actionIndex = 0;

for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
{
    var playerState = states[playerIndex];
    if (leadingPlayerIndex == -1)
    {
        playerState.CouldBetFirst = true;
    }
    else if (playerState.Available > amountPlaced)
    {
        playerState.CouldRaise = true;
    }
    else
    {
        // They can't raise, but can they match, can they go all-in?
    }

    var playerAction = actions[actionIndex];
    var playerStatistics = statistics[playerIndex];
    switch (playerAction.Action)
    {
        case "bet":
            playerStatistics.BetFirst = true;
            playerStatistics.NumberOfActions++;
            playerState.AmountPlaced += playerAction.Amount;
            playerState.AmountAvailable -= playerAction.Amount;
            highestAmountPlaced = playerState.AmountPlaced;
            leadingPlayerIndex = playerIndex;
            break;
        case "raise":
            playerStatistics.Raised = true;
            playerStatistics.NumberOfActions++;
            playerState.AmountPlaced += playerAction.Amount;
            playerState.AmountAvailable -= playerAction.Amount;
            highestAmountPlaced = playerState.AmountPlaced;
            leadingPlayerIndex = playerIndex;
            break;
        case "pass":
            playerState.Passed = true;
            break;
    }

    actionIndex++;
}

if (actionIndex < actions.Count)
{
    for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
    {
        var playerState = states[playerIndex];
        if (playerState.Passed)
            continue;

        if (leadingPlayerIndex == playerIndex)
        {
            // This player is still leading the bet
            break;
        }
        else if (playerState.Available > amountPlaced)
        {
            playerState.CouldReraise = true;
        }
        else
        {
            // They can't raise, but can they match, can they go all-in?
        }

        var playerAction = actions[actionIndex];
        var playerStatistics = statistics[playerIndex];
        switch (playerAction.Action)
        {
            case "raise":
                playerStatistics.Reraised = true;
                playerStatistics.NumberOfActions++;
                playerState.AmountPlaced += playerAction.Amount;
                playerState.AmountAvailable -= playerAction.Amount;
                highestAmountPlaced = playerState.AmountPlaced;
                leadingPlayerIndex = playerIndex;
                break;
            case "pass":
                playerState.Passed = true;
                break;
        }

        actionIndex++;
    }
}

【讨论】:

  • 这似乎解决了所有 9 个“问题”。接受这项工作可能是合适的......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2014-02-21
相关资源
最近更新 更多