Beispiel #1
0
        private void game_PlayerActed(Guid playerId, ActionType action, int amountToCall, int amount)
        {
            var actionBucket = ActionAbstracter.MapToBucket(action, amountToCall, currentGame.PotSize, amount);

            actionHistory.Add(actionBucket);

            FeatureAction featureAction = FeatureActionAbstracter.FromActionBucket(actionBucket);

            var currentPhasehistory = actionHistoriesPerPhase.FirstOrDefault(x => x.Phase == currentGame.Phase);

            currentPhasehistory.ActionHistory.Add(featureAction);

            if (this.Id != playerId)
            {
                Positioning opponentPositioning = Positioning.None;
                Aggression  opponentAggression  = Aggression.None;
                if (currentGame.Phase > GamePhase.PreFlop)
                {
                    opponentPositioning = getOpponentPositioning(currentPhasehistory.ActionHistory.Count);
                    opponentAggression  = getOpponentAggression(opponentPositioning);
                }

                opponent.UpdateFeaturesAfterAction(currentPhasehistory.ActionHistory, currentGame.Phase, opponentAggression, opponentPositioning);
            }
        }
Beispiel #2
0
        public void GetBetSizeBigPotBetTest()
        {
            int betSize         = ActionAbstracter.GetBetSize(ActionBucket.LowBet, 0, 180);
            int expectedBetSize = 10;

            Assert.AreEqual(expectedBetSize, betSize);

            betSize         = ActionAbstracter.GetBetSize(ActionBucket.MediumBet, 0, 180);
            expectedBetSize = 10;
            Assert.AreEqual(expectedBetSize, betSize);

            betSize         = ActionAbstracter.GetBetSize(ActionBucket.HighBet, 0, 150);
            expectedBetSize = 25;
            Assert.AreEqual(expectedBetSize, betSize);
        }
Beispiel #3
0
        public void GetBetSizeSmallPotRaiseTest()
        {
            int betSize         = ActionAbstracter.GetBetSize(ActionBucket.LowBet, 10, 20);
            int expectedBetSize = 20;

            Assert.AreEqual(expectedBetSize, betSize);

            betSize         = ActionAbstracter.GetBetSize(ActionBucket.MediumBet, 10, 20);
            expectedBetSize = 30;
            Assert.AreEqual(expectedBetSize, betSize);

            betSize         = ActionAbstracter.GetBetSize(ActionBucket.HighBet, 10, 30);
            expectedBetSize = 90;
            Assert.AreEqual(expectedBetSize, betSize);
        }
Beispiel #4
0
        public override Task <GameActionEntity> GetAction(List <ActionType> possibleActions, int amountToCall)
        {
            var infoSet = new InformationSet <ActionBucket>();

            infoSet.ActionHistory = actionHistory;
            infoSet.CardBucket    = handBucket;

            RegretGameNode <ActionBucket> gameNode;

            trainedTree.TryGetValue(infoSet.GetLongHashCode(), out gameNode);
            if (gameNode == null)
            {
                // this should never occur
                randomBot.ChipStack = this.ChipStack;
                return(randomBot.GetAction(possibleActions, amountToCall));
            }
            else
            {
                var    optimalStrategy     = gameNode.calculateAverageStrategy();
                var    rand                = new Random();
                double randomValue         = rand.NextDouble();
                bool   isCallActionEnabled = (optimalStrategy.Count == 5);

                int          index                = 0;
                double       sumPercent           = 0;
                ActionBucket selectedActionBucket = ActionBucket.None;
                foreach (ActionBucket action in Enum.GetValues(typeof(ActionBucket)))
                {
                    if (action == ActionBucket.None)
                    {
                        continue;
                    }
                    if (action == ActionBucket.Call && !isCallActionEnabled)
                    {
                        continue;
                    }

                    double newSumPercent = sumPercent + optimalStrategy[index];
                    if (randomValue >= sumPercent && randomValue <= newSumPercent)
                    {
                        selectedActionBucket = action;
                        break;
                    }

                    sumPercent = newSumPercent;
                    index++;
                }

                ActionType selectedAction = ActionAbstracter.MapToAction(selectedActionBucket, amountToCall);
                int        betSize        = 0;
                switch (selectedAction)
                {
                case ActionType.Bet:
                case ActionType.Raise:
                    betSize = ActionAbstracter.GetBetSize(selectedActionBucket, amountToCall, currentGame.PotSize);
                    break;

                case ActionType.Call:
                    betSize = amountToCall;
                    break;
                }


                if (!possibleActions.Contains(selectedAction))
                {
                    // in all-in scenarios actions from bot may differ from possible actions
                    switch (selectedAction)
                    {
                    case ActionType.Bet:
                    case ActionType.Raise:
                    case ActionType.Check:
                        if (possibleActions.Contains(ActionType.Call))
                        {
                            selectedAction = ActionType.Call;
                        }
                        break;

                    default:
                        throw new Exception("Selected action is illegal!");
                    }
                }

                if (ChipStack < betSize)
                {
                    betSize = this.ChipStack;
                }

                return(Task.FromResult <GameActionEntity>(new GameActionEntity
                {
                    ActionType = selectedAction,
                    Amount = betSize,
                    PlayerId = this.Id
                }));
            }
        }
Beispiel #5
0
        private void Game_PlayerActed(Guid playerId, ActionType action, int amountToCall, int amount)
        {
            var actionBucket = ActionAbstracter.MapToBucket(action, amountToCall, currentGame.PotSize, amount);

            actionHistory.Add(actionBucket);
        }
Beispiel #6
0
        public override Task <GameActionEntity> GetAction(List <ActionType> possibleActions, int amountToCall)
        {
            var possibleFeatureActions = new List <FeatureAction>();

            foreach (var possibleAction in possibleActions)
            {
                var featureAction = FeatureActionAbstracter.FromActionType(possibleAction);
                if (!possibleFeatureActions.Contains(featureAction))
                {
                    possibleFeatureActions.Add(featureAction);
                }
            }

            var         currentPhasehistory = actionHistoriesPerPhase.FirstOrDefault(x => x.Phase == currentGame.Phase);
            Positioning opponentPositioning = Positioning.None;
            Aggression  opponentAggression  = Aggression.None;

            if (currentGame.Phase > GamePhase.PreFlop)
            {
                opponentPositioning = getOpponentPositioning(currentPhasehistory.ActionHistory.Count);
                opponentAggression  = getOpponentAggression(opponentPositioning);
            }

            var          counterAction        = opponent.GetCounterAction(possibleFeatureActions, currentPhasehistory.ActionHistory, currentGame.Phase, opponentAggression, opponentPositioning);
            ActionBucket selectedActionBucket = ActionBucket.None;

            if (counterAction == null)
            {
                if (currentGame.Phase == GamePhase.PreFlop)
                {
                    switch (opponent.PlayStyle)
                    {
                    case PlayStyle.LooseAggressive:
                    case PlayStyle.LoosePassive:
                        if (this.IsBigBlind)
                        {
                            selectedActionBucket = ActionBucket.LowBet;
                        }
                        else
                        {
                            if (startHandBucket >= StartHandBucket.VeryBad)
                            {
                                selectedActionBucket = ActionBucket.LowBet;
                            }
                            else
                            {
                                selectedActionBucket = ActionBucket.Pass;
                            }
                        }

                        break;

                    case PlayStyle.TightAggressive:
                    case PlayStyle.TightPassive:
                        //play hyper aggressive
                        selectedActionBucket = ActionBucket.LowBet;
                        break;
                    }
                }
                else
                {
                    switch (opponent.PlayStyle)
                    {
                    case PlayStyle.LooseAggressive:
                        //play TightAggressive
                        if (handStrengthBucket >= HandStrengthBucket.HighCardAce)
                        {
                            selectedActionBucket = ActionBucket.LowBet;
                        }
                        else
                        {
                            selectedActionBucket = ActionBucket.Pass;
                        }
                        break;

                    case PlayStyle.LoosePassive:
                        selectedActionBucket = ActionBucket.LowBet;
                        break;

                    case PlayStyle.TightAggressive:
                        //play LooseAggressive
                        if (handStrengthBucket >= HandStrengthBucket.LowPair)
                        {
                            selectedActionBucket = ActionBucket.LowBet;
                        }
                        else
                        {
                            selectedActionBucket = ActionBucket.Pass;
                        }
                        break;

                    case PlayStyle.TightPassive:
                        //play LooseAggressive
                        if (handStrengthBucket >= HandStrengthBucket.HighCardElse)
                        {
                            selectedActionBucket = ActionBucket.LowBet;
                        }
                        else
                        {
                            selectedActionBucket = ActionBucket.Pass;
                        }
                        break;
                    }
                }
            }
            else
            {
                if (currentGame.Phase == GamePhase.PreFlop)
                {
                    switch (counterAction)
                    {
                    case FeatureAction.Bet:
                        if (startHandBucket > StartHandBucket.Worst)
                        {
                            selectedActionBucket = ActionBucket.LowBet;
                        }
                        break;

                    case FeatureAction.Pass:
                        if (startHandBucket < StartHandBucket.Bad)
                        {
                            selectedActionBucket = ActionBucket.Pass;
                        }
                        break;
                    }
                }
                else
                {
                    switch (counterAction)
                    {
                    case FeatureAction.Bet:
                        selectedActionBucket = ActionBucket.LowBet;
                        break;

                    case FeatureAction.Pass:
                        if (handStrengthBucket < HandStrengthBucket.LowPair)
                        {
                            selectedActionBucket = ActionBucket.Pass;
                        }
                        break;
                    }
                }
            }

            switch (selectedActionBucket)
            {
            case ActionBucket.None:
                //no counter move has been found. Fall back to randomness
                randomBot.ChipStack = this.ChipStack;
                return(randomBot.GetAction(possibleActions, amountToCall));

            default:
                ActionType selectedAction = ActionAbstracter.MapToAction(selectedActionBucket, amountToCall);
                int        betSize        = 0;
                switch (selectedAction)
                {
                case ActionType.Bet:
                case ActionType.Raise:
                    betSize = ActionAbstracter.GetBetSize(selectedActionBucket, amountToCall, currentGame.PotSize);
                    break;

                case ActionType.Call:
                    betSize = amountToCall;
                    break;
                }


                if (!possibleActions.Contains(selectedAction))
                {
                    switch (selectedAction)
                    {
                    case ActionType.Bet:
                    case ActionType.Raise:
                    case ActionType.Check:
                        if (possibleActions.Contains(ActionType.Call))
                        {
                            selectedAction = ActionType.Call;
                        }
                        break;

                    default:
                        throw new Exception("Selected action is illegal!");
                    }
                }

                if (ChipStack < betSize)
                {
                    betSize = this.ChipStack;
                }

                return(Task.FromResult <GameActionEntity>(
                           new GameActionEntity
                {
                    ActionType = selectedAction,
                    Amount = betSize,
                    PlayerId = this.Id
                }));
            }
        }
        public override Task <GameActionEntity> GetAction(List <ActionType> possibleActions, int amountToCall)
        {
            ActionBucket selectedActionBucket;

            switch (currentGame.Phase)
            {
            case GamePhase.PreFlop:
                double startHandStrengthRatio = (double)startHandBucket / (double)StartHandBucket.Best;
                if (TightRatio <= startHandStrengthRatio)
                {
                    startHandStrengthRatio = 1 - startHandStrengthRatio;
                    if (AggressiveRatio >= startHandStrengthRatio)
                    {
                        selectedActionBucket = ActionBucket.LowBet;
                    }
                    else
                    {
                        selectedActionBucket = ActionBucket.Call;
                    }
                }
                else
                {
                    selectedActionBucket = ActionBucket.Pass;
                }
                break;

            default:
                double handStrengthRatio = 1 - (double)handStrengthBucket / (double)HandStrengthBucket.TopHands;
                if (AggressiveRatio >= handStrengthRatio)
                {
                    selectedActionBucket = ActionBucket.LowBet;
                }
                else
                {
                    selectedActionBucket = ActionBucket.Pass;
                }

                break;
            }

            ActionType selectedAction = ActionAbstracter.MapToAction(selectedActionBucket, amountToCall);
            int        betSize        = 0;

            switch (selectedAction)
            {
            case ActionType.Bet:
            case ActionType.Raise:
                betSize = ActionAbstracter.GetBetSize(selectedActionBucket, amountToCall, currentGame.PotSize);
                break;

            case ActionType.Call:
                betSize = amountToCall;
                break;
            }

            if (!possibleActions.Contains(selectedAction))
            {
                switch (selectedAction)
                {
                case ActionType.Bet:
                case ActionType.Raise:
                case ActionType.Check:
                    if (possibleActions.Contains(ActionType.Call))
                    {
                        selectedAction = ActionType.Call;
                    }
                    break;

                case ActionType.Call:
                    if (possibleActions.Contains(ActionType.Check))
                    {
                        selectedAction = ActionType.Check;
                    }
                    break;

                default:
                    throw new Exception("Selected action is illegal!");
                }
            }

            if (ChipStack < betSize)
            {
                betSize = this.ChipStack;
            }

            return(Task.FromResult <GameActionEntity>(
                       new GameActionEntity
            {
                ActionType = selectedAction,
                Amount = betSize,
                PlayerId = this.Id
            }));
        }
        /// <summary>
        /// Traverses the sub tree of the passed hand buckets recursively. Calculates and then backpropagates the counter factual regret for each node.
        /// </summary>
        /// <param name="gameState"></param>
        /// <param name="handBuckets"></param>
        /// <param name="actions"></param>
        /// <param name="probabilityPlayer1">probability of player 1 to reach this node</param>
        /// <param name="probabilityPlayer2">probability of player 2 to reach this node</param>
        /// <returns></returns>
        private float CalculateCounterFactualRegret(HeadsUpGameState gameState, byte[] handBuckets, List <ActionBucket> actions, float probabilityPlayer1, float probabilityPlayer2)
        {
            int plays       = actions.Count;
            int playerIndex = plays % 2;

            var          newState   = gameState.GetCopy();
            ActionBucket lastAction = ActionBucket.None;

            if (actions.Count > 0)
            {
                lastAction = actions[plays - 1];
            }
            ActionBucket secondLastAction = ActionBucket.None;

            if (actions.Count > 1)
            {
                secondLastAction = actions[plays - 2];
            }

            bool nextActionCallEnabled = true;
            bool phaseChanged          = false;

            //the last actions determine whether the next phase has to be set or whether the game (i.e. the recursion) ends
            switch (lastAction)
            {
            case ActionBucket.Pass:
                if (secondLastAction == ActionBucket.LowBet ||
                    secondLastAction == ActionBucket.HighBet ||
                    secondLastAction == ActionBucket.MediumBet)
                {
                    int payoff = (newState.PotSize - newState.AmountToCall) / 2;
                    return((playerIndex == 0) ? payoff : -payoff);
                }

                switch (secondLastAction)
                {
                case ActionBucket.None:
                    return((playerIndex == 0) ? HeadsupGame.SmallBlindSize : -HeadsupGame.SmallBlindSize);

                case ActionBucket.Call:
                    newState.SetNextPhase(newState.Phase);
                    phaseChanged = true;
                    break;

                case ActionBucket.Pass:
                    //check if last pass count is dividable by 2 (then it's a new phase)
                    int lastActionPassCount = 0;
                    for (int i = actions.Count - 1; i >= 0; i--)
                    {
                        if (actions[i] == ActionBucket.Pass)
                        {
                            lastActionPassCount++;
                        }
                        else
                        {
                            //special case: if it's first round, call pass results in ending the round
                            if ((actions[i] == ActionBucket.Call && i == 0) &&
                                actions.Count > 2 && actions[i + 1] == ActionBucket.Pass)
                            {
                                lastActionPassCount--;
                            }
                            break;
                        }
                    }

                    if (lastActionPassCount % 2 == 0)
                    {
                        newState.SetNextPhase(newState.Phase);
                        phaseChanged = true;
                    }
                    break;
                }
                nextActionCallEnabled = false;
                break;

            case ActionBucket.Call:
                newState.PotSize     += newState.AmountToCall;
                newState.AmountToCall = 0;
                //special case for first round: big blind needs to check or bet
                if (actions.Count == 1)
                {
                    nextActionCallEnabled = false;
                }
                else
                {
                    newState.SetNextPhase(newState.Phase);
                    phaseChanged = true;
                }
                break;

            case ActionBucket.HighBet:
            case ActionBucket.MediumBet:
            case ActionBucket.LowBet:
                int betSize = 0;
                if (newState.AmountToCall > 0 && newState.AmountToCall == HeadsupGame.SmallBlindSize)
                {
                    //exception: first round
                    newState.PotSize += HeadsupGame.SmallBlindSize;
                }

                int lastActionLowBetCount = 0;
                for (int i = actions.Count - 1; i >= 0; i--)
                {
                    if (actions[i] == ActionBucket.LowBet)
                    {
                        lastActionLowBetCount++;
                    }
                    else
                    {
                        break;
                    }
                }

                betSize = ActionAbstracter.GetBetSize(lastAction, newState.AmountToCall, newState.PotSize);
                if (betSize > 0)
                {
                    newState.AmountToCall = betSize;
                    newState.PotSize     += betSize;
                    if (newState.PotSize >= HeadsupGame.StackSize * 2)
                    {
                        phaseChanged   = true;
                        newState.Phase = GamePhase.Showdown;
                    }
                }
                else
                {
                    phaseChanged   = true;
                    newState.Phase = GamePhase.Showdown;
                }
                break;
            }

            //if the phase has changed, the next event has to occur (e.g. adding a card to the current board)
            if (phaseChanged)
            {
                if (newState.Phase == GamePhase.Showdown)
                {
                    int payoff         = newState.PotSize / 2;
                    var handComparison = HandComparer.Compare(newState.Player1HoleCards, newState.Player2HoleCards, newState.Board);
                    switch (handComparison)
                    {
                    case HandComparison.None:
                        return(0);

                    case HandComparison.Player1Won:
                        return((playerIndex == 0) ? payoff : -payoff);

                    case HandComparison.Player2Won:
                        return((playerIndex == 0) ? -payoff : payoff);
                    }
                }
                else
                {
                    nextActionCallEnabled = false;

                    List <Card> currentBoard = null;
                    switch (newState.Phase)
                    {
                    case GamePhase.Flop:
                        currentBoard = gameState.Board.Take(3).ToList();
                        break;

                    case GamePhase.Turn:
                        currentBoard = gameState.Board.Take(4).ToList();
                        break;

                    case GamePhase.River:
                        currentBoard = gameState.Board;
                        break;
                    }

                    //evaluate new hand buckets
                    byte bucket1 = (byte)HandStrengthAbstracter.MapToBucket(currentBoard, newState.Player1HoleCards);
                    byte bucket2 = (byte)HandStrengthAbstracter.MapToBucket(currentBoard, newState.Player2HoleCards);
                    handBuckets = new byte[] { bucket1, bucket2 };
                }
            }

            var infoSet = new InformationSet <ActionBucket>()
            {
                CardBucket    = handBuckets[playerIndex],
                ActionHistory = actions
            };

            int numberOfActions = Settings.NumberOfActions;

            if (!nextActionCallEnabled)
            {
                numberOfActions = Settings.NumberOfActions - 1;
            }

            RegretGameNode <ActionBucket> node = null;
            long hash = infoSet.GetLongHashCode();

            //checks if the current information set already exists in O(1)
            if (!GameNodes.TryGetValue(hash, out node))
            {
                node         = new RegretGameNode <ActionBucket>(numberOfActions);
                node.InfoSet = infoSet;
                GameNodes.Add(hash, node);
            }

            //gets the strategy of the current player
            var strategy = node.calculateStrategy(playerIndex == 0 ? probabilityPlayer1 : probabilityPlayer2);

            // initialise utilities  with zeros
            var utilities = new List <float>(numberOfActions);

            for (int i = 0; i < numberOfActions; i++)
            {
                utilities.Add(0);
            }

            float nodeUtility = 0;
            int   index       = 0;

            // traverse the tree further down with a breadth first search
            foreach (ActionBucket nextAction in Enum.GetValues(typeof(ActionBucket)))
            {
                //skip illegal actions
                if (nextAction == ActionBucket.None)
                {
                    continue;
                }
                if (nextAction == ActionBucket.Call && !nextActionCallEnabled)
                {
                    continue;
                }

                var nextHistory = new List <ActionBucket>();
                nextHistory.AddRange(actions.ToArray());
                nextHistory.Add(nextAction);

                utilities[index] = playerIndex == 0
                    ? -CalculateCounterFactualRegret(newState, handBuckets, nextHistory, probabilityPlayer1 * strategy[index], probabilityPlayer2)
                   : -CalculateCounterFactualRegret(newState, handBuckets, nextHistory, probabilityPlayer1, probabilityPlayer2 * strategy[index]);

                //accumulate the utility of the sub branches
                nodeUtility += strategy[index] * utilities[index];
                index++;
            }

            for (int i = 0; i < numberOfActions; i++)
            {
                //calculate the regret
                float regret = utilities[i] - nodeUtility;
                //calculate the regret sum based on the current player
                node.RegretSum[i] += (playerIndex == 0 ? probabilityPlayer2 : probabilityPlayer1) * regret;
            }

            return(nodeUtility);
        }