Exemplo n.º 1
0
 public InformationSet GetFullInformationSet(string code)
 {
     InformationSet result = new InformationSet();
     Core.Functions client = new Core.Functions();
     result = client.GetFullInformationSet(code);
     return result;
 }
Exemplo n.º 2
0
    public static string GetPathFromInformationSet(InformationSet infoSet)
    {
        switch (infoSet)
        {
        case InformationSet.WorldInformation:
            return(GameInfo.WorldDataPath);

        case InformationSet.LevelInformation:
            return(GameInfo.LevelDataPath);

        case InformationSet.RoomInformation:
            return(GameInfo.RoomDataPath);

        case InformationSet.InteractableInformation:
            return(GameInfo.InteractableDataPath);

        case InformationSet.ObstacleInformation:
            return(GameInfo.ObstacleDataPath);

        case InformationSet.ItemInformation:
            return(GameInfo.ItemDataPath);

        case InformationSet.MessageInformation:
            return(GameInfo.MessageDataPath);
        }
        return("");
    }
Exemplo n.º 3
0
 public PlayerNode(string playerName, string charName, InformationSet infoSet)
 {
     this.playerName = playerName;
     this.charName   = charName;
     this.InfoSet    = infoSet;
     playerDead      = false;
     character       = CharacterAbstractFactory.CreatePlayer(charName);
 }
Exemplo n.º 4
0
    public static string LoadGameFile(InformationSet informationSet)
    {
        string path = GetPathFromInformationSet(informationSet);

        if (File.Exists(path))
        {
            return(File.ReadAllText(path));
        }

        return("");
    }
Exemplo n.º 5
0
        public void TrainedOneHandTest()
        {
            // this number highly depends on abstraction (i.e. stack size, bet size, hand strength and actions)
            int numberOfStatesPerBucket = 15908;

            Assert.AreEqual(numberOfStatesPerBucket, trainer.GameNodes.Count);

            //check if information sets are complete
            var infoSet = new InformationSet <ActionBucket>()
            {
                CardBucket    = (int)StartHandBucket.Best,
                ActionHistory = new List <ActionBucket>()
            };

            //check if some nodes has been added to the dictionary
            var gameNode = trainer.GameNodes[infoSet.GetHashCode()];

            Assert.IsNotNull(gameNode);

            infoSet = new InformationSet <ActionBucket>()
            {
                CardBucket    = (int)StartHandBucket.Best,
                ActionHistory = new List <ActionBucket>()
                {
                    ActionBucket.Call, ActionBucket.LowBet
                }
            };
            gameNode = trainer.GameNodes[infoSet.GetHashCode()];
            Assert.IsNotNull(gameNode);

            infoSet = new InformationSet <ActionBucket>()
            {
                CardBucket    = (int)StartHandBucket.Best,
                ActionHistory = new List <ActionBucket>()
                {
                    ActionBucket.LowBet, ActionBucket.LowBet, ActionBucket.LowBet, ActionBucket.LowBet
                }
            };
            gameNode = trainer.GameNodes[infoSet.GetHashCode()];
            Assert.IsNotNull(gameNode);

            infoSet = new InformationSet <ActionBucket>()
            {
                CardBucket    = (int)StartHandBucket.Worst,
                ActionHistory = new List <ActionBucket>()
                {
                    ActionBucket.LowBet, ActionBucket.LowBet, ActionBucket.LowBet
                }
            };
            gameNode = trainer.GameNodes[infoSet.GetHashCode()];
            Assert.IsNotNull(gameNode);
        }
Exemplo n.º 6
0
        private List <float> getOptimalStrategy(byte handBucket, List <ActionBucket> actions)
        {
            var infoSet = new InformationSet <ActionBucket>()
            {
                CardBucket    = handBucket,
                ActionHistory = actions
            };

            var gameNode = trainedTree[infoSet.GetHashCode()];

            Assert.IsNotNull(gameNode);
            return(gameNode.calculateAverageStrategy());
        }
Exemplo n.º 7
0
        public void NashEquilibriumPlayer1Test()
        {
            // get information set (jack and first action)
            var infoSet = new InformationSet <GameAction>()
            {
                CardBucket    = (int)CardValue.Jack,
                ActionHistory = new List <GameAction>() // No actions => first turn
            };

            var gameNode        = trainer.GameNodes[infoSet.GetHashCode()];
            var averageStrategy = gameNode.calculateAverageStrategy();

            // Jack's bet probability (alpha) must lie within 0 and 1/3 to be in nash equilibrium
            var   alpha    = averageStrategy[(int)GameAction.Bet];
            float alphaMax = 1f / 3f;
            float alphaMin = 0f;

            Assert.IsTrue(alpha < (alphaMax + tolerance));
            Assert.IsTrue(alpha > (alphaMin - tolerance));

            // sum of bet and pass probability should be approximately one, since there are only two actions in Kuhn Poker
            var sum = averageStrategy[(int)GameAction.Pass] + averageStrategy[(int)GameAction.Bet];

            Assert.IsTrue(sum < 1 + tolerance && sum > 1 - tolerance);

            // alpha value is further used to evaluate nash equilibrium of player 1
            infoSet.CardBucket = (int)CardValue.King; // 3 = King
            gameNode           = trainer.GameNodes[infoSet.GetHashCode()];
            averageStrategy    = gameNode.calculateAverageStrategy();

            // King bet probability should be 3 * alpha
            var betProbability         = averageStrategy[(int)GameAction.Bet];
            var betProbabilityExpected = (3 * alpha);

            Assert.IsTrue(Math.Abs(betProbability - betProbabilityExpected) < tolerance);

            infoSet.CardBucket = (int)CardValue.Queen;
            // player 1 checked first turn, Player 2 bet second turn.
            infoSet.ActionHistory = new List <GameAction>()
            {
                GameAction.Pass, GameAction.Bet
            };
            gameNode        = trainer.GameNodes[infoSet.GetHashCode()];
            averageStrategy = gameNode.calculateAverageStrategy();

            // Queen should call (bet) with a probability of alpha + 1/3
            betProbability         = averageStrategy[(int)GameAction.Bet];
            betProbabilityExpected = (alpha + 1f / 3f);
            Assert.IsTrue(Math.Abs(betProbability - betProbabilityExpected) < tolerance);
        }
Exemplo n.º 8
0
        public InformationSet GetFullInformationSet(string code)
        {
            InformationSet result = new InformationSet();

            if (false) //WebAPI
            {
                // To do : problème
            }
            else //DLL
            {
                Core.Functions client = new Core.Functions();
                result = client.GetFullInformationSet(code);
            }
            return(result);
        }
    /// <summary>
    /// Dropdown has changed - Update Information.
    /// </summary>
    public void InformationSetChanged()
    {
        MessageUI.text = "";
        string value = InformationSetType.options[InformationSetType.value].text;

        _currentInformationSet = (InformationSet)Enum.Parse(typeof(InformationSet), value);

        if (_currentInformationSet == InformationSet.NameGeneration)
        {
            ShowNameGenerationEditor();
            PopulateNameGenerationFromFile();
        }
        else
        {
            ShowDataEditor();
            PopulateFromJson();
        }
    }
Exemplo n.º 10
0
        public void NashEquilibriumPlayer2Test()
        {
            // Get information set (jack and first action)
            var infoSet = new InformationSet <GameAction>()
            {
                CardBucket    = (int)CardValue.Jack,
                ActionHistory = new List <GameAction>()
                {
                    GameAction.Bet
                }                                                         // No actions => first turn
            };

            var gameNode        = trainer.GameNodes[infoSet.GetHashCode()];
            var averageStrategy = gameNode.calculateAverageStrategy();

            // Jack should never bet (i.e. call) after player 1 bet
            var betProbability         = averageStrategy[(int)GameAction.Bet];
            var betProbabilityExpected = 0f;

            Assert.IsTrue(Math.Abs(betProbability - betProbabilityExpected) < tolerance);

            // Queen should bet (i.e. call) 1/3 after player 1 bet
            infoSet.CardBucket     = (int)CardValue.Queen;
            gameNode               = trainer.GameNodes[infoSet.GetHashCode()];
            averageStrategy        = gameNode.calculateAverageStrategy();
            betProbability         = averageStrategy[(int)GameAction.Bet];
            betProbabilityExpected = (1f / 3f);
            Assert.IsTrue(Math.Abs(betProbability - betProbabilityExpected) < tolerance);


            // Jack should bet 1/3 of the time after being passed (i.e. checked) to
            infoSet.CardBucket    = (int)CardValue.Jack;
            infoSet.ActionHistory = new List <GameAction> {
                GameAction.Pass
            };
            gameNode               = trainer.GameNodes[infoSet.GetHashCode()];
            averageStrategy        = gameNode.calculateAverageStrategy();
            betProbability         = averageStrategy[(int)GameAction.Bet];
            betProbabilityExpected = (1f / 3f);
            Assert.IsTrue(Math.Abs(betProbability - betProbabilityExpected) < tolerance);
        }
Exemplo n.º 11
0
 public InformationSet GetFullInformationSet(string code)
 {
     if (true) //WebAPI
     {
         RunAsync(code).Wait();
     }
     else //DLL
     {
         Functions client = new Functions();
         try
         {
             result = client.GetFullInformationSet(code);
         }
         catch (Exception e)
         {
             result.executeResult         = -1;
             result.AdditionalInformation = e.Message;
         }
     }
     return(result);
 }
Exemplo n.º 12
0
        static async Task RunAsync(string code)
        {
            using (var client = new HttpClient())
            {
                string baseAddress = ConfigurationManager.AppSettings["baseAddress"];
                client.BaseAddress = new Uri(baseAddress);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                HttpResponseMessage response = new HttpResponseMessage();
                string responseResult        = "";

                // GetFullInformationSet
                string urlEncode = WebUtility.UrlEncode(code);
                response = await client.GetAsync("api/ParserIO/GetFullInformationSet?code=" + urlEncode);

                if (response.IsSuccessStatusCode)
                {
                    responseResult = await response.Content.ReadAsStringAsync();

                    JavaScriptSerializer JSserializer = new JavaScriptSerializer();
                    result = JSserializer.Deserialize <InformationSet>(responseResult);
                }
            }
        }
Exemplo n.º 13
0
    public static Type GetTypeFromInformationSet(InformationSet infoSet)
    {
        switch (infoSet)
        {
        case InformationSet.WorldInformation:
            return(typeof(WorldData));

        case InformationSet.LevelInformation:
            return(typeof(LevelArray));

        case InformationSet.RoomInformation:
            return(typeof(RoomArray));

        case InformationSet.InteractableInformation:
            return(typeof(InteractableArray));

        case InformationSet.ObstacleInformation:
            return(typeof(ObstacleArray));

        case InformationSet.ItemInformation:
            return(typeof(ItemArray));
        }
        return(null);
    }
Exemplo n.º 14
0
 public RuleBasedNode(string playerName, string charName, InformationSet infoSet, bool playerdead) : base(playerName, charName, infoSet, playerdead)
 {
 }
Exemplo n.º 15
0
        public int Parse_1(string Barcode,
                           out string ACL,                  //1
                           out string ADDITIONALID,         //2
                           out string BESTBEFORE,           //3
                           out string CIP,                  //4
                           out string Company,              //5
                           out bool containsOrMayContainId,
                           out string CONTENT,              //6
                           out string COUNT,                //7
                           out string EAN,
                           out string Expiry,               //8
                           out string Family,               //9
                           out string GTIN,                 //10
                           out string LIC,                  //11
                           out string Lot,                  //12
                           out string LPP,                  //13
                           out string NaS7,                 //14
                           out string NormalizedBESTBEFORE, //15
                           out string NormalizedExpiry,     //16
                           out string NormalizedPRODDATE,   //17
                           out string PCN,                  //18
                           out string PRODDATE,             //19
                           out string Product,              //20
                           out string Quantity,             //21
                           out string Reference,            //22
                           out string NaSIdParamName,
                           out string Serial,               //23
                           out string SSCC,                 //24
                           out string SubType,              //25
                           out string SymbologyID,
                           out string Type,                 //26
                           out string UDI,
                           out string UoM,                  //28
                           out string UPN,
                           out string VARCOUNT,             //29
                           out string VARIANT,              //30
                           out string Errors)
        {
            int executeResult = 0;

            ACL                    = "";    //1
            ADDITIONALID           = "";    //2
            BESTBEFORE             = "";    //3
            CIP                    = "";    //4
            Company                = "";    //5
            containsOrMayContainId = false; //31
            CONTENT                = "";    //6
            COUNT                  = "";    //7
            EAN                    = "";
            Expiry                 = "";    //8
            Family                 = "";    //9
            GTIN                   = "";    //10
            LIC                    = "";    //11
            Lot                    = "";    //12
            LPP                    = "";    //13
            NaS7                   = "";    //14
            NormalizedBESTBEFORE   = "";    //15
            NormalizedExpiry       = "";    //16
            NormalizedPRODDATE     = "";    //17
            PCN                    = "";    //18
            PRODDATE               = "";    //19
            Product                = "";    //20
            Quantity               = "";    //21
            Reference              = "";    //22
            NaSIdParamName         = "";
            Serial                 = "";    //23
            SSCC                   = "";    //24
            SubType                = "";    //25
            SymbologyID            = "";
            Type                   = "";    //26
            UDI                    = "";
            UoM                    = "";    //28
            UPN                    = "";
            VARCOUNT               = "";    //29
            VARIANT                = "";    //30
            Errors                 = "";
            try
            {
                InformationSet result = new InformationSet();
                Core.Functions client = new Core.Functions();
                result                 = client.GetFullInformationSet(Barcode);
                executeResult          = result.executeResult;
                Type                   = result.Type;
                SubType                = result.SubType;
                ACL                    = result.ACL;
                ADDITIONALID           = result.ADDITIONALID;
                BESTBEFORE             = result.BESTBEFORE;
                CIP                    = result.CIP;                  //4
                Company                = "";                          // result.Company;  //5 Obsolete
                containsOrMayContainId = result.ContainsOrMayContainId;
                CONTENT                = result.CONTENT;              //6
                COUNT                  = result.COUNT;                //7
                EAN                    = result.EAN;
                Expiry                 = result.Expiry;               //8
                Family                 = result.Family;               //9
                GTIN                   = result.GTIN;                 //10
                LIC                    = result.LIC;                  //11
                Lot                    = result.Lot;                  //12
                LPP                    = result.LPP;                  //13
                NaS7                   = result.NaS7;                 //14
                NormalizedBESTBEFORE   = result.NormalizedBESTBEFORE; //15
                NormalizedExpiry       = result.NormalizedExpiry;     //16
                NormalizedPRODDATE     = result.NormalizedPRODDATE;   //17
                PCN                    = result.PCN;                  //18
                PRODDATE               = result.PRODDATE;             //19
                Product                = "";                          // result.Product;  //20 //Obsolete
                Quantity               = result.Quantity;             //21
                Reference              = result.Reference;            //22
                NaSIdParamName         = result.NaSIdParamName;
                Serial                 = result.Serial;               //23
                SSCC                   = result.SSCC;                 //24
                SymbologyID            = result.SymbologyID;
                UDI                    = result.UDI;
                UoM                    = result.UoM;      //28
                UPN                    = result.UPN;
                VARCOUNT               = result.VARCOUNT; //29
                VARIANT                = result.VARIANT;  //30
                Errors                 = result.AdditionalInformation;
            }
            catch (Exception e)
            {
                Errors = Errors + e.Message;
            }
            return(executeResult);
        }
Exemplo n.º 16
0
    public static bool SaveGameFile(string json, InformationSet informationSet)
    {
        string path = GetPathFromInformationSet(informationSet);

        return(SaveFile(json, path));
    }
Exemplo n.º 17
0
 public RuleBasedNode(string playerName, string charName, InformationSet infoSet) : base(playerName, charName, infoSet)
 {
 }
Exemplo n.º 18
0
        /// <summary>
        /// Recursively implements the Counterfactual Regret Minimization algorithm
        ///
        /// </summary>
        /// <param name="cards">Hand cards of player 1 and 2</param>
        /// <param name="actions">Action History</param>
        /// <param name="probability0">Accumulated action probability of player 1</param>
        /// <param name="probability1">Accumulated action probability of player 2</param>
        /// <returns></returns>
        private float CalculateCounterFactualRegret(int[] cards, List <GameAction> actions, float probability0, float probability1)
        {
            int plays    = actions.Count;
            int player   = plays % 2;
            int opponent = 1 - player;

            if (plays > 1)
            {
                bool isLastActionPass       = (actions.Last() == GameAction.Pass);
                bool isSecondLastActionPass = (actions[actions.Count - 2] == GameAction.Pass);
                bool isPlayerCardHigher     = cards[player] > cards[opponent];
                bool isDoubleBet            = !isLastActionPass && !isSecondLastActionPass;
                bool isDoublePass           = isLastActionPass && isSecondLastActionPass;

                if (isLastActionPass)
                {
                    if (isDoublePass)
                    {
                        return(isPlayerCardHigher ? 1 : -1);
                    }
                    else
                    {
                        return(1);
                    }
                }
                else if (isDoubleBet)
                {
                    return(isPlayerCardHigher ? 2 : -2);
                }
            }

            var infoSet = new InformationSet <GameAction>()
            {
                CardBucket    = cards[player],
                ActionHistory = actions
            };

            RegretGameNode <GameAction> node = null;
            var hash = infoSet.GetHashCode();

            if (!GameNodes.TryGetValue(hash, out node))
            {
                node         = new RegretGameNode <GameAction>(Settings.NumberOfActions);
                node.InfoSet = infoSet;
                GameNodes.Add(hash, node);
            }

            var strategy  = node.calculateStrategy(player == 0 ? probability0 : probability1);
            var utilities = new List <float>(Settings.NumberOfActions)
            {
                0, 0
            };
            float nodeUtility = 0;

            for (int i = 0; i < Settings.NumberOfActions; i++)
            {
                var nextAction  = (i == 0) ? GameAction.Pass : GameAction.Bet;
                var nextHistory = new List <GameAction>(actions);
                nextHistory.Add(nextAction);

                utilities[i] = player == 0
                    ? -CalculateCounterFactualRegret(cards, nextHistory, probability0 * strategy[i], probability1)
                   : -CalculateCounterFactualRegret(cards, nextHistory, probability0, probability1 * strategy[i]);

                nodeUtility += strategy[i] * utilities[i];
            }

            for (int i = 0; i < Settings.NumberOfActions; i++)
            {
                float regret = utilities[i] - nodeUtility;
                node.RegretSum[i] += (player == 0 ? probability1 : probability0) * regret;
            }

            return(nodeUtility);
        }
Exemplo n.º 19
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
                }));
            }
        }
        /// <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);
        }