/// <summary> /// Generates some random hands and verifies the result. /// </summary> /// <param name="gd"></param> /// <param name="repCount"></param> void Verify(GameDefinition gd, int playersCount, int repCount) { int seed = (int)DateTime.Now.Ticks; Console.WriteLine("Game: {0}, players: {1}, RNG seed: {2}", gd.Name, playersCount, seed); McDealer mcDealer = new McDealer(gd, seed); List <bool> isSharedDeal = new List <bool>(); int totalCardsCount = 0; for (int r = 0; r < gd.RoundsCount; ++r) { for (int i = 0; i < gd.PublicCardsCount[r] + gd.PrivateCardsCount[r]; ++i) { isSharedDeal.Add(false); } totalCardsCount += (gd.PublicCardsCount[r] + gd.PrivateCardsCount[r]) * playersCount; for (int i = 0; i < gd.SharedCardsCount[r]; ++i) { isSharedDeal.Add(true); } totalCardsCount += gd.SharedCardsCount[r]; } Assert.AreEqual(isSharedDeal.Count, mcDealer.HandSize); int [][] hands = new int[playersCount][].Fill(i => new int[isSharedDeal.Count]); for (int rep = 0; rep < repCount; ++rep) { mcDealer.NextDeal(hands); HashSet <int> distinctCards = new HashSet <int>(); for (int d = 0; d < isSharedDeal.Count; ++d) { if (isSharedDeal[d]) { // Make sure all players has the same card. for (int p = 0; p < playersCount; ++p) { Assert.AreEqual(hands[0][d], hands[p][d]); } Assert.IsFalse(distinctCards.Contains(hands[0][d])); distinctCards.Add(hands[0][d]); } else { // Add cards of each player to the set. If multiple players have // this card or it was dealt before, we will get an error. for (int p = 0; p < playersCount; ++p) { Assert.IsFalse(distinctCards.Contains(hands[p][d])); distinctCards.Add(hands[p][d]); } } } Assert.AreEqual(totalCardsCount, distinctCards.Count); } }
void Benchmark(GameDefinition gd, int playersCount, int repCount) { int seed = (int)DateTime.Now.Ticks; McDealer mcDealer = new McDealer(gd, seed); int[][] hands = new int[playersCount][].Fill(i => new int[mcDealer.HandSize]); int checksum = 0; DateTime startTime = DateTime.Now; for (int rep = 0; rep < repCount; ++rep) { mcDealer.NextDeal(hands); checksum += hands[0][0]; } double time = (DateTime.Now - startTime).TotalSeconds; Console.WriteLine("Game: {0}, players: {1}, repetitions: {2:0,0}, time: {3:0.000} s, {4:0,0} deals/s", gd.Name, playersCount, repCount, time, repCount / time); }
/// <summary> /// Now do fictious play repeatedly. /// </summary> private void DoIterations() { DateTime start = DateTime.Now; // Start from the player with minimal iteration count, if equal, start from 0. int minIterCount = int.MaxValue; for (int p = 0; p < _playersCount; ++p) { if (minIterCount > IterationCounts[p]) { _heroPos = p; minIterCount = IterationCounts[p]; } } for (CurrentIterationCount = 0; ; _heroPos = (_heroPos + 1) % _playersCount) { if (MaxIterationCount >= 0 && CurrentIterationCount >= MaxIterationCount) { break; } IterationCounts[_heroPos]++; CurrentIterationCount++; _mcDealer.NextDeal(new int[][] { _hands[_heroPos] }); DealOppCardsAndCalculateGV(); BestResponse(); if (CurrentIterationCount >= _playersCount) { // Calculate espsilon as soon as we have done SBR for each player at least once. CurrentEpsilon = 0; for (int p = 0; p < _playersCount; ++p) { CurrentEpsilon += LastBrValues[p]; } CurrentEpsilon = Math.Abs(CurrentEpsilon); UpdateEpsilonLog(); } else { CurrentEpsilon = Double.MaxValue; } _iterationTime = (DateTime.Now - start).TotalSeconds; if (IsIterationVerbose()) { PrintIterationStatus(Console.Out); } if (CheckExitCriteria()) { break; } } if (IsVerbose) { PrintIterationStatus(Console.Out); } }
/// <summary> /// Generate an internal chance tree by MC sampling. /// </summary> /// <param name="chanceAbstractions">An array of chance abstractions</param> /// <param name="areAbstractionsEqual">If the absractions are equal, /// one MC sample will update multiple nodes of the chance tree.</param> /// <param name="samplesCount">Number of samples. If equal absractions are used, /// less MC samples will be actually done to reach the specified numbers of updates.</param> /// <param name="rngSeed">RNG seed.</param> /// <param name="feedback">User feedback callback.</param> /// <returns></returns> public static Tree Generate(GameDefinition gd, IChanceAbstraction[] chanceAbstractions, bool areAbstractionsEqual, long samplesCount, int rngSeed, FeedbackDelegate feedback) { if (chanceAbstractions.Length != 2) { throw new ArgumentOutOfRangeException("Only heads up games are supported now"); } McDealer mcDealer = new McDealer(gd, rngSeed); int [][] hands = new int[chanceAbstractions.Length][].Fill(i => new int[mcDealer.HandSize]); int[] handSizes = gd.GetHandSizes(); Tree tree = new Tree { PlayersCount = chanceAbstractions.Length, RoundsCount = gd.RoundsCount, SourceInfo = GetSourceInfo(gd, chanceAbstractions) }; Node root = tree.Root; byte[] abstrCards = new byte[gd.RoundsCount * chanceAbstractions.Length]; uint[] ranks = new uint[chanceAbstractions.Length]; Int64 samplesDone; int updateCount = areAbstractionsEqual ? 2 : 1; for (samplesDone = 0; samplesDone < samplesCount; samplesDone += updateCount) { if (feedback != null && (samplesDone % FEEDBACK_PERIOD == 0)) { if (!feedback(samplesDone)) { break; } } mcDealer.NextDeal(hands); gd.GameRules.Showdown(gd, hands, ranks); int c = 0; for (int r = 0; r < gd.RoundsCount; ++r) { for (int p = 0; p < chanceAbstractions.Length; ++p) { int abstrCard = chanceAbstractions[p].GetAbstractCard(hands[p], handSizes[r]); if (abstrCard < byte.MinValue || abstrCard > byte.MaxValue) { throw new ApplicationException(string.Format("Abstract card {0} out of byte range", abstrCard)); } abstrCards[c++] = (byte)abstrCard; } } for (int u = 0; ;) { LeafT[] leaves = tree.GetLeavesByCards(abstrCards); int lastCard = abstrCards[abstrCards.Length - 1]; leaves[lastCard].Update(ranks); if (++u == updateCount) { break; } // If the abstractions are equal, we can update another node // by permuting cards and results // Note: implemented for 2 players only for (c = 0; c < abstrCards.Length; c += 2) { ShortSequence.Swap(ref abstrCards[c], ref abstrCards[c + 1]); } ShortSequence.Swap(ref ranks[0], ref ranks[1]); } } tree.SamplesCount = (UInt64)samplesDone; tree.UpdateDescription(); return(tree); }