public virtual void OnGameBegin(string gameString) { _gameRecord = new GameRecord(gameString); _pos = _gameRecord.FindPositionByName(_name); if (_checkBlinds) { for (int p = 0; p < _gameRecord.Players.Count; ++p) { _curStrNodeIdx = p + 1; StrategyTreeNode stNode = new StrategyTreeNode(); _strategies[_pos].GetNode(p + 1, &stNode); double strBlind = stNode.Amount; double grBlind = _gameRecord.Players[p].Blind; if (!FloatingPoint.AreEqual(grBlind, strBlind, AMOUNT_EPSILON)) { throw new ApplicationException( String.Format("{0}: blind size mismatch: game: {1}, strategy: {2}", GetBotStateDiagText(), grBlind, strBlind)); } } } if (_gameRecord.Actions.Count != 0) { throw new ApplicationException( String.Format("{0}: action count must be zero in OnGameBegin, game string '{1}'", GetBotStateDiagText(), gameString)); } _curStrNodeIdx = _gameRecord.Players.Count; _processedActionsCount = 0; _lastAbsStrProbab = 1.0; _gameState = new GameState(_gameRecord, null); log.InfoFormat("{0} OnGameBegin() pos {1}", _name, _pos); }
/// <summary> /// Returns a descriptive text describing the current state of the bot. /// It contains important parameters that are essential to understand the context of almost every error. /// It should be use in exceptions and error traces. /// </summary> /// <returns></returns> string GetBotStateDiagText() { StrategyTreeNode stNode = new StrategyTreeNode(); _strategies[_pos].GetNode(_curStrNodeIdx, &stNode); return(String.Format("Player: {0}, pos: {1}, str. node: {2}({3})", _name, _pos, _curStrNodeIdx, stNode.ToStrategicString(null))); }
public virtual PokerAction OnActionRequired(string gameString) { log.InfoFormat("{0} OnActionRequired", _name); _gameRecord = new GameRecord(gameString); ProcessActions(); int chBegin, movesCount; _strIndexes[_pos].GetChildrenBeginIdxAndCount(_curStrNodeIdx, out chBegin, out movesCount); double[] probabs = new double[movesCount]; double sumProbab = 0; for (int c = 0; c < movesCount; ++c) { int stNodeIdx = _strIndexes[_pos].GetChildIdx(chBegin + c); StrategyTreeNode stNode = new StrategyTreeNode(); _strategies[_pos].GetNode(stNodeIdx, &stNode); // Verify we are in the correct position. // This is proven to be very helpful in testing. if (!stNode.IsPlayerAction(_pos)) { throw new ApplicationException(String.Format("{0}: expected strategy child: player action for pos {1}, but was: '{2}'", GetBotStateDiagText(), _pos, stNode.ToStrategicString(null))); } probabs[c] = stNode.Probab; sumProbab += probabs[c]; } try { // Convert to relative probability and ignore if necessary for (int c = 0; c < probabs.Length; ++c) { probabs[c] /= sumProbab; if (probabs[c] < _relProbabIgnoreLevel) { probabs[c] = 0; } } _moveSelector.SetWeights(probabs); } catch (Exception e) { // If there are problems with 0-probablities, add more info to it. throw new ApplicationException(String.Format("{0} : see inner exception", GetBotStateDiagText()), e); } int moveIdx = _moveSelector.Next(); Debug.Assert(probabs[moveIdx] > 0, "0-probabs must not occur"); int nextStrNode = _strIndexes[_pos].GetChildIdx(chBegin + moveIdx); PokerAction move = ConvertStrActionToPokerAction(nextStrNode); log.InfoFormat("{0} OnActionRequired() returns {1}", _name, move); return(move); }
/// <summary> /// Process new poker actions received from the server. /// </summary> private void ProcessActions() { for (int a = _processedActionsCount; a < _gameRecord.Actions.Count; ++a, _processedActionsCount++) { PokerAction pa = _gameRecord.Actions[a]; log.InfoFormat("{0} process {1} of pos {2}", _name, pa.Kind, pa.Position); ProcessAction(pa); StrategyTreeNode stNode = new StrategyTreeNode(); _strategies[_pos].GetNode(_curStrNodeIdx, &stNode); if (stNode.IsPlayerAction(_pos)) { _lastAbsStrProbab = stNode.Probab; } } }
public void Test_Properties() { StrategyTreeNode n = new StrategyTreeNode(); n.IsDealerAction = false; Assert.IsFalse(n.IsDealerAction); for (int p = 0; p < 7; ++p) { n.Position = p; Assert.AreEqual(p, n.Position); } n.Amount = 0; Assert.AreEqual(0, n.Amount); n.Amount = 2000; Assert.AreEqual(2000, n.Amount); for (double a = 0; a <= 2000.0; a += 0.54321) { double amount = Math.Round(a, 5); n.Amount = amount; Assert.AreEqual(amount, n.Amount); } n.Probab = 0; Assert.AreEqual(0, n.Probab); n.Probab = 0.123; Assert.AreEqual(0.123, n.Probab); n.Probab = 1; Assert.AreEqual(1, n.Probab); n.IsDealerAction = true; Assert.IsTrue(n.IsDealerAction); for (int p = 0; p < 7; ++p) { n.Position = p; Assert.AreEqual(p, n.Position); } for (int c = 0; c <= 1000; ++c) { n.Card = c; Assert.AreEqual(c, n.Card); } }
private PokerAction ConvertStrActionToPokerAction(int nextStNodeIdx) { StrategyTreeNode nextStNode = new StrategyTreeNode(); _strategies[_pos].GetNode(nextStNodeIdx, &nextStNode); if (!nextStNode.IsPlayerAction(_pos)) { throw new ApplicationException(String.Format("{0} : wrong move: {1}", GetBotStateDiagText(), nextStNode.ToStrategicString(null))); } double amount = nextStNode.Amount; double raiseAmount = _gameState.Players[_pos].Bet + amount - _gameState.Bet; if (raiseAmount > 0) { return(PokerAction.r(_pos, raiseAmount)); } if (amount == 0 && _gameState.Players[_pos].Bet < _gameState.Bet) { return(PokerAction.f(_pos)); } return(PokerAction.c(_pos)); }
/// <summary> /// Processes each poker action received from the server. /// Updates the game state and moves to the next node in our strategy. /// </summary> /// <param name="pa"></param> void ProcessAction(PokerAction pa) { int chBegin, chCount; _strIndexes[_pos].GetChildrenBeginIdxAndCount(_curStrNodeIdx, out chBegin, out chCount); int nextStrNodeIdx = -1; if (pa.IsDealerAction()) { _gameState.UpdateByAction(pa, null); if (pa.Position >= 0 && pa.Position != _pos) { // This is a deal to an opponent // We can skip it for games without public cards (and it is very unlikely we will have some // in the future). return; } int[] hand = _deckDescr.GetIndexes(_gameState.Players[_pos].Hand); int abstrCard = _chanceAbsrtractions[_pos].GetAbstractCard(hand, hand.Length); for (int c = 0; c < chCount; ++c) { int stNodeIdx = _strIndexes[_pos].GetChildIdx(chBegin + c); StrategyTreeNode stNode = new StrategyTreeNode(); _strategies[_pos].GetNode(stNodeIdx, &stNode); if (!stNode.IsDealerAction) { throw new ApplicationException( String.Format("{0} : expected strategy child: dealer action but was: '{1}'", GetBotStateDiagText(), stNode.ToStrategicString(null))); } if (stNode.Card == abstrCard) { nextStrNodeIdx = stNodeIdx; goto searchFinished; } } } else // Player action { double inPotBefore = _gameState.Players[pa.Position].InPot; _gameState.UpdateByAction(pa, null); double inPotAfter = _gameState.Players[pa.Position].InPot; double actualAmount = inPotAfter - inPotBefore; double bestAmount = double.MinValue; for (int c = 0; c < chCount; ++c) { int stNodeIdx = _strIndexes[_pos].GetChildIdx(chBegin + c); StrategyTreeNode stNode = new StrategyTreeNode(); _strategies[_pos].GetNode(stNodeIdx, &stNode); if (!stNode.IsPlayerAction(pa.Position)) { throw new ApplicationException( String.Format("{0} : expected strategy child: player action pos {1} but was: '{2}'", GetBotStateDiagText(), pa.Position, stNode.ToStrategicString(null))); } double amount = stNode.Amount; switch (_amountSearchMethod) { case AmountSearchMethod.Equal: if (FloatingPoint.AreEqual(amount, actualAmount, AMOUNT_EPSILON)) { nextStrNodeIdx = stNodeIdx; goto searchFinished; } break; case AmountSearchMethod.Closest: if (Math.Abs(amount - actualAmount) < Math.Abs(bestAmount - actualAmount)) { bestAmount = amount; nextStrNodeIdx = stNodeIdx; } break; } } } searchFinished: if (nextStrNodeIdx == -1) { throw new ApplicationException( String.Format("{0} : cannot find strategy action for poker action '{1}'", GetBotStateDiagText(), pa.ToGameString())); } _curStrNodeIdx = nextStrNodeIdx; }
void Initialize() { _playerInfoProps = new Props(); string strDir = _creationParams.Get("StrategyDir"); _playerInfoProps.Set("StrategyDir", strDir); string propsFile = Path.Combine(strDir, "props.xml"); Props props = XmlSerializerExt.Deserialize <Props>(propsFile); int rngSeed = int.Parse(_creationParams.GetDefault("RngSeed", "0")); if (rngSeed == 0) { rngSeed = (int)DateTime.Now.Ticks; } _checkBlinds = bool.Parse(_creationParams.GetDefault("CheckBlinds", "true")); string amountCompareMethodText = _creationParams.GetDefault("AmountSearchMethod", "Equal"); if (amountCompareMethodText == "Equal") { _amountSearchMethod = AmountSearchMethod.Equal; } else if (amountCompareMethodText == "Closest") { _amountSearchMethod = AmountSearchMethod.Closest; } else { throw new ApplicationException(string.Format("Unknown amount compare method {0}", amountCompareMethodText)); } _relProbabIgnoreLevel = double.Parse(_creationParams.GetDefault("RelProbabIgnoreLevel", "0.0"), CultureInfo.InvariantCulture); _playerInfoProps.Set("RelProbabIgnoreLevel", _relProbabIgnoreLevel.ToString()); // Use MersenneTwister because it is under our control on all platforms and // is probably better than System.Random. Random underlyingRng = new MersenneTwister(rngSeed); _moveSelector = new DiscreteProbabilityRng(underlyingRng); _playerInfoProps.Set("RngSeed", rngSeed.ToString()); _deckDescr = XmlSerializerExt.Deserialize <DeckDescriptor>(props.Get("DeckDescriptor")); _chanceAbsrtractions = new IChanceAbstraction[0]; // Create chance abstractions for (int pos = 0; ; pos++) { string caPropName = String.Format("ChanceAbstraction-{0}", pos); string relFileName = props.Get(caPropName); if (string.IsNullOrEmpty(relFileName)) { break; } Array.Resize(ref _chanceAbsrtractions, _chanceAbsrtractions.Length + 1); string absFileName = Path.Combine(strDir, relFileName); _chanceAbsrtractions[pos] = ChanceAbstractionHelper.CreateFromPropsFile(absFileName); _playerInfoProps.Set(caPropName + ".Name", _chanceAbsrtractions[pos].Name); } Dictionary <string, int> fileToPos = new Dictionary <string, int>(); _strategies = new StrategyTree[0]; _strIndexes = new UFTreeChildrenIndex[0]; // Load strategies, reuse if file is the same for (int pos = 0; ; pos++) { string strPropName = String.Format("Strategy-{0}", pos); string relFileName = props.Get(strPropName); if (string.IsNullOrEmpty(relFileName)) { break; } Array.Resize(ref _strategies, _strategies.Length + 1); Array.Resize(ref _strIndexes, _strIndexes.Length + 1); string absFileName = Path.Combine(strDir, relFileName); int existingPos; if (fileToPos.TryGetValue(absFileName, out existingPos)) { _strategies[pos] = _strategies[existingPos]; _strIndexes[pos] = _strIndexes[existingPos]; } else { fileToPos.Add(absFileName, pos); _strategies[pos] = StrategyTree.ReadFDA <StrategyTree>(absFileName); _strIndexes[pos] = new UFTreeChildrenIndex(_strategies[pos], Path.Combine(strDir, "strategy-idx.dat"), false); } _playerInfoProps.Set(strPropName + ".Version", _strategies[pos].Version.ToString()); } // Read blinds for (int strPos = 0; strPos < _strategies.Length; ++strPos) { StringBuilder sb = new StringBuilder(); for (int playerPos = 0; playerPos < _strategies.Length; ++playerPos) { StrategyTreeNode stNode = new StrategyTreeNode(); _strategies[strPos].GetNode(playerPos + 1, &stNode); sb.AppendFormat("{0:0.000000 }", stNode.Amount); } _playerInfoProps.Set("Blinds." + strPos.ToString(), sb.ToString()); } }