protected override Play GetDecision()
        {
            Dictionary <string, decimal> networkInputs = new Dictionary <string, decimal>();

            networkInputs.Add("ProbCardsWin", infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage));
            networkInputs.Add("ProbCardsWinOpponentWin", (1 - infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage)) / (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) - 1));

            decimal currentGameStage = (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte));
            decimal preFlop;

            if (currentGameStage == 0)
            {
                preFlop = 1;
            }
            else
            {
                preFlop = 0;
            }

            networkInputs.Add("PreFlop", preFlop);

            decimal currentRoundBetAmount = currentDecision.Cache.getPlayerCurrentRoundBetAmount(currentDecision.PlayerId);
            decimal minCallAmount         = infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal);
            decimal scaledCallAmount      = (minCallAmount - currentRoundBetAmount) / currentDecision.Cache.MaxStack;

            if (scaledCallAmount > 1)
            {
                scaledCallAmount = 1;
            }
            else if (scaledCallAmount < 0)
            {
                scaledCallAmount = 0;
            }

            networkInputs.Add("ScaledCallAmount", scaledCallAmount);

            decimal raiseToCallAmount  = infoStore.GetInfoValue(InfoType.PAP_RaiseToCallAmount_Amount);
            decimal raiseToStealAmount = infoStore.GetInfoValue(InfoType.PAP_RaiseToStealAmount_Amount);

            decimal scaledRaiseToStealAmount = raiseToStealAmount / currentDecision.Cache.MaxStack;

            if (scaledRaiseToStealAmount > 1)
            {
                scaledRaiseToStealAmount = 1;
            }

            networkInputs.Add("ScaledRaiseToStealAmount", scaledRaiseToStealAmount);

            decimal raiseRatio     = 0;
            decimal totalNumRaises = infoStore.GetInfoValue(InfoType.BP_TotalNumRaises_Byte);
            decimal totalNumCalls  = infoStore.GetInfoValue(InfoType.BP_TotalNumCalls_Byte);
            decimal totalNumChecks = infoStore.GetInfoValue(InfoType.BP_TotalNumChecks_Byte);

            if (totalNumRaises + totalNumCalls + totalNumChecks > 0)
            {
                raiseRatio = totalNumRaises / (totalNumRaises + totalNumCalls + totalNumChecks);
            }

            networkInputs.Add("RaiseRatio", raiseRatio);
            networkInputs.Add("ImmediatePotOdds", infoStore.GetInfoValue(InfoType.BP_ImmediatePotOdds_Double));
            networkInputs.Add("UnactedRatio", infoStore.GetInfoValue(InfoType.GP_NumUnactedPlayers_Byte) / infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte));
            networkInputs.Add("ActiveRatio", infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) / infoStore.GetInfoValue(InfoType.GP_NumTableSeats_Byte));

            networkInputs.Add("ProbRaiseToBotCheck", infoStore.GetInfoValue(InfoType.PAP_RaiseToBotCheck_Prob));
            networkInputs.Add("ProbRaiseToBotCall", infoStore.GetInfoValue(InfoType.PAP_RaiseToBotCall_Prob));
            networkInputs.Add("ProbFoldToBotCall", infoStore.GetInfoValue(InfoType.PAP_FoldToBotCall_Prob));
            networkInputs.Add("ProbRaiseToStealSuccess", infoStore.GetInfoValue(InfoType.PAP_RaiseToStealSuccess_Prob));

            networkInputs.Add("AvgScaledOppRaiseFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppRaiseFreq_Double));
            networkInputs.Add("AvgScaledOppCallFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppCallFreq_Double));
            networkInputs.Add("AvgScaledOppPreFlopPlayFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppPreFlopPlayFreq_Double));

            //probCardsWin, probCardsWinOpponentWin, preFlop, scaledCallAmount, scaledRaiseToStealAmount,
            //raiseRatio, immediatePotOdds, unactedRatio, activeRatio, probRaiseToBotCheck, probRaiseToBotCall,
            //probFoldToBotCall, probRaiseToStealSuccess, avgScaledOppRaiseFreq, avgScaledOppCallFreq,
            //avgScaledOppPreFlopPlayFreq;

            NNDataSource aiDecisionData = new NNDataSource(networkInputs.Values.ToArray(), NeuralAINNModelV3.Input_Neurons);

            //decisionLogStr = aiDecisionData.ToString();

            //Get the network outputs
            double[] networkInputsArray = null;
            aiDecisionData.returnInput(ref networkInputsArray);
            double[] networkOutput = getPlayerNetworkPrediction(currentDecision.AiConfigStr, networkInputsArray);

            NeuralAiDecision decision = new NeuralAiDecision(networkOutput[0], networkOutput[1], networkOutput[2], networkOutput[3], networkOutput[4]);
            //Debug.Print("AI Decision - [{0}], [{1}], [{2}], [{3}], [{4}]", networkOutput[0], networkOutput[1], networkOutput[2], networkOutput[3], networkOutput[4]);

            decimal playerRemainingStackAmount = currentDecision.Cache.getPlayerStack(currentDecision.PlayerId);

            if (decision.BotAction == 0)
            {
                return(new Play(PokerAction.Fold, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else if (decision.BotAction == 1)
            {
                return(new Play(PokerAction.Call, currentDecision.Cache.getMinimumPlayAmount() - currentRoundBetAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else if (decision.BotAction == 2)
            {
                return(new Play(PokerAction.Raise, (decimal)raiseToCallAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else if (decision.BotAction == 3)
            {
                return(new Play(PokerAction.Raise, (decimal)raiseToStealAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else if (decision.BotAction == 4)
            {
                return(new Play(PokerAction.Raise, playerRemainingStackAmount + currentRoundBetAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else
            {
                throw new Exception("Something has gone wery wong!");
            }

            //return new Play(PokerAction.NoAction, 0, 0, 0, playerId, decisionLogStr, aiDecisionData.AIDecisionStringType);
        }
        protected override Play GetDecision()
        {
            //Some values need to be moved the top because they are used in multiple places
            decimal currentRoundMinPlayCallAmount = currentDecision.Cache.getMinimumPlayAmount();
            decimal additionalCallAmount          = currentRoundMinPlayCallAmount - infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);
            decimal currentPotAmount     = infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal);
            decimal remainingPlayerStack = currentDecision.Cache.getPlayerStack(currentDecision.PlayerId);

            //Current Round Actions
            List <PokerAction> currentRoundPlayerActions = (from current in currentDecision.Cache.getPlayerCurrentRoundActions(currentDecision.PlayerId)
                                                            where current == PokerAction.Check ||
                                                            current == PokerAction.Call ||
                                                            current == PokerAction.Raise
                                                            select current).ToList();

            #region NewRaiseAmounts
            decimal raiseToCallAmountNew, raiseToStealAmountNew;

            //If we are preflop we calculate the raise amounts in a slightly more fixed fashion
            if (currentDecision.Cache.getBettingRound() == 0)
            {
                double randomNumber = randomGen.NextDouble();

                //If the pot is unraised
                if (infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) == currentDecision.Cache.BigBlind)
                {
                    if (randomNumber > 0.8)
                    {
                        raiseToCallAmountNew = 4m * currentDecision.Cache.BigBlind;
                    }
                    else if (randomNumber > 0.4)
                    {
                        raiseToCallAmountNew = 3.5m * currentDecision.Cache.BigBlind;
                    }
                    else
                    {
                        raiseToCallAmountNew = 3.0m * currentDecision.Cache.BigBlind;
                    }
                }
                else
                {
                    decimal currentRoundLastRaiseAmount = infoStore.GetInfoValue(InfoType.BP_LastAdditionalRaiseAmount);
                    decimal additionalNewRaiseAmount;

                    if (randomNumber > 0.9)
                    {
                        additionalNewRaiseAmount = 2 * currentRoundLastRaiseAmount;
                    }
                    else if (randomNumber > 0.45)
                    {
                        additionalNewRaiseAmount = 1.5m * currentRoundLastRaiseAmount;
                    }
                    else
                    {
                        additionalNewRaiseAmount = 1 * currentRoundLastRaiseAmount;
                    }

                    raiseToCallAmountNew = additionalNewRaiseAmount + infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal);
                }

                raiseToStealAmountNew = 1.5m * raiseToCallAmountNew;

                //We will only scale the raiseToCallAmount if it is not a bigblind multiple
                if (((int)(raiseToCallAmountNew / currentDecision.Cache.BigBlind)) * currentDecision.Cache.BigBlind != raiseToCallAmountNew)
                {
                    //Round the raiseToCallAmount
                    decimal raiseToCallAmountBlindMultiple = raiseToCallAmountNew / currentDecision.Cache.LittleBlind;
                    raiseToCallAmountNew = Math.Round(raiseToCallAmountBlindMultiple, 0, MidpointRounding.AwayFromZero) * currentDecision.Cache.LittleBlind;
                }

                //Round the raiseToStealAmount
                decimal raiseToStealAmountBlindMultiple = raiseToStealAmountNew / currentDecision.Cache.LittleBlind;
                raiseToStealAmountNew = Math.Round(raiseToStealAmountBlindMultiple, 0, MidpointRounding.AwayFromZero) * currentDecision.Cache.LittleBlind;
            }
            else
            {
                raiseToCallAmountNew  = (decimal)infoStore.GetInfoValue(InfoType.WR_RaiseToCallAmount);
                raiseToStealAmountNew = (decimal)infoStore.GetInfoValue(InfoType.WR_RaiseToStealAmount);
            }

            //Some raise validation
            //Validate we can actually raise the selected amounts
            decimal maximumRaiseAmount = remainingPlayerStack + (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);

            //if (raiseToCallAmount > maximumRaiseAmount) raiseToCallAmount = maximumRaiseAmount;
            //if (raiseToStealAmount > maximumRaiseAmount) raiseToStealAmount = maximumRaiseAmount;

            //Check for a big raise amount which would be best as going all in
            if (raiseToCallAmountNew > 0.8m * maximumRaiseAmount)
            {
                raiseToCallAmountNew = maximumRaiseAmount;
            }
            if (raiseToStealAmountNew > 0.8m * maximumRaiseAmount)
            {
                raiseToStealAmountNew = maximumRaiseAmount;
            }

            //If we have already raised twice this round all amounts are set to all in
            if (currentRoundPlayerActions.Count(entry => entry == PokerAction.Raise) >= 2)
            {
                raiseToCallAmountNew  = maximumRaiseAmount;
                raiseToStealAmountNew = maximumRaiseAmount;
            }
            #endregion RaiseAmounts

            #region EV and CheckPossibilitiy

            decimal probWin = 1.0m - infoStore.GetInfoValue(InfoType.WR_ProbOpponentHasBetterWRFIXED);
            decimal bigBlindEVScaleAmount = 20;

            #region CallEV
            //The following EV assumes we can actually call the additionaCallAmount
            //We could cap the additionalCallAmount but we would then also not be able to win the total pot amount ;( again a minor error
            decimal actualCallCheckEV = (currentPotAmount * probWin) - ((1.0m - probWin) * additionalCallAmount);
            decimal scaledCallCheckEV = (actualCallCheckEV / currentDecision.Cache.BigBlind) / bigBlindEVScaleAmount;
            //if (scaledCallCheckEV > 1) networkInputs.Add("ScaledCallCheckEV", 1);
            //else if (scaledCallCheckEV < -1) networkInputs.Add("ScaledCallCheckEV", -1);
            //else networkInputs.Add("ScaledCallCheckEV", scaledCallCheckEV);
            #endregion

            //We can raise if we have more stack than the current additional call amount
            //If we can't raise the raiseEV's get set to the callEV (which may be negative)
            bool raisePossible = (remainingPlayerStack > additionalCallAmount);

            decimal probAllOpponentFoldRaiseToCall  = infoStore.GetInfoValue(InfoType.WR_RaiseToCallStealSuccessProb);
            decimal probAllOpponentFoldRaiseToSteal = (currentDecision.Cache.getBettingRound() == 0 ? 0 : infoStore.GetInfoValue(InfoType.WR_RaiseToStealSuccessProb));
            long[]  activePlayerIds = currentDecision.Cache.getActivePlayerIds();

            #region RaiseToCallEV
            decimal scaledRaiseToCallEV = 0;
            if (raisePossible)
            {
                decimal potAmountAssumingAllCall = currentPotAmount;

                //If we are preflop then it would be unrealistic to expect everyone to call
                //For Call we assume that if there are 8 players to act after the raise we get 2 calls
                //If there are 4 players to act after the raise we get 1 call
                //This is the same as dividing the number of activePlayers by 4
                if (currentDecision.Cache.getBettingRound() == 0)
                {
                    //We assume anyone who has already called or raised will call again
                    long[] calledRaisedPlayerIds = (from current in currentDecision.Cache.getAllHandActions()
                                                    where current.actionType == PokerAction.Call || current.actionType == PokerAction.Raise
                                                    where current.playerId != currentDecision.PlayerId
                                                    select current.playerId).ToArray();

                    for (int i = 0; i < calledRaisedPlayerIds.Length; i++)
                    {
                        potAmountAssumingAllCall += raiseToCallAmountNew - currentDecision.Cache.getPlayerCurrentRoundBetAmount(calledRaisedPlayerIds[i]);
                    }

                    potAmountAssumingAllCall += ((decimal)(activePlayerIds.Length - 1 - calledRaisedPlayerIds.Length) / 4) * raiseToCallAmountNew;
                }
                else
                {
                    for (int i = 0; i < activePlayerIds.Length; i++)
                    {
                        if (activePlayerIds[i] != currentDecision.PlayerId)
                        {
                            potAmountAssumingAllCall += raiseToCallAmountNew - currentDecision.Cache.getPlayerCurrentRoundBetAmount(activePlayerIds[i]);
                        }
                    }
                }

                decimal extraDollarRiskAmount = raiseToCallAmountNew - infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);

                decimal actualRaiseToCallEV = (currentPotAmount * probAllOpponentFoldRaiseToCall) + (probWin * potAmountAssumingAllCall * (1.0m - probAllOpponentFoldRaiseToCall)) - ((1.0m - probWin) * extraDollarRiskAmount * (1.0m - probAllOpponentFoldRaiseToCall));
                scaledRaiseToCallEV = (actualRaiseToCallEV / currentDecision.Cache.BigBlind) / bigBlindEVScaleAmount;
                //if (scaledRaiseToCallEV > 1) networkInputs.Add("ScaledRaiseToCallEV", 1);
                //else if (scaledRaiseToCallEV < -1) networkInputs.Add("ScaledRaiseToCallEV", -1);
                //else networkInputs.Add("ScaledRaiseToCallEV", scaledRaiseToCallEV);
            }
            #endregion

            #region RaiseToStealEV
            decimal scaledRaiseToStealEV = 0;
            if (raisePossible)
            {
                decimal potAmountAssumingAllCall = currentPotAmount;

                //If we are preflop then it would be unrealistic to expect everyone to call
                //For steal we assume that if there are 8 players to act after the raise we get 1 call
                //If there are 4 players to act after the raise we get 0.5 call
                //This is the same as dividing the number of activePlayers by 8
                if (currentDecision.Cache.getBettingRound() == 0)
                {
                    //We assume anyone who has already raised will call again
                    long[] raisedPlayerIds = (from current in currentDecision.Cache.getAllHandActions()
                                              where current.actionType == PokerAction.Raise
                                              where current.playerId != currentDecision.PlayerId
                                              select current.playerId).ToArray();

                    for (int i = 0; i < raisedPlayerIds.Length; i++)
                    {
                        potAmountAssumingAllCall += raiseToStealAmountNew - currentDecision.Cache.getPlayerCurrentRoundBetAmount(raisedPlayerIds[i]);
                    }

                    potAmountAssumingAllCall += ((decimal)(activePlayerIds.Length - 1 - raisedPlayerIds.Length) / 8) * raiseToStealAmountNew;
                }
                else
                {
                    for (int i = 0; i < activePlayerIds.Length; i++)
                    {
                        if (activePlayerIds[i] != currentDecision.PlayerId)
                        {
                            potAmountAssumingAllCall += raiseToStealAmountNew - currentDecision.Cache.getPlayerCurrentRoundBetAmount(activePlayerIds[i]);
                        }
                    }
                }

                decimal extraDollarRiskAmount = raiseToStealAmountNew - infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);

                decimal actualRaiseToStealEV = (currentPotAmount * probAllOpponentFoldRaiseToSteal) + (probWin * potAmountAssumingAllCall * (1.0m - probAllOpponentFoldRaiseToSteal)) - ((1.0m - probWin) * extraDollarRiskAmount * (1.0m - probAllOpponentFoldRaiseToSteal));
                scaledRaiseToStealEV = (actualRaiseToStealEV / currentDecision.Cache.BigBlind) / bigBlindEVScaleAmount;
                //if (scaledRaiseToStealEV > 1) networkInputs.Add("ScaledRaiseToStealEV", 1);
                //else if (scaledRaiseToStealEV < -1) networkInputs.Add("ScaledRaiseToStealEV", -1);
                //else networkInputs.Add("ScaledRaiseToStealEV", scaledRaiseToStealEV);
            }
            #endregion
            #endregion

            Dictionary <string, decimal> networkInputs = new Dictionary <string, decimal>();

            #region neuralInputs

            //Card info types
            //networkInputs.Add("HoleCardsAAPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsAAPair_Bool));
            //networkInputs.Add("HoleCardsKKPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsKKPair_Bool));
            //networkInputs.Add("HoleCardsAKPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsAK_Bool));
            //networkInputs.Add("HoleCardsOtherHighPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsOtherHighPair_Bool));
            //networkInputs.Add("HoleCardsOtherLowPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsOtherLowPair_Bool));
            //networkInputs.Add("HoleCardsTroubleHand", infoStore.GetInfoValue(InfoType.CP_HoleCardsTroubleHand_Bool));
            //networkInputs.Add("HoleCardsSuited", infoStore.GetInfoValue(InfoType.CP_HoleCardsSuited_Bool));

            networkInputs.Add("HoleCardPlayability", infoStore.GetInfoValue(InfoType.CP_HoleCardsMatchedPlayability));

            /*
             * networkInputs.Add("HoleCardsMidConnector", infoStore.GetInfoValue(InfoType.CP_HoleCardsMidConnector_Bool));
             * networkInputs.Add("HoleCardsLowConnector", infoStore.GetInfoValue(InfoType.CP_HoleCardsLowConnector_Bool));
             *
             * networkInputs.Add("HoleCardsFlushDraw", infoStore.GetInfoValue(InfoType.CP_HoleCardsFlushDraw_Bool));
             * networkInputs.Add("HoleCardsOuterStraightDraw", infoStore.GetInfoValue(InfoType.CP_HoleCardsOuterStraightDrawWithHC_Bool));
             * networkInputs.Add("HoleCardsInnerStraightDraw", infoStore.GetInfoValue(InfoType.CP_HoleCardsInnerStraightDrawWithHC_Bool));
             * networkInputs.Add("HoleCardsTopOrTwoPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsTopOrTwoPair_Bool));
             * networkInputs.Add("HoleCardsAorKinHand", infoStore.GetInfoValue(InfoType.CP_HoleCardsAOrKInHand_Bool));
             * networkInputs.Add("HoleCards3KindOrBetter", infoStore.GetInfoValue(InfoType.CP_HoleCards3KindOrBetterMadeWithHC_Bool));
             * networkInputs.Add("AonBoard", infoStore.GetInfoValue(InfoType.CP_AOnBoard_Bool));
             * networkInputs.Add("KonBoard", infoStore.GetInfoValue(InfoType.CP_KOnBoard_Bool));
             * networkInputs.Add("AKQRatio", infoStore.GetInfoValue(InfoType.CP_AKQToBoardRatio_Real));
             *
             * networkInputs.Add("FlushPossible", infoStore.GetInfoValue(InfoType.CP_FlushPossible_Bool));
             * networkInputs.Add("StraightPossible", infoStore.GetInfoValue(InfoType.CP_StraightPossible_Bool));
             * networkInputs.Add("TableStraightDraw", infoStore.GetInfoValue(InfoType.CP_TableStraightDraw_Bool));
             * networkInputs.Add("TableFlushDraw", infoStore.GetInfoValue(InfoType.CP_TableFlushDraw_Bool));
             */

            //DealtInRatio, ActiveRatio and UnactedRatio
            //networkInputs.Add("DealtInRatio", infoStore.GetInfoValue(InfoType.GP_NumPlayersDealtIn_Byte) / infoStore.GetInfoValue(InfoType.GP_NumTableSeats_Byte));
            //networkInputs.Add("ActiveRatio", infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) / infoStore.GetInfoValue(InfoType.GP_NumPlayersDealtIn_Byte));
            //networkInputs.Add("UnactedRatio", infoStore.GetInfoValue(InfoType.GP_NumUnactedPlayers_Byte) / infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte));

            #region gameStage

            decimal gameStagePreFlop = 0;
            decimal gameStageRiver   = 0;

            if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 0)
            {
                gameStagePreFlop = 1;
            }
            else if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 3)
            {
                gameStageRiver = 1;
            }

            networkInputs.Add("GameStagePreFlop", gameStagePreFlop);
            networkInputs.Add("GameStageRiver", gameStageRiver);

            #endregion gameStage

            #region dealerDistance

            decimal dealerDistance     = ((infoStore.GetInfoValue(InfoType.GP_DealerDistance_Byte) - 1) / (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) - 1));
            decimal tablePositionEarly = 0;
            decimal tablePositionMid   = 0;
            decimal tablePositionLate  = 0;

            if (dealerDistance < (1.0m / 3.0m) && dealerDistance >= 0)
            {
                tablePositionEarly = 1;
            }
            else if (dealerDistance < (2.0m / 3.0m) && dealerDistance >= 0)
            {
                tablePositionMid = 1;
            }
            else if (dealerDistance <= 1.0m && dealerDistance >= 0)
            {
                tablePositionLate = 1;
            }
            else
            {
                throw new Exception("Dealer distance must be between 0 and 1.");
            }

            networkInputs.Add("TablePositionEarly", tablePositionEarly);
            //networkInputs.Add("TablePositionMid", tablePositionMid);
            networkInputs.Add("TablePositionLate", tablePositionLate);


            networkInputs.Add("FirstToAct", (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) == infoStore.GetInfoValue(InfoType.GP_NumUnactedPlayers_Byte) ? 1 : 0));
            networkInputs.Add("LastToAct", (infoStore.GetInfoValue(InfoType.GP_NumUnactedPlayers_Byte) == 1 ? 1 : 0));

            #endregion dealerDistance

            networkInputs.Add("ImmediatePotOdds", infoStore.GetInfoValue(InfoType.BP_ImmediatePotOdds_Double));
            //networkInputs.Add("ImpliedPotOdds", 0); //(infoStore.GetInfoValue(InfoType.IO_ImpliedPotOdds_Double))

            decimal potCommitted = 0;
            if (infoStore.GetInfoValue(InfoType.BP_PlayerMoneyInPot_Decimal) > infoStore.GetInfoValue(InfoType.BP_PlayerHandStartingStackAmount_Decimal) / 2)
            {
                potCommitted = 1;
            }

            networkInputs.Add("PotCommitted", potCommitted);
            networkInputs.Add("PotRatio", infoStore.GetInfoValue(InfoType.BP_PlayerMoneyInPot_Decimal) / infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal));

            #region raise & check ratio

            decimal raiseRatio     = 0;
            decimal checkRatio     = 0;
            decimal totalNumRaises = infoStore.GetInfoValue(InfoType.BP_TotalNumRaises_Byte);
            decimal totalNumCalls  = infoStore.GetInfoValue(InfoType.BP_TotalNumCalls_Byte);
            decimal totalNumChecks = infoStore.GetInfoValue(InfoType.BP_TotalNumChecks_Byte);
            if (totalNumRaises + totalNumCalls + totalNumChecks > 0)
            {
                raiseRatio = totalNumRaises / (totalNumRaises + totalNumCalls + totalNumChecks);
                checkRatio = totalNumChecks / (totalNumRaises + totalNumCalls + totalNumChecks);
            }

            networkInputs.Add("RaiseRatio", raiseRatio);
            networkInputs.Add("CheckRatio", checkRatio);

            #endregion raise & check ratio

            bool checkPossible = (infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) == infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal));
            networkInputs.Add("CheckPossible", Convert.ToDecimal(checkPossible));

            /*
             * double betsToCall = infoStore.GetInfoValue(InfoType.BP_BetsToCall_Byte);
             * double betsToCall0 = 0;
             * double betsToCall1 = 0;
             * double betsToCall2Greater = 0;
             *
             * if (betsToCall >= 2)
             *  betsToCall2Greater = 1;
             * else if (betsToCall == 1)
             *  betsToCall1 = 1;
             * else if (betsToCall == 0)
             *  betsToCall0 = 1;
             * else
             *  throw new Exception("This is impossible!!");
             *
             * networkInputs.Add("BetsToCall0", betsToCall0);
             * networkInputs.Add("BetsToCall1", betsToCall1);
             * networkInputs.Add("BetsToCall2Greater", betsToCall2Greater);
             *
             * double betsLastRound1Greater = 0;
             * if (infoStore.GetInfoValue(InfoType.BP_LastRoundBetsToCall_Byte) > 0)
             *  betsLastRound1Greater = 1;
             *
             * networkInputs.Add("BetsLastRound1Greater", betsLastRound1Greater);
             */

            /*
             * double raisedLastRound = 0;
             * if (infoStore.GetInfoValue(InfoType.BP_RaisedLastRound_Bool) == 1)
             *  raisedLastRound = 1;
             *
             * networkInputs.Add("RaisedLastRound", raisedLastRound);
             *
             * double lastActionRaise = 0;
             * if (infoStore.GetInfoValue(InfoType.BP_PlayerLastAction_Short) == 9)
             *  lastActionRaise = 1;
             *
             * networkInputs.Add("LastActionRaise", lastActionRaise);
             */

            //call and raise amounts
            networkInputs.Add("ScaledCallAmount", infoStore.GetInfoValue(InfoType.BP_ScaledCallAmount_Double));
            networkInputs.Add("CallAmountBB", ((decimal)infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) - (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal)) == currentDecision.Cache.BigBlind ? 1 : 0);
            //networkInputs.Add("ScaledRaiseToCallAmount", BetsProvider.ScaleRaiseAmount(cache.BigBlind, infoStore.GetInfoValue(InfoType.PAP_RaiseToCallAmount_Amount) - infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal), infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal)));
            //networkInputs.Add("ScaledRaiseToStealAmount", BetsProvider.ScaleRaiseAmount(cache.BigBlind, infoStore.GetInfoValue(InfoType.PAP_RaiseToStealAmount_Amount) - infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal), infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal)));

            //WinRatio
            //networkInputs.Add("WRProbCardsWin", infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage));
            //networkInputs.Add("WRProbCardsOpponentWin", infoStore.GetInfoValue(InfoType.WR_CardsOnlyOpponentWinPercentage));
            networkInputs.Add("WRProbBeat", infoStore.GetInfoValue(InfoType.WR_ProbOpponentHasBetterWRFIXED));
            //networkInputs.Add("WRLastRoundWRChange", infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentageLastRoundChange));
            networkInputs.Add("WRHandIndexTop10Hands", (infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentageIndex) < 0.01m ? 1 : 0));

            //networkInputs.Add("ProbRaiseToBotCheck",infoStore.GetInfoValue(InfoType.PAP_RaiseToBotCheck_Prob));
            //networkInputs.Add("ProbRaiseToBotCall",infoStore.GetInfoValue(InfoType.PAP_RaiseToBotCall_Prob));
            //networkInputs.Add("ProbFoldToBotCall",infoStore.GetInfoValue(InfoType.PAP_FoldToBotCall_Prob));
            networkInputs.Add("ProbRaiseToStealSuccess", infoStore.GetInfoValue(InfoType.PAP_RaiseToStealSuccess_Prob));

            ////////////////////////////////////////////////////////////////////
            ///////// MASSIVE HACK BEGIN ///////////////////////////////////////
            ////////////////////////////////////////////////////////////////////
            //We need to disable the aggression information for fixed marc and ailwyn bots
            //if (currentDecision.RequiredInfoTypeUpdateConfigs.ContainsKey(InfoType.CP_HoleCardsMatchedPlayability))
            //{
            //    string holeCardsMatchedPlayability = currentDecision.RequiredInfoTypeUpdateConfigs[InfoType.CP_HoleCardsMatchedPlayability];
            //    if (aggressionInfoDisbleList.Contains(holeCardsMatchedPlayability))
            //    {
            //        networkInputs.Add("AvgScaledOppPreFlopPlayFreq", AggressionProvider.DEFAULT_pFREQ_PREFLOP);
            //        if (gameStagePreFlop == 1)
            //        {
            //            networkInputs.Add("AvgScaledOppRaiseFreq", AggressionProvider.DEFAULT_rFREQ_PREFLOP);
            //            networkInputs.Add("AvgScaledOppCallFreq", AggressionProvider.DEFAULT_cFREQ_PREFLOP);
            //        }
            //        else
            //        {
            //            networkInputs.Add("AvgScaledOppRaiseFreq", AggressionProvider.DEFAULT_rFREQ_POSTFLOP);
            //            networkInputs.Add("AvgScaledOppCallFreq", AggressionProvider.DEFAULT_cFREQ_POSTFLOP);
            //        }
            //    }
            //    else
            //    {
            //        networkInputs.Add("AvgScaledOppRaiseFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppRaiseFreq_Double));
            //        networkInputs.Add("AvgScaledOppCallFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppCallFreq_Double));
            //        networkInputs.Add("AvgScaledOppPreFlopPlayFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppPreFlopPlayFreq_Double));
            //    }
            //}
            //else
            //{
            //    networkInputs.Add("AvgScaledOppRaiseFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppRaiseFreq_Double));
            //    networkInputs.Add("AvgScaledOppCallFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppCallFreq_Double));
            //    networkInputs.Add("AvgScaledOppPreFlopPlayFreq", infoStore.GetInfoValue(InfoType.AP_AvgScaledOppPreFlopPlayFreq_Double));
            //}

            //All of V4 players get aggression defaults
            networkInputs.Add("AvgScaledOppPreFlopPlayFreq", AggressionProvider.DEFAULT_pFREQ_PREFLOP);
            if (gameStagePreFlop == 1)
            {
                networkInputs.Add("AvgScaledOppRaiseFreq", AggressionProvider.DEFAULT_rFREQ_PREFLOP);
                networkInputs.Add("AvgScaledOppCallFreq", AggressionProvider.DEFAULT_cFREQ_PREFLOP);
            }
            else
            {
                networkInputs.Add("AvgScaledOppRaiseFreq", AggressionProvider.DEFAULT_rFREQ_POSTFLOP);
                networkInputs.Add("AvgScaledOppCallFreq", AggressionProvider.DEFAULT_cFREQ_POSTFLOP);
            }
            ////////////////////////////////////////////////////////////////////
            ///////// MASSIVE HACK END /////////////////////////////////////////
            ////////////////////////////////////////////////////////////////////

            #endregion neuralInputs

            NNDataSource aiDecisionData = new NNDataSource(networkInputs.Values.ToArray(), NeuralAINNModelV4.Input_Neurons);

            //decisionLogStr = aiDecisionData.ToString();

            //decimal playerRemainingStackAmount = currentDecision.Cache.getPlayerStack(currentDecision.PlayerId);
            decimal raiseToCallAmount  = (decimal)infoStore.GetInfoValue(InfoType.PAP_RaiseToCallAmount_Amount);
            decimal raiseToStealAmount = (decimal)infoStore.GetInfoValue(InfoType.PAP_RaiseToStealAmount_Amount);

            if (true)
            {
                //Get the network outputs
                double[] networkInputsArray = null;
                aiDecisionData.returnInput(ref networkInputsArray);
                double[] networkOutput = getPlayerNetworkPrediction(currentDecision.AiConfigStr, networkInputsArray);
                //Debug.Print(Math.Round(networkOutput[0], 2) + ", " + Math.Round(networkOutput[1], 2) + ", " + Math.Round(networkOutput[2], 2) + ", " + Math.Round(networkOutput[3], 2) + ", " + Math.Round(networkOutput[4], 2));

                NeuralAiDecision decision;
                //We use the network outputs and do raises amounts slightly differently for the current genetic distribution
                if (currentDecision.RequiredInfoTypeUpdateConfigs.ContainsKey(InfoType.IS_CURRENT_GENETIC))
                {
                    //Blank the non possible actions
                    if (checkPossible)
                    {
                        //Blank fold
                        networkOutput[0] = 0;
                        //Blank call
                        networkOutput[2] = 0;
                    }
                    else
                    {
                        //Blank check
                        networkOutput[1] = 0;
                    }

                    //Get the bot decision
                    decision = new NeuralAiDecision(networkOutput, !Convert.ToBoolean(gameStageRiver) || checkPossible, randomGen.NextDouble(), true);

                    if (decision.BotAction >= 3)
                    {
                        //If we have set a config for PAP_RaiseToCallAmount_Amount then we limit raises per round to three
                        if (currentDecision.Cache.getPlayerCurrentRoundActions(currentDecision.PlayerId).Count(entry => entry == PokerAction.Raise) >= 2)
                        {
                            raiseToCallAmount  = remainingPlayerStack + (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);
                            raiseToStealAmount = raiseToCallAmount;
                        }
                    }
                }
                else
                {
                    //We enable stochastisim on all but the river
                    decision = new NeuralAiDecision(networkOutput, !Convert.ToBoolean(gameStageRiver), randomGen.NextDouble(), false);
                    //Debug.Print("AI Decision - [{0}], [{1}], [{2}], [{3}], [{4}]", networkOutput[0], networkOutput[1], networkOutput[2], networkOutput[3], networkOutput[4]);

                    //Lets put a little double check here for the AI outputs
                    if (checkPossible && decision.BotAction == 0)
                    {
                        decision.BotAction = 1;
                    }
                    else if (!checkPossible && decision.BotAction == 1)
                    {
                        decision.BotAction = 0;
                    }
                }

                //Need to work out WTF is going on!!
#if logging
                lock (locker)
                {
                    if (currentDecision.Cache.getCurrentHandId() == 880)
                    {
                        mem = new System.IO.MemoryStream();
                        bin.Serialize(mem, Encog.Neural.Networks.Structure.NetworkCODEC.NetworkToArray(PlayerNetworkCopy(currentDecision.AiConfigStr)));
                        string networkHash = BitConverter.ToString(md5.ComputeHash(mem.ToArray()));

                        using (System.IO.StreamWriter sw = new System.IO.StreamWriter("aiDecisionsLog.csv", true))
                            sw.WriteLine(currentDecision.Cache.getCurrentHandId() + ", " + currentDecision.Cache.getMostRecentLocalIndex()[1] + ", " + currentDecision.PlayerId + ", " + decision.StochasticDouble + ", ," + aiDecisionData.ToString() + ", ," + Math.Round(networkOutput[0], 2) + ", " + Math.Round(networkOutput[1], 2) + ", " + Math.Round(networkOutput[2], 2) + ", " + Math.Round(networkOutput[3], 2) + ", " + Math.Round(networkOutput[4], 2) + ", " + decision.BotAction + ",," + infoStore.GetInfoValue(InfoType.PAP_RaiseToCallAmount_Amount) + "," + infoStore.GetInfoValue(InfoType.PAP_RaiseToStealAmount_Amount) + ",," + networkHash + ",," + currentDecision.Cache.CurrentHandHash(true));
                        //sw.WriteLine(cache.getCurrentHandId() + ", " + +cache.getMostRecentLocalIndex() + ", " + playerId + ", " + cache.CurrentHandHash(true) + ",," + aiDecisionData.ToString());
                        //sw.WriteLine(currentDecision.Cache.getCurrentHandId() + ", " + currentDecision.Cache.getMostRecentLocalIndex() + ", " + currentDecision.PlayerId + ", " + currentDecision.Cache.CurrentHandHash(true) + ",," + networkInputs["WRProbBeat"]);
                    }
                }
#endif

                //Decision override to prevent horrificly wrong decision
                if (decision.BotAction > 1 && scaledCallCheckEV < -0.2M && scaledRaiseToCallEV < -0.2M && scaledRaiseToStealEV < -0.2M)
                {
                    if (checkPossible)
                    {
                        decision.BotAction = 1;
                    }
                    else
                    {
                        decision.BotAction = 0;
                    }
                }

                if (decision.BotAction == 0)
                {
                    return(new Play(PokerAction.Fold, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 1)
                {
                    return(new Play(PokerAction.Check, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 2)
                {
                    return(new Play(PokerAction.Call, currentDecision.Cache.getMinimumPlayAmount() - (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal), 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 3)
                {
                    return(new Play(PokerAction.Raise, raiseToCallAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 4)
                {
                    return(new Play(PokerAction.Raise, raiseToStealAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                //else if (decision.BotAction == 4)
                //    return new Play(PokerAction.Raise, playerRemainingStackAmount + (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal), 0, cache.getCurrentHandId(), playerId, decisionLogStr, 0);
                else
                {
                    throw new Exception("Something has gone wery wong!");
                }
            }

            //return new Play(PokerAction.Fold, 0, 0, cache.getCurrentHandId(), playerId, decisionLogStr, 0);
        }
        protected override Play GetDecision()
        {
            Dictionary <string, decimal> networkInputs = new Dictionary <string, decimal>();

            //Some values need to be moved the top because they are used in multiple places
            decimal currentRoundMinPlayCallAmount = currentDecision.Cache.getMinimumPlayAmount();
            decimal additionalCallAmount          = currentRoundMinPlayCallAmount - infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);
            decimal currentPotAmount     = infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal);
            decimal remainingPlayerStack = currentDecision.Cache.getPlayerStack(currentDecision.PlayerId);

            //Current Round Actions
            List <PokerAction> currentRoundPlayerActions = (from current in currentDecision.Cache.getPlayerCurrentRoundActions(currentDecision.PlayerId)
                                                            where current == PokerAction.Check ||
                                                            current == PokerAction.Call ||
                                                            current == PokerAction.Raise
                                                            select current).ToList();

            #region RaiseAmounts
            decimal raiseToCallAmountNew, raiseToStealAmountNew;

            //If we are preflop we calculate the raise amounts in a slightly more fixed fashion
            if (currentDecision.Cache.getBettingRound() == 0)
            {
                double randomNumber = randomGen.NextDouble();

                //If the pot is unraised
                if (infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) == currentDecision.Cache.BigBlind)
                {
                    if (randomNumber > 0.8)
                    {
                        raiseToCallAmountNew = 4m * currentDecision.Cache.BigBlind;
                    }
                    else if (randomNumber > 0.4)
                    {
                        raiseToCallAmountNew = 3.5m * currentDecision.Cache.BigBlind;
                    }
                    else
                    {
                        raiseToCallAmountNew = 3.0m * currentDecision.Cache.BigBlind;
                    }
                }
                else
                {
                    decimal currentRoundLastRaiseAmount = infoStore.GetInfoValue(InfoType.BP_LastAdditionalRaiseAmount);
                    decimal additionalNewRaiseAmount;

                    if (randomNumber > 0.9)
                    {
                        additionalNewRaiseAmount = 2 * currentRoundLastRaiseAmount;
                    }
                    else if (randomNumber > 0.45)
                    {
                        additionalNewRaiseAmount = 1.5m * currentRoundLastRaiseAmount;
                    }
                    else
                    {
                        additionalNewRaiseAmount = 1 * currentRoundLastRaiseAmount;
                    }

                    raiseToCallAmountNew = additionalNewRaiseAmount + infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal);
                }

                raiseToStealAmountNew = 1.5m * raiseToCallAmountNew;

                //We will only scale the raiseToCallAmount if it is not a bigblind multiple
                if (((int)(raiseToCallAmountNew / currentDecision.Cache.BigBlind)) * currentDecision.Cache.BigBlind != raiseToCallAmountNew)
                {
                    //Round the raiseToCallAmount
                    decimal raiseToCallAmountBlindMultiple = raiseToCallAmountNew / currentDecision.Cache.LittleBlind;
                    raiseToCallAmountNew = Math.Round(raiseToCallAmountBlindMultiple, 0, MidpointRounding.AwayFromZero) * currentDecision.Cache.LittleBlind;
                }

                //Round the raiseToStealAmount
                decimal raiseToStealAmountBlindMultiple = raiseToStealAmountNew / currentDecision.Cache.LittleBlind;
                raiseToStealAmountNew = Math.Round(raiseToStealAmountBlindMultiple, 0, MidpointRounding.AwayFromZero) * currentDecision.Cache.LittleBlind;
            }
            else
            {
                raiseToCallAmountNew  = (decimal)infoStore.GetInfoValue(InfoType.WR_RaiseToCallAmount);
                raiseToStealAmountNew = (decimal)infoStore.GetInfoValue(InfoType.WR_RaiseToStealAmount);
            }

            //Some raise validation
            //Validate we can actually raise the selected amounts
            decimal maximumRaiseAmount = remainingPlayerStack + (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);

            //if (raiseToCallAmount > maximumRaiseAmount) raiseToCallAmount = maximumRaiseAmount;
            //if (raiseToStealAmount > maximumRaiseAmount) raiseToStealAmount = maximumRaiseAmount;

            //Check for a big raise amount which would be best as going all in
            if (raiseToCallAmountNew > 0.8m * maximumRaiseAmount)
            {
                raiseToCallAmountNew = maximumRaiseAmount;
            }
            if (raiseToStealAmountNew > 0.8m * maximumRaiseAmount)
            {
                raiseToStealAmountNew = maximumRaiseAmount;
            }

            //If we have already raised twice this round all amounts are set to all in
            if (currentRoundPlayerActions.Count(entry => entry == PokerAction.Raise) >= 2)
            {
                raiseToCallAmountNew  = maximumRaiseAmount;
                raiseToStealAmountNew = maximumRaiseAmount;
            }
            #endregion RaiseAmounts

            /////////////////////////////////////////
            ///////////ALL NON BOOLEAN INPUTS BETWEEN 0.9 and 0.1
            /////////////////////////////////////////

            #region neuralInputs

            #region gameStage

            decimal gameStagePreFlop  = 0;
            decimal gameStagePostFlop = 0;
            decimal gameStageRiver    = 0;

            if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 0)
            {
                gameStagePreFlop = 1;
            }
            else
            {
                gameStagePostFlop = 1;
            }

            if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 3)
            {
                gameStageRiver = 1;
            }

            networkInputs.Add("GameStagePreFlop", gameStagePreFlop);
            networkInputs.Add("GameStagePostFlop", gameStagePostFlop);
            networkInputs.Add("GameStageRiver", gameStageRiver);

            #endregion gameStage
            //3 inputs

            #region preFlop HoleCards
            if (gameStagePreFlop == 1)
            {
                //Card info types
                networkInputs.Add("HoleCardsAAPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsAAPair_Bool));
                networkInputs.Add("HoleCardsKKPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsKKPair_Bool));
                networkInputs.Add("HoleCardsAKPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsAK_Bool));
                networkInputs.Add("HoleCardsOtherHighPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsOtherHighPair_Bool));
                networkInputs.Add("HoleCardsOtherLowPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsOtherLowPair_Bool));
                networkInputs.Add("HoleCardsTroubleHand", infoStore.GetInfoValue(InfoType.CP_HoleCardsTroubleHand_Bool));
                networkInputs.Add("HoleCardsMidConnector", infoStore.GetInfoValue(InfoType.CP_HoleCardsMidConnector_Bool));
                networkInputs.Add("HoleCardsLowConnector", infoStore.GetInfoValue(InfoType.CP_HoleCardsLowConnector_Bool));
                networkInputs.Add("HoleCardsSuited", infoStore.GetInfoValue(InfoType.CP_HoleCardsSuited_Bool));
            }
            else
            {
                networkInputs.Add("HoleCardsAAPair", 0);
                networkInputs.Add("HoleCardsKKPair", 0);
                networkInputs.Add("HoleCardsAKPair", 0);
                networkInputs.Add("HoleCardsOtherHighPair", 0);
                networkInputs.Add("HoleCardsOtherLowPair", 0);
                networkInputs.Add("HoleCardsTroubleHand", 0);
                networkInputs.Add("HoleCardsMidConnector", 0);
                networkInputs.Add("HoleCardsLowConnector", 0);
                networkInputs.Add("HoleCardsSuited", 0);
            }
            #endregion
            //12 inputs

            #region playerPosition and numPlayers
            decimal dealerDistance     = ((infoStore.GetInfoValue(InfoType.GP_DealerDistance_Byte) - 1) / (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) - 1));
            decimal tablePositionEarly = 0;
            decimal tablePositionMid   = 0;
            decimal tablePositionLate  = 0;

            if (dealerDistance < (1.0m / 3.0m) && dealerDistance >= 0)
            {
                tablePositionEarly = 1;
            }
            else if (dealerDistance < (2.0m / 3.0m) && dealerDistance >= 0)
            {
                tablePositionMid = 1;
            }
            else if (dealerDistance <= 1.0m && dealerDistance >= 0)
            {
                tablePositionLate = 1;
            }
            else
            {
                throw new Exception("Dealer distance must be between 0 and 1.");
            }

            networkInputs.Add("TablePositionEarly", tablePositionEarly);
            networkInputs.Add("TablePositionMid", tablePositionMid);
            networkInputs.Add("TablePositionLate", tablePositionLate);

            networkInputs.Add("NumActivePlayers4Plus", (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) >= 4 ? 1 : 0));
            networkInputs.Add("NumActivePlayers3", (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) == 3 ? 1 : 0));
            networkInputs.Add("NumActivePlayers2", (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) == 2 ? 1 : 0));

            networkInputs.Add("LastToAct", (infoStore.GetInfoValue(InfoType.GP_NumUnactedPlayers_Byte) == 1 ? 1 : 0));
            #endregion playerPosition and numPlayers
            //19 inputs

            #region potCommitment
            decimal potCommitted = 0;
            if (infoStore.GetInfoValue(InfoType.BP_PlayerMoneyInPot_Decimal) > infoStore.GetInfoValue(InfoType.BP_PlayerHandStartingStackAmount_Decimal) * 0.75m)
            {
                potCommitted = 1;
            }

            networkInputs.Add("PotCommitted", potCommitted);
            #endregion
            //20 inputs

            #region Hand and Action History
            List <PokerAction> allPlayerActionsCurrentHand = currentDecision.Cache.getPlayerActionsCurrentHand(currentDecision.PlayerId);
            decimal            callCount  = (decimal)allPlayerActionsCurrentHand.Count(entry => entry == PokerAction.Call);
            decimal            checkCount = (decimal)allPlayerActionsCurrentHand.Count(entry => entry == PokerAction.Check);
            decimal            raiseCount = (decimal)allPlayerActionsCurrentHand.Count(entry => entry == PokerAction.Raise);

            decimal currentHandOurAggression = 0;

            currentHandOurAggression = raiseCount - (callCount + checkCount);
            if (currentHandOurAggression < -6)
            {
                currentHandOurAggression = -6;
            }
            else if (currentHandOurAggression > 6)
            {
                currentHandOurAggression = 6;
            }

            networkInputs.Add("CurrentHandOurAggression", currentHandOurAggression / 6.0m);

            networkInputs.Add("CurrentRoundFirstAction", (currentRoundPlayerActions.Count == 0 ? 1 : 0));
            networkInputs.Add("CurrentRoundSecondAction", (currentRoundPlayerActions.Count == 1 ? 1 : 0));
            networkInputs.Add("CurrentRoundThirdPlusAction", (currentRoundPlayerActions.Count >= 2 ? 1 : 0));
            #endregion Hand and Round Actions
            //24 inputs

            #region WinRatio
            decimal probWin = 1.0m - infoStore.GetInfoValue(InfoType.WR_ProbOpponentHasBetterWRFIXED);
            networkInputs.Add("WRProbWin", ScaleContInput(probWin));
            #endregion
            //25 inputs

            #region EV and CheckPossibilitiy

            decimal bigBlindEVScaleAmount = 20;

            #region CallEV
            //The following EV assumes we can actually call the additionaCallAmount
            //We could cap the additionalCallAmount but we would then also not be able to win the total pot amount ;( again a minor error
            decimal actualCallCheckEV = (currentPotAmount * probWin) - ((1.0m - probWin) * additionalCallAmount);
            decimal scaledCallCheckEV = (actualCallCheckEV / currentDecision.Cache.BigBlind) / bigBlindEVScaleAmount;
            if (scaledCallCheckEV > 1)
            {
                networkInputs.Add("ScaledCallCheckEV", 1);
            }
            else if (scaledCallCheckEV < -1)
            {
                networkInputs.Add("ScaledCallCheckEV", -1);
            }
            else
            {
                networkInputs.Add("ScaledCallCheckEV", scaledCallCheckEV);
            }
            #endregion

            //We can raise if we have more stack than the current additional call amount
            //If we can't raise the raiseEV's get set to the callEV (which may be negative)
            bool raisePossible = (remainingPlayerStack > additionalCallAmount);

            decimal probAllOpponentFoldRaiseToCall  = infoStore.GetInfoValue(InfoType.WR_RaiseToCallStealSuccessProb);
            decimal probAllOpponentFoldRaiseToSteal = (gameStagePreFlop == 1 ? 0 : infoStore.GetInfoValue(InfoType.WR_RaiseToStealSuccessProb));
            long[]  activePlayerIds = currentDecision.Cache.getActivePlayerIds();

            #region RaiseToCallEV
            if (raisePossible)
            {
                decimal potAmountAssumingAllCall = currentPotAmount;

                //If we are preflop then it would be unrealistic to expect everyone to call
                //For Call we assume that if there are 8 players to act after the raise we get 2 calls
                //If there are 4 players to act after the raise we get 1 call
                //This is the same as dividing the number of activePlayers by 4
                if (gameStagePreFlop == 1)
                {
                    //We assume anyone who has already called or raised will call again
                    long[] calledRaisedPlayerIds = (from current in currentDecision.Cache.getAllHandActions()
                                                    where current.actionType == PokerAction.Call || current.actionType == PokerAction.Raise
                                                    where current.playerId != currentDecision.PlayerId
                                                    select current.playerId).ToArray();

                    for (int i = 0; i < calledRaisedPlayerIds.Length; i++)
                    {
                        potAmountAssumingAllCall += raiseToCallAmountNew - currentDecision.Cache.getPlayerCurrentRoundBetAmount(calledRaisedPlayerIds[i]);
                    }

                    potAmountAssumingAllCall += ((decimal)(activePlayerIds.Length - 1 - calledRaisedPlayerIds.Length) / 4) * raiseToCallAmountNew;
                }
                else
                {
                    for (int i = 0; i < activePlayerIds.Length; i++)
                    {
                        if (activePlayerIds[i] != currentDecision.PlayerId)
                        {
                            potAmountAssumingAllCall += raiseToCallAmountNew - currentDecision.Cache.getPlayerCurrentRoundBetAmount(activePlayerIds[i]);
                        }
                    }
                }

                decimal extraDollarRiskAmount = raiseToCallAmountNew - infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);

                decimal actualRaiseToCallEV = (currentPotAmount * probAllOpponentFoldRaiseToCall) + (probWin * potAmountAssumingAllCall * (1.0m - probAllOpponentFoldRaiseToCall)) - ((1.0m - probWin) * extraDollarRiskAmount * (1.0m - probAllOpponentFoldRaiseToCall));

                decimal scaledRaiseToCallEV = (actualRaiseToCallEV / currentDecision.Cache.BigBlind) / bigBlindEVScaleAmount;
                if (scaledRaiseToCallEV > 1)
                {
                    networkInputs.Add("ScaledRaiseToCallEV", 1);
                }
                else if (scaledRaiseToCallEV < -1)
                {
                    networkInputs.Add("ScaledRaiseToCallEV", -1);
                }
                else
                {
                    networkInputs.Add("ScaledRaiseToCallEV", scaledRaiseToCallEV);
                }
            }
            else
            {
                networkInputs.Add("ScaledRaiseToCallEV", networkInputs["ScaledCallCheckEV"]);
            }
            #endregion

            #region RaiseToStealEV
            if (raisePossible)
            {
                decimal potAmountAssumingAllCall = currentPotAmount;

                //If we are preflop then it would be unrealistic to expect everyone to call
                //For steal we assume that if there are 8 players to act after the raise we get 1 call
                //If there are 4 players to act after the raise we get 0.5 call
                //This is the same as dividing the number of activePlayers by 8
                if (gameStagePreFlop == 1)
                {
                    //We assume anyone who has already raised will call again
                    long[] raisedPlayerIds = (from current in currentDecision.Cache.getAllHandActions()
                                              where current.actionType == PokerAction.Raise
                                              where current.playerId != currentDecision.PlayerId
                                              select current.playerId).ToArray();

                    for (int i = 0; i < raisedPlayerIds.Length; i++)
                    {
                        potAmountAssumingAllCall += raiseToStealAmountNew - currentDecision.Cache.getPlayerCurrentRoundBetAmount(raisedPlayerIds[i]);
                    }

                    potAmountAssumingAllCall += ((decimal)(activePlayerIds.Length - 1 - raisedPlayerIds.Length) / 8) * raiseToStealAmountNew;
                }
                else
                {
                    for (int i = 0; i < activePlayerIds.Length; i++)
                    {
                        if (activePlayerIds[i] != currentDecision.PlayerId)
                        {
                            potAmountAssumingAllCall += raiseToStealAmountNew - currentDecision.Cache.getPlayerCurrentRoundBetAmount(activePlayerIds[i]);
                        }
                    }
                }

                decimal extraDollarRiskAmount = raiseToStealAmountNew - infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);

                decimal actualRaiseToStealEV = (currentPotAmount * probAllOpponentFoldRaiseToSteal) + (probWin * potAmountAssumingAllCall * (1.0m - probAllOpponentFoldRaiseToSteal)) - ((1.0m - probWin) * extraDollarRiskAmount * (1.0m - probAllOpponentFoldRaiseToSteal));
                decimal scaledRaiseToStealEV = (actualRaiseToStealEV / currentDecision.Cache.BigBlind) / bigBlindEVScaleAmount;
                if (scaledRaiseToStealEV > 1)
                {
                    networkInputs.Add("ScaledRaiseToStealEV", 1);
                }
                else if (scaledRaiseToStealEV < -1)
                {
                    networkInputs.Add("ScaledRaiseToStealEV", -1);
                }
                else
                {
                    networkInputs.Add("ScaledRaiseToStealEV", scaledRaiseToStealEV);
                }
            }
            else
            {
                networkInputs.Add("ScaledRaiseToStealEV", networkInputs["ScaledCallCheckEV"]);
            }
            #endregion

            bool checkPossible = (infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) == infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal));
            networkInputs.Add("CheckPossible", Convert.ToDecimal(checkPossible));

            networkInputs.Add("CallAmount1BBOrLess", !checkPossible && ((decimal)infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) - (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal)) <= currentDecision.Cache.BigBlind ? 1 : 0);
            #endregion
            //28 inputs

            #region PAP
            //probAllOpponentFoldRaiseToSteal == 0 preflop
            networkInputs.Add("ProbRaiseToStealSuccess", ScaleContInput(probAllOpponentFoldRaiseToSteal));
            #endregion
            //29 inputs

            #region PlayerAggression
            //All of V4 players get aggression defaults
            networkInputs.Add("AvgLiveOppPreFlopPlayFreq", ScaleContInput(infoStore.GetInfoValue(InfoType.AP_AvgLiveOppPreFlopPlayFreq_Double)));
            networkInputs.Add("AvgLiveOppPostFlopPlayFreq", ScaleContInput(infoStore.GetInfoValue(InfoType.AP_AvgLiveOppPostFlopPlayFreq_Double)));
            networkInputs.Add("AvgLiveOppCurrentRoundAggr", ScaleContInput(infoStore.GetInfoValue(InfoType.AP_AvgLiveOppCurrentRoundAggr_Double)));
            networkInputs.Add("AvgLiveOppCurrentRoundAggrAcc", ScaleContInput(infoStore.GetInfoValue(InfoType.AP_AvgLiveOppCurrentRoundAggrAcc_Double)));
            #endregion
            //33 inputs

            #endregion neuralInputs

            NNDataSource aiDecisionData = new NNDataSource(networkInputs.Values.ToArray(), NeuralAINNModelV6.Input_Neurons, true);
            //decisionLogStr = aiDecisionData.ToStringAdv(true);

            if (true)
            {
                //Get the network outputs
                double[] networkInputsArray = null;
                aiDecisionData.returnInput(ref networkInputsArray);
                double[] networkOutput = getPlayerNetworkPrediction(currentDecision.AiConfigStr, networkInputsArray);

                //Blank the non possible actions
                if (checkPossible)
                {
                    //Blank fold
                    networkOutput[0] = 0;
                    //Blank call
                    networkOutput[2] = 0;
                }
                else
                {
                    //Blank check
                    networkOutput[1] = 0;
                }

                //Get the bot decision
                NeuralAiDecision decision = new NeuralAiDecision(networkOutput, !Convert.ToBoolean(gameStageRiver) /*|| checkPossible*/, randomGen.NextDouble(), true);

                Debug.Print("Decision");

                //Can now check the fold decision for aces
                //if (decision.BotAction == 0 && networkInputs["HoleCardsAAPair"] == 1 && gameStagePreFlop == 1 && currentDecision.AiConfigStr.StartsWith("GeneticPokerPlayers"))
                //    currentDecision.Cache.SaveToDisk("", currentDecision.PlayerId + "-" + InfoProviderBase.CurrentJob.JobId + "-" + currentDecision.Cache.getCurrentHandId() + "-" + currentDecision.Cache.getMostRecentLocalIndex()[1]);

                if (decision.BotAction == 0)
                {
                    return(new Play(PokerAction.Fold, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 1)
                {
                    return(new Play(PokerAction.Check, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 2)
                {
                    return(new Play(PokerAction.Call, additionalCallAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 3)
                {
                    return(new Play(PokerAction.Raise, raiseToCallAmountNew, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 4)
                {
                    return(new Play(PokerAction.Raise, raiseToStealAmountNew, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else
                {
                    throw new Exception("Something has gone wery wong!");
                }
            }
        }
示例#4
0
        protected override Play GetDecision()
        {
            Dictionary <string, decimal> networkInputs = new Dictionary <string, decimal>();

            /////////////////////////////////////////
            ///////////ALL NON BOOLEAN INPUTS BETWEEN 0.9 and 0.1
            /////////////////////////////////////////

            #region neuralInputs

            #region gameStage

            decimal gameStagePreFlop  = 0;
            decimal gameStagePostFlop = 0;
            decimal gameStageRiver    = 0;

            if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 0)
            {
                gameStagePreFlop = 1;
            }
            else
            {
                gameStagePostFlop = 1;
            }

            if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 3)
            {
                gameStageRiver = 1;
            }

            networkInputs.Add("GameStagePreFlop", gameStagePreFlop);
            networkInputs.Add("GameStagePostFlop", gameStagePostFlop);
            networkInputs.Add("GameStageRiver", gameStageRiver);

            #endregion gameStage
            //3 inputs

            #region preFlop HoleCards
            if (gameStagePreFlop == 1)
            {
                //Card info types
                networkInputs.Add("HoleCardsAAPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsAAPair_Bool));
                networkInputs.Add("HoleCardsKKPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsKKPair_Bool));
                networkInputs.Add("HoleCardsAKPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsAK_Bool));
                networkInputs.Add("HoleCardsOtherHighPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsOtherHighPair_Bool));
                networkInputs.Add("HoleCardsOtherLowPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsOtherLowPair_Bool));
                networkInputs.Add("HoleCardsTroubleHand", infoStore.GetInfoValue(InfoType.CP_HoleCardsTroubleHand_Bool));
                networkInputs.Add("HoleCardsMidConnector", infoStore.GetInfoValue(InfoType.CP_HoleCardsMidConnector_Bool));
                networkInputs.Add("HoleCardsLowConnector", infoStore.GetInfoValue(InfoType.CP_HoleCardsLowConnector_Bool));
                networkInputs.Add("HoleCardsSuited", infoStore.GetInfoValue(InfoType.CP_HoleCardsSuited_Bool));
            }
            else
            {
                networkInputs.Add("HoleCardsAAPair", 0);
                networkInputs.Add("HoleCardsKKPair", 0);
                networkInputs.Add("HoleCardsAKPair", 0);
                networkInputs.Add("HoleCardsOtherHighPair", 0);
                networkInputs.Add("HoleCardsOtherLowPair", 0);
                networkInputs.Add("HoleCardsTroubleHand", 0);
                networkInputs.Add("HoleCardsMidConnector", 0);
                networkInputs.Add("HoleCardsLowConnector", 0);
                networkInputs.Add("HoleCardsSuited", 0);
            }
            #endregion
            //12 inputs

            #region playerPosition and numPlayers
            decimal dealerDistance     = ((infoStore.GetInfoValue(InfoType.GP_DealerDistance_Byte) - 1) / (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) - 1));
            decimal tablePositionEarly = 0;
            decimal tablePositionMid   = 0;
            decimal tablePositionLate  = 0;

            if (dealerDistance < (1.0m / 3.0m) && dealerDistance >= 0)
            {
                tablePositionEarly = 1;
            }
            else if (dealerDistance < (2.0m / 3.0m) && dealerDistance >= 0)
            {
                tablePositionMid = 1;
            }
            else if (dealerDistance <= 1.0m && dealerDistance >= 0)
            {
                tablePositionLate = 1;
            }
            else
            {
                throw new Exception("Dealer distance must be between 0 and 1.");
            }

            networkInputs.Add("TablePositionEarly", tablePositionEarly);
            networkInputs.Add("TablePositionMid", tablePositionMid);
            networkInputs.Add("TablePositionLate", tablePositionLate);

            networkInputs.Add("NumActivePlayers4Plus", (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) >= 4 ? 1 : 0));
            networkInputs.Add("NumActivePlayers3", (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) == 3 ? 1 : 0));
            networkInputs.Add("NumActivePlayers2", (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) == 2 ? 1 : 0));

            networkInputs.Add("LastToAct", (infoStore.GetInfoValue(InfoType.GP_NumUnactedPlayers_Byte) == 1 ? 1 : 0));
            #endregion playerPosition and numPlayers
            //19 inputs

            #region potCommitment
            decimal potCommitted = 0;
            if (infoStore.GetInfoValue(InfoType.BP_PlayerMoneyInPot_Decimal) > infoStore.GetInfoValue(InfoType.BP_PlayerHandStartingStackAmount_Decimal) * 0.75m)
            {
                potCommitted = 1;
            }

            networkInputs.Add("PotCommitted", potCommitted);
            #endregion
            //20 inputs

            #region Hand and Action History
            List <PokerAction> allPlayerActionsCurrentHand = currentDecision.Cache.getPlayerActionsCurrentHand(currentDecision.PlayerId);
            decimal            callCount  = (decimal)allPlayerActionsCurrentHand.Count(entry => entry == PokerAction.Call);
            decimal            checkCount = (decimal)allPlayerActionsCurrentHand.Count(entry => entry == PokerAction.Check);
            decimal            raiseCount = (decimal)allPlayerActionsCurrentHand.Count(entry => entry == PokerAction.Raise);

            decimal currentHandOurAggression = 0;
            //if ((callCount > 0 || checkCount > 0) && raiseCount > 0)
            //    //We scale our own aggression to be between 1 and 0, 1 being very aggressive, 0 being very passive
            //    currentHandOurAggression = (decimal)((Math.Log10(raiseCount / (callCount + checkCount)) + 2.0) / 3.0);
            //else if (callCount == 0 && checkCount == 0 && raiseCount > 0)
            //    currentHandOurAggression = 1;

            currentHandOurAggression = raiseCount - (callCount + checkCount);
            if (currentHandOurAggression < -6)
            {
                currentHandOurAggression = -6;
            }
            else if (currentHandOurAggression > 6)
            {
                currentHandOurAggression = 6;
            }

            networkInputs.Add("CurrentHandOurAggression", currentHandOurAggression / 6.0m);

            //Current Round Actions
            List <PokerAction> currentRoundPlayerActions = (from current in currentDecision.Cache.getPlayerCurrentRoundActions(currentDecision.PlayerId)
                                                            where current == PokerAction.Check ||
                                                            current == PokerAction.Call ||
                                                            current == PokerAction.Raise
                                                            select current).ToList();

            networkInputs.Add("CurrentRoundFirstAction", (currentRoundPlayerActions.Count == 0 ? 1 : 0));
            networkInputs.Add("CurrentRoundSecondAction", (currentRoundPlayerActions.Count == 1 ? 1 : 0));
            networkInputs.Add("CurrentRoundThirdPlusAction", (currentRoundPlayerActions.Count >= 2 ? 1 : 0));
            #endregion Hand and Round Actions
            //24 inputs

            #region WinRatio
            decimal probWin = 1.0m - infoStore.GetInfoValue(InfoType.WR_ProbOpponentHasBetterWRFIXED);
            networkInputs.Add("WRProbWin", ScaleContInput(probWin));
            #endregion
            //25 inputs

            #region CallEV and CheckPossibilitiy
            //We are only going to use call EV
            //Defined as (Pot * ProbWin) - (Call Amount * ProbLoose)
            decimal additionaCallAmount = currentDecision.Cache.getMinimumPlayAmount() - (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);

            //The following EV assumes we can actually call the additionaCallAmount
            //We could cap the additionalCallAmount but we would then also not be able to win the total pot amount ;( again a minor error
            decimal actualCallEV = (infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal) * probWin) - ((1.0m - probWin) * additionaCallAmount);

            //We now have to scale the actualCallEV amount between 1 and -1 as a fraction of the big blind amount
            decimal scaledCallEV = (actualCallEV / currentDecision.Cache.BigBlind) / 5.0m;
            if (scaledCallEV > 1)
            {
                networkInputs.Add("ScaledCallEV", 1);
            }
            else if (scaledCallEV < -1)
            {
                networkInputs.Add("ScaledCallEV", -1);
            }
            else
            {
                networkInputs.Add("ScaledCallEV", scaledCallEV);
            }

            bool checkPossible = (infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) == infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal));
            networkInputs.Add("CheckPossible", Convert.ToDecimal(checkPossible));

            networkInputs.Add("CallAmount1BBOrLess", !checkPossible && ((decimal)infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) - (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal)) <= currentDecision.Cache.BigBlind ? 1 : 0);
            #endregion
            //28 inputs

            #region PAP
            if (gameStagePreFlop == 1)
            {
                networkInputs.Add("ProbRaiseToStealSuccess", ScaleContInput(0));
            }
            else
            {
                networkInputs.Add("ProbRaiseToStealSuccess", ScaleContInput(infoStore.GetInfoValue(InfoType.WR_RaiseToStealSuccessProb)));
            }
            #endregion
            //29 inputs

            #region PlayerAggression
            //All of V4 players get aggression defaults
            networkInputs.Add("AvgLiveOppPreFlopPlayFreq", ScaleContInput(infoStore.GetInfoValue(InfoType.AP_AvgLiveOppPreFlopPlayFreq_Double)));
            networkInputs.Add("AvgLiveOppPostFlopPlayFreq", ScaleContInput(infoStore.GetInfoValue(InfoType.AP_AvgLiveOppPostFlopPlayFreq_Double)));
            networkInputs.Add("AvgLiveOppCurrentRoundAggr", ScaleContInput(infoStore.GetInfoValue(InfoType.AP_AvgLiveOppCurrentRoundAggr_Double)));
            networkInputs.Add("AvgLiveOppCurrentRoundAggrAcc", ScaleContInput(infoStore.GetInfoValue(InfoType.AP_AvgLiveOppCurrentRoundAggrAcc_Double)));
            #endregion
            //33 inputs

            #endregion neuralInputs

            #region RaiseAmounts
            decimal raiseToCallAmount, raiseToStealAmount;

            //If we are preflop we calculate the raise amounts in a slightly more fixed fashion
            if (gameStagePreFlop == 1)
            {
                double randomNumber = randomGen.NextDouble();

                //If the pot is unraised
                if (infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal) == currentDecision.Cache.BigBlind)
                {
                    if (randomNumber > 0.8)
                    {
                        raiseToCallAmount = 4m * currentDecision.Cache.BigBlind;
                    }
                    else if (randomNumber > 0.4)
                    {
                        raiseToCallAmount = 3.5m * currentDecision.Cache.BigBlind;
                    }
                    else
                    {
                        raiseToCallAmount = 3.0m * currentDecision.Cache.BigBlind;
                    }
                }
                else
                {
                    decimal currentRoundLastRaiseAmount = infoStore.GetInfoValue(InfoType.BP_LastAdditionalRaiseAmount);
                    decimal additionalNewRaiseAmount;

                    if (randomNumber > 0.9)
                    {
                        additionalNewRaiseAmount = 2 * currentRoundLastRaiseAmount;
                    }
                    else if (randomNumber > 0.45)
                    {
                        additionalNewRaiseAmount = 1.5m * currentRoundLastRaiseAmount;
                    }
                    else
                    {
                        additionalNewRaiseAmount = 1 * currentRoundLastRaiseAmount;
                    }

                    raiseToCallAmount = additionalNewRaiseAmount + infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal);
                }

                raiseToStealAmount = 1.5m * raiseToCallAmount;

                //We will only scale the raiseToCallAmount if it is not a bigblind multiple
                if (((int)(raiseToCallAmount / currentDecision.Cache.BigBlind)) * currentDecision.Cache.BigBlind != raiseToCallAmount)
                {
                    //Round the raiseToCallAmount
                    decimal raiseToCallAmountBlindMultiple = raiseToCallAmount / currentDecision.Cache.LittleBlind;
                    raiseToCallAmount = Math.Round(raiseToCallAmountBlindMultiple, 0, MidpointRounding.AwayFromZero) * currentDecision.Cache.LittleBlind;
                }

                //Round the raiseToStealAmount
                decimal raiseToStealAmountBlindMultiple = raiseToStealAmount / currentDecision.Cache.LittleBlind;
                raiseToStealAmount = Math.Round(raiseToStealAmountBlindMultiple, 0, MidpointRounding.AwayFromZero) * currentDecision.Cache.LittleBlind;
            }
            else
            {
                raiseToCallAmount  = (decimal)infoStore.GetInfoValue(InfoType.WR_RaiseToCallAmount);
                raiseToStealAmount = (decimal)infoStore.GetInfoValue(InfoType.WR_RaiseToStealAmount);
            }
            #endregion RaiseAmounts

            NNDataSource aiDecisionData = new NNDataSource(networkInputs.Values.ToArray(), NeuralAINNModelV5.Input_Neurons, true);
            //decisionLogStr = aiDecisionData.ToStringAdv(true);

            if (true)
            {
                //Get the network outputs
                double[] networkInputsArray = null;
                aiDecisionData.returnInput(ref networkInputsArray);
                double[] networkOutput = getPlayerNetworkPrediction(currentDecision.AiConfigStr, networkInputsArray);

                //Blank the non possible actions
                if (checkPossible)
                {
                    //Blank fold
                    networkOutput[0] = 0;
                    //Blank call
                    networkOutput[2] = 0;
                }
                else
                {
                    //Blank check
                    networkOutput[1] = 0;
                }

                //Get the bot decision
                NeuralAiDecision decision = new NeuralAiDecision(networkOutput, !Convert.ToBoolean(gameStageRiver) /*|| checkPossible*/, randomGen.NextDouble(), true);

                if (decision.BotAction >= 3)
                {
                    //We need to cap the raise to steal amount as it's probably too large!!
                    //if (raiseToStealAmount > 1.3m * infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal))
                    //    raiseToStealAmount = 1.3m * infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal);

                    //if (raiseToCallAmount > 1.3m * infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal))
                    //    raiseToCallAmount = 1.3m * infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal);

                    //If we have set a config for PAP_RaiseToCallAmount_Amount then we limit raises per round to three
                    if (currentRoundPlayerActions.Count(entry => entry == PokerAction.Raise) >= 2)
                    {
                        raiseToCallAmount  = currentDecision.Cache.getPlayerStack(currentDecision.PlayerId) + (decimal)infoStore.GetInfoValue(InfoType.BP_PlayerBetAmountCurrentRound_Decimal);
                        raiseToStealAmount = raiseToCallAmount;
                    }
                }

                if (decision.BotAction == 0)
                {
                    return(new Play(PokerAction.Fold, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 1)
                {
                    return(new Play(PokerAction.Check, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 2)
                {
                    return(new Play(PokerAction.Call, additionaCallAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 3)
                {
                    return(new Play(PokerAction.Raise, raiseToCallAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else if (decision.BotAction == 4)
                {
                    return(new Play(PokerAction.Raise, raiseToStealAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
                }
                else
                {
                    throw new Exception("Something has gone wery wong!");
                }
            }
        }
        protected override Play GetDecision()
        {
            //We need to write to the decision string here!
            Dictionary <string, decimal> networkInputs = new Dictionary <string, decimal>();

            networkInputs.Add("ProbCardsWin", infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage));
            networkInputs.Add("ProbCardsWinOpponentWin", (1 - infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage)) / (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) - 1));

            decimal currentGameStage = (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte));
            decimal postFlop;

            if (currentGameStage == 0)
            {
                postFlop = 0;
            }
            else
            {
                postFlop = 1;
            }

            networkInputs.Add("PostFlop", postFlop);

            decimal currentRoundBetAmount = currentDecision.Cache.getPlayerCurrentRoundBetAmount(currentDecision.PlayerId);
            decimal minCallAmount         = infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal);
            decimal raiseToCallAmount     = (infoStore.GetInfoValue(InfoType.PAP_RaiseToCallAmount_Amount));

            decimal scaledCurrentRoundMinimumPlayAmount = (minCallAmount / currentDecision.Cache.MaxStack);
            decimal scaledCurrentRoundPlayerBetAmount   = (currentRoundBetAmount / currentDecision.Cache.MaxStack);

            if (scaledCurrentRoundMinimumPlayAmount > 1)
            {
                scaledCurrentRoundMinimumPlayAmount = 1;
            }
            if (scaledCurrentRoundPlayerBetAmount > 1)
            {
                scaledCurrentRoundPlayerBetAmount = 1;
            }

            networkInputs.Add("ScaledCurrentRoundMinimumPlayAmount", scaledCurrentRoundMinimumPlayAmount);
            networkInputs.Add("ScaledCurrentRoundPlayerBetAmount", scaledCurrentRoundPlayerBetAmount);

            //(probCardsWin, probCardsWinOpponentWin, postFlop, scaledCurrentRoundMinimumPlayAmount, scaledCurrentRoundPlayerBetAmount);
            NNDataSource aiDecisionData = new NNDataSource(networkInputs.Values.ToArray(), NeuralAINNModelV2.Input_Neurons);

            //decisionLogStr = aiDecisionData.ToString();

            //Get the network outputs
            double[] networkInputsArray = null;
            aiDecisionData.returnInput(ref networkInputsArray);
            double[] networkOutput = getPlayerNetworkPrediction(currentDecision.AiConfigStr, networkInputsArray);

            NeuralAiDecision decision = new NeuralAiDecision(networkOutput);
            //Debug.Print("AI Decision - [{0}], [{1}], [{2}], [{3}], [{4}]", networkOutput[0], networkOutput[1], networkOutput[2], networkOutput[3], networkOutput[4]);
            Play botAction;

            if (decision.BotAction == 0)
            {
                botAction = new Play(PokerAction.Fold, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0);
            }
            else if (decision.BotAction == 1)
            {
                botAction = new Play(PokerAction.Call, currentDecision.Cache.getMinimumPlayAmount() - currentRoundBetAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0);
            }
            else if (decision.BotAction == 2)
            {
                botAction = new Play(PokerAction.Raise, (decimal)raiseToCallAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0);
            }
            else
            {
                throw new Exception("Something has gone wery wong!");
            }

            //Call if already raised pre flop
            if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 0 && botAction.Action == PokerAction.Raise)
            {
                //Split down into seperate IF to make sure there is not too much of a performance hit
                if (currentDecision.Cache.getPlayerCurrentRoundActions(currentDecision.PlayerId).Contains(PokerAction.Raise))
                {
                    botAction.Action = PokerAction.Call;
                    botAction.Amount = (decimal)(minCallAmount - currentRoundBetAmount);
                }
            }

            return(botAction);
        }
示例#6
0
        protected override Play GetDecision()
        {
            //List<double> networkInputs = new List<double>();
            Dictionary <string, decimal> networkInputs = new Dictionary <string, decimal>();

            //We need to write to the decision string here!

            #region neuralInputs

            //Card info types
            networkInputs.Add("HoleCardsAAPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsAAPair_Bool));
            networkInputs.Add("HoleCardsKKPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsKKPair_Bool));
            networkInputs.Add("HoleCardsAKPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsAK_Bool));
            networkInputs.Add("HoleCardsOtherHighPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsOtherHighPair_Bool));
            networkInputs.Add("HoleCardsOtherLowPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsOtherLowPair_Bool));
            networkInputs.Add("HoleCardsTroubleHand", infoStore.GetInfoValue(InfoType.CP_HoleCardsTroubleHand_Bool));
            networkInputs.Add("HoleCardsMidConnector", infoStore.GetInfoValue(InfoType.CP_HoleCardsMidConnector_Bool));
            networkInputs.Add("HoleCardsLowConnector", infoStore.GetInfoValue(InfoType.CP_HoleCardsLowConnector_Bool));
            networkInputs.Add("HoleCardsSuited", infoStore.GetInfoValue(InfoType.CP_HoleCardsSuited_Bool));
            networkInputs.Add("HoleCardsFlushDraw", infoStore.GetInfoValue(InfoType.CP_HoleCardsFlushDraw_Bool));

            networkInputs.Add("HoleCardsOuterStraightDraw", infoStore.GetInfoValue(InfoType.CP_HoleCardsOuterStraightDrawWithHC_Bool));
            networkInputs.Add("HoleCardsInnerStraightDraw", infoStore.GetInfoValue(InfoType.CP_HoleCardsInnerStraightDrawWithHC_Bool));
            networkInputs.Add("HoleCardsTopOrTwoPair", infoStore.GetInfoValue(InfoType.CP_HoleCardsTopOrTwoPair_Bool));
            networkInputs.Add("HoleCardsAorKinHand", infoStore.GetInfoValue(InfoType.CP_HoleCardsAOrKInHand_Bool));
            networkInputs.Add("HoleCards3KindOrBetter", infoStore.GetInfoValue(InfoType.CP_HoleCards3KindOrBetterMadeWithHC_Bool));
            networkInputs.Add("AonBoard", infoStore.GetInfoValue(InfoType.CP_AOnBoard_Bool));
            networkInputs.Add("KonBoard", infoStore.GetInfoValue(InfoType.CP_KOnBoard_Bool));
            networkInputs.Add("AKQRatio", infoStore.GetInfoValue(InfoType.CP_AKQToBoardRatio_Real));

            networkInputs.Add("FlushPossible", infoStore.GetInfoValue(InfoType.CP_FlushPossible_Bool));
            networkInputs.Add("StraightPossible", infoStore.GetInfoValue(InfoType.CP_StraightPossible_Bool));
            networkInputs.Add("TableStraightDraw", infoStore.GetInfoValue(InfoType.CP_TableStraightDraw_Bool));
            networkInputs.Add("TableFlushDraw", infoStore.GetInfoValue(InfoType.CP_TableFlushDraw_Bool));

            //DealtInRatio, ActiveRatio and UnactedRatio
            networkInputs.Add("DealtInRatio", infoStore.GetInfoValue(InfoType.GP_NumPlayersDealtIn_Byte) / infoStore.GetInfoValue(InfoType.GP_NumTableSeats_Byte));
            networkInputs.Add("ActiveRatio", infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) / infoStore.GetInfoValue(InfoType.GP_NumPlayersDealtIn_Byte));
            networkInputs.Add("UnactedRatio", infoStore.GetInfoValue(InfoType.GP_NumUnactedPlayers_Byte) / infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte));

            #region gameStage

            decimal gameStagePreFlop   = 0;
            decimal gameStageFlop      = 0;
            decimal gameStageTurnRiver = 0;

            if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 0)
            {
                gameStagePreFlop = 1;
            }
            else if (infoStore.GetInfoValue(InfoType.GP_GameStage_Byte) == 1)
            {
                gameStageFlop = 1;
            }
            else
            {
                gameStageTurnRiver = 1;
            }

            networkInputs.Add("GameStagePreFlop", gameStagePreFlop);
            networkInputs.Add("GameStageFlop", gameStageFlop);
            networkInputs.Add("GameStageTurnRiver", gameStageTurnRiver);

            #endregion gameStage

            #region dealerDistance

            decimal dealerDistance     = ((infoStore.GetInfoValue(InfoType.GP_DealerDistance_Byte) - 1) / (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) - 1));
            decimal tablePositionEarly = 0;
            decimal tablePositionMid   = 0;
            decimal tablePositionLate  = 0;

            if (dealerDistance < (1.0m / 3.0m) && dealerDistance >= 0)
            {
                tablePositionEarly = 1;
            }
            else if (dealerDistance < (2.0m / 3.0m) && dealerDistance >= 0)
            {
                tablePositionMid = 1;
            }
            else if (dealerDistance <= 1.0m && dealerDistance >= 0)
            {
                tablePositionLate = 1;
            }
            else
            {
                throw new Exception("Dealer distance must be between 0 and 1.");
            }

            networkInputs.Add("TablePositionEarly", tablePositionEarly);
            networkInputs.Add("TablePositionMid", tablePositionMid);
            networkInputs.Add("TablePositionLate", tablePositionLate);

            #endregion dealerDistance

            networkInputs.Add("ImmediatePotOdds", infoStore.GetInfoValue(InfoType.BP_ImmediatePotOdds_Double));
            networkInputs.Add("ImpliedPotOdds", 0); //(infoStore.GetInfoValue(InfoType.IO_ImpliedPotOdds_Double))

            decimal potCommitted = 0;
            if (infoStore.GetInfoValue(InfoType.BP_PlayerMoneyInPot_Decimal) > infoStore.GetInfoValue(InfoType.BP_PlayerHandStartingStackAmount_Decimal) / 2)
            {
                potCommitted = 1;
            }

            networkInputs.Add("PotCommitted", potCommitted);
            networkInputs.Add("PotRatio", infoStore.GetInfoValue(InfoType.BP_PlayerMoneyInPot_Decimal) / infoStore.GetInfoValue(InfoType.BP_TotalPotAmount_Decimal));

            #region raise & check ratio

            decimal raiseRatio     = 0;
            decimal checkRatio     = 0;
            decimal totalNumRaises = infoStore.GetInfoValue(InfoType.BP_TotalNumRaises_Byte);
            decimal totalNumCalls  = infoStore.GetInfoValue(InfoType.BP_TotalNumCalls_Byte);
            decimal totalNumChecks = infoStore.GetInfoValue(InfoType.BP_TotalNumChecks_Byte);
            if (totalNumRaises + totalNumCalls + totalNumChecks > 0)
            {
                raiseRatio = totalNumRaises / (totalNumRaises + totalNumCalls + totalNumChecks);
                checkRatio = totalNumChecks / (totalNumRaises + totalNumCalls + totalNumChecks);
            }

            networkInputs.Add("RaiseRatio", raiseRatio);
            networkInputs.Add("CheckRatio", checkRatio);

            #endregion raise & check ratio

            decimal betsToCall         = infoStore.GetInfoValue(InfoType.BP_BetsToCall_Byte);
            decimal betsToCall0        = 0;
            decimal betsToCall1        = 0;
            decimal betsToCall2Greater = 0;

            if (betsToCall >= 2m)
            {
                betsToCall2Greater = 1;
            }
            else if (betsToCall == 1m)
            {
                betsToCall1 = 1;
            }
            else if (betsToCall == 0)
            {
                betsToCall0 = 1;
            }
            else
            {
                throw new Exception("This is impossible!!");
            }

            networkInputs.Add("BetsToCall0", betsToCall0);
            networkInputs.Add("BetsToCall1", betsToCall1);
            networkInputs.Add("BetsToCall2Greater", betsToCall2Greater);

            decimal betsLastRound1Greater = 0;
            if (infoStore.GetInfoValue(InfoType.BP_LastRoundBetsToCall_Byte) > 0)
            {
                betsLastRound1Greater = 1;
            }

            networkInputs.Add("BetsLastRound1Greater", betsLastRound1Greater);
            networkInputs.Add("CurrentCallAmountLarger4BB", infoStore.GetInfoValue(InfoType.BP_CurrentCallAmountLarger4BB));

            decimal raisedLastRound = 0;
            if (infoStore.GetInfoValue(InfoType.BP_RaisedLastRound_Bool) == 1)
            {
                raisedLastRound = 1;
            }

            networkInputs.Add("RaisedLastRound", raisedLastRound);

            decimal lastActionRaise = 0;
            if (infoStore.GetInfoValue(InfoType.BP_PlayerLastAction_Short) == 9)
            {
                lastActionRaise = 1;
            }

            networkInputs.Add("LastActionRaise", lastActionRaise);

            #region winPercentage

            networkInputs.Add("probCardsWin", infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage));
            networkInputs.Add("probCardsWeightedWin", infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage));

            //networkInputs.Add("probCardsWinOpponentWin",infoStore.GetInfoValue(InfoType.WR_CardsOnlyOpponentWinPercentage));
            networkInputs.Add("probCardsWinOpponentWin", (1 - infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage)) / (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) - 1));

            //networkInputs.Add("probCardsOpponentWeightedWin",infoStore.GetInfoValue(InfoType.WR_CardsOnlyOpponentWinPercentage));
            networkInputs.Add("probCardsOpponentWeightedWin", (1 - infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentage)) / (infoStore.GetInfoValue(InfoType.GP_NumActivePlayers_Byte) - 1));

            networkInputs.Add("probCardsOnlyWinPercentageLastRoundChange", infoStore.GetInfoValue(InfoType.WR_CardsOnlyWinPercentageLastRoundChange));

            #endregion winPercentage

            #region PAP

            networkInputs.Add("ProbRaiseToBotCheck", infoStore.GetInfoValue(InfoType.PAP_RaiseToBotCheck_Prob));
            networkInputs.Add("ProbRaiseToBotCall", infoStore.GetInfoValue(InfoType.PAP_RaiseToBotCall_Prob));
            networkInputs.Add("ProbFoldToBotCall", infoStore.GetInfoValue(InfoType.PAP_FoldToBotCall_Prob));
            networkInputs.Add("ProbRaiseToStealSuccess", infoStore.GetInfoValue(InfoType.PAP_RaiseToStealSuccess_Prob));

            #endregion PAP

            #endregion neuralInputs

            //We have 53 Inputs

            /*
             *  AAPocketPair, KKPocketPair,AKPocket,OtherHighPocketPair,OtherLowPocketPair,HoleCardTrouble,midConnectorPocket,lowConnectorPocket,suitedPocket,holeCardsFlushDraw;
             *  holeCardsOuterStraightDraw,holeCardsInnerStraightDraw,holeCardsTopOrTwoPair,holeCardsAorK,holeCards3KindOrBetter, aceOnBoard,kingOnBoard,tableAKQRatio;
             *  tableFlushPossible, tableStraightPossible,tableStraightDraw, tableFlushDraw // 22 Card Types
             *
             *  dealtInRatio, activeRatio,unactedRatio,stagePreFlop,stageFlop,stageTurnRiver,tablePositionEarly,tablePositionMid, tablePositionLate // 9 Game Types
             *
             *  imPotOdds,impliedPotOdds, potCommitted, potRatio, raiseRatio, checkRatio,betsToCall0,betsToCall1,betsToCall2Greater,betsLastRound1Greater,callAmountGreaterThan4BB
             *  raisedLastRound, lastActionRaise // 13 Bet Types
             *
             *  probCardsWin,probCardsWeightedWin,probCardsOpponentWin,probCardsWeightedOpponentWin,cardsWinPercentageLastRoundChange; // 5 Monto Carlo Types
             *
             *  probRaiseToBotCheck,probRaiseToBotCall,probFoldToBotCall,probRaiseToStealSuccess // 4 Prediction Types
             */

            NNDataSource aiDecisionData = new NNDataSource(networkInputs.Values.ToArray(), NeuralAINNModelV1.Input_Neurons);

            //decisionLogStr = aiDecisionData.ToString();

            //Get the network outputs
            double[] networkInputsArray = null;
            aiDecisionData.returnInput(ref networkInputsArray);
            double[] networkOutput = getPlayerNetworkPrediction(currentDecision.AiConfigStr, networkInputsArray);

            NeuralAiDecision decision = new NeuralAiDecision(networkOutput[0], networkOutput[1], networkOutput[2], networkOutput[3], networkOutput[4]);
            //Debug.Print("AI Decision - [{0}], [{1}], [{2}], [{3}], [{4}]", networkOutput[0], networkOutput[1], networkOutput[2], networkOutput[3], networkOutput[4]);

            decimal raiseToCallAmount          = (infoStore.GetInfoValue(InfoType.PAP_RaiseToCallAmount_Amount));
            decimal raiseToStealAmount         = (infoStore.GetInfoValue(InfoType.PAP_RaiseToStealAmount_Amount));
            decimal minCallAmount              = infoStore.GetInfoValue(InfoType.BP_MinimumPlayAmount_Decimal);
            decimal currentRoundBetAmount      = currentDecision.Cache.getPlayerCurrentRoundBetAmount(currentDecision.PlayerId);
            decimal playerRemainingStackAmount = currentDecision.Cache.getPlayerStack(currentDecision.PlayerId);

            if (decision.BotAction == 0)
            {
                return(new Play(PokerAction.Fold, 0, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else if (decision.BotAction == 1)
            {
                return(new Play(PokerAction.Call, currentDecision.Cache.getMinimumPlayAmount() - currentRoundBetAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else if (decision.BotAction == 2)
            {
                return(new Play(PokerAction.Raise, (decimal)raiseToCallAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else if (decision.BotAction == 3)
            {
                return(new Play(PokerAction.Raise, (decimal)raiseToStealAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else if (decision.BotAction == 4)
            {
                return(new Play(PokerAction.Raise, playerRemainingStackAmount + currentRoundBetAmount, 0, currentDecision.Cache.getCurrentHandId(), currentDecision.PlayerId, decisionLogStr, 0));
            }
            else
            {
                throw new Exception("Something has gone wery wong!");
            }
        }