Exemple #1
0
        public void Test_CreateCt()
        {
            int oppCount = 2;
            var pockets  = PocketHelper.GetAllPockets();
            //var pockets = new HePocketKind[] { HePocketKind._AA, HePocketKind._76s, HePocketKind._72o };
            var pocketDist = PocketHelper.GetProbabDistr(pockets);

            double[,] ptPeMax = MultiplayerPocketProbability.ComputePreferenceMatrixPeMax(pockets);


            string     xmlAt = Props.Global.Expand("${bds.DataDir}ai.pkr.holdem.learn/nlpf-1.xml");
            ActionTree at    = XmlToActionTree.Convert(xmlAt);

            string[] actionLabels = CreateActionLabels(at, 0);
            Dictionary <string, int> actionLabelToId = new Dictionary <string, int>();

            for (int i = 0; i < actionLabels.Length; ++i)
            {
                actionLabelToId.Add(actionLabels[i], i);
            }

            double[]   oppDist = MultiplayerPocketProbability.Compute(oppCount, pocketDist, ptPeMax);
            ChanceTree ct      = PreflopStrategy.CreateCt(pockets, oppDist);


            //GameDefinition gd = XmlSerializerExt.Deserialize<GameDefinition>(Props.Global.Expand("${bds.DataDir}ai.pkr.metastrategy/${0}", "kuhn.gamedef.xml"));
            //at = CreateActionTreeByGameDef.Create(gd);
            //ct = CreateChanceTreeByGameDef.Create(gd);

            VisChanceTree.Show(ct, Path.Combine(_outDir, "ct.gv"));
            VisActionTree.Show(at, Path.Combine(_outDir, "at.gv"));

            StrategyTree[] st = SolveAndVerifyVerifySolution(at, ct, 0.001, false);

            double[,] fs = FlattenStrategy(st[0], 0, pockets.Length, actionLabelToId);

            Console.WriteLine("Result for opponent count: {0}", oppCount);
            Console.Write("{0,4}", oppCount);
            foreach (string al in actionLabels)
            {
                Console.Write("{0,20}", al);
            }
            Console.WriteLine();
            int raiseCount = 0;

            for (int c = 0; c < pockets.Length; ++c)
            {
                Console.Write("{0,4}", HePocket.KindToString(pockets[c]));
                for (int j = 0; j < actionLabels.Length; ++j)
                {
                    Console.Write("{0,20}", Math.Round(fs[c, j] * 100, 0));
                }
                if (fs[c, actionLabels.Length - 1] > 0.9)
                {
                    raiseCount += HePocket.KindToRange(pockets[c]).Length;
                }
                Console.WriteLine();
            }
            Console.WriteLine("Raise count: {0}", raiseCount);
        }
        void Initialize()
        {
            DateTime startTime = DateTime.Now;

            _at = ActionTree.Read <ActionTree>(ActionTreeFile);
            if (IsVerbose)
            {
                Console.WriteLine("Action tree: {0}", _at.Version.ToString());
            }
            _init             = new InitData(this);
            _playersCount     = _at.PlayersCount;
            _epsilonLog       = new List <EpsilonLogEntry>();
            _snapshotSwitcher = new SnapshotSwitcher(OutputPath, GetSnapshotHeaderFileName(), SnapshotsCount);
            _curSnapshotInfo  = new SnapshotInfo(_snapshotSwitcher.CurrentSnapshotPath, _playersCount);
            IterationCounts   = new int[_playersCount];
            LastBrValues      = new double[_playersCount];
            _ptExt            = new Node[_playersCount][];

            _rng       = new System.Random(RngSeed);
            _mcDealer  = new McDealer(GameDef, _rng);
            _hands     = new int[_playersCount][].Fill(i => new int[_mcDealer.HandSize]);
            _handSizes = GameDef.GetHandSizes();
            _oppGv     = new double[_at.NodesCount];

            bool isNewSnapshot = !_snapshotSwitcher.IsSnapshotAvailable;

            if (isNewSnapshot)
            {
                CreateNewSnapshot();
            }
            //LoadSnapshot();

            CreatePlayerTrees();

            for (int p = 0; p < _playersCount; ++p)
            {
                if (TraceDir != null)
                {
                    Vis.Show(this, p, GetTraceFileName(p, "tree", "init-pt", "gv"));
                }
            }
            if (TraceDir != null)
            {
                VisChanceTree.Show(_init.PlayerCt, GetTraceFileName(0, "pct", "", "gv"));
            }

            PrintInitDone();

            // Clean-up
            _init = null;
            double time = (DateTime.Now - startTime).TotalSeconds;

            if (IsVerbose)
            {
                Console.WriteLine("Initialization done in {0:0.0} s", time);
            }
        }
        /// <summary>
        /// Runs FictitiousPlay with the specified parameters.
        /// Some parameters are set by default (e.g. verbosity), the caller has a chance to overwrite them
        /// using Configure delegate.
        /// </summary>
        public StrategyTree[] Solve(ActionTree at, ChanceTree ct)
        {
            int playersCount = 2;

            DirectoryExt.Delete(BaseDir);
            Directory.CreateDirectory(BaseDir);

            string inputDir = Path.Combine(BaseDir, "input");

            Directory.CreateDirectory(inputDir);
            string traceDir = Path.Combine(BaseDir, "trace");

            string chanceTreeFile = Path.Combine(inputDir, "ct.dat");
            string actionTreeFile = Path.Combine(inputDir, "at.dat");

            ct.Write(chanceTreeFile);
            at.Write(actionTreeFile);

            if (VisualizeTrees)
            {
                VisActionTree.Show(at, actionTreeFile + ".gv");
                VisChanceTree.Show(ct, chanceTreeFile + ".gv");
            }

            Solver.ChanceTreeFile     = chanceTreeFile;
            Solver.EqualCa            = false;
            Solver.ActionTreeFile     = actionTreeFile;
            Solver.OutputPath         = BaseDir;
            Solver.SnapshotsCount     = 2;
            Solver.Epsilon            = Epsilon;
            Solver.ThreadsCount       = 6;
            Solver.IsVerbose          = true;
            Solver.IterationVerbosity = 10000;
            Solver.MaxIterationCount  = 1000000000;

            if (Configure != null)
            {
                Configure(Solver);
            }
            Solver.Solve();

            StrategyTree[] eqStrategies = new StrategyTree[playersCount];

            for (int p = 0; p < playersCount; ++p)
            {
                string fileName = Solver.CurrentSnapshotInfo.StrategyFile[p];
                eqStrategies[p] = StrategyTree.Read <StrategyTree>(fileName);
                if (VisualizeTrees)
                {
                    VisStrategyTree.Show(eqStrategies[p], fileName + ".gv");
                }
            }

            return(eqStrategies);
        }
        /// <summary>
        /// This functions simulate a typical use case: create trees in many runs of MC sampling, write them, read again
        /// and merge into the master tree. The master tree is than verified.
        /// </summary>
        private void GenerateAndVerifyCT(string name, GameDefinition gd, IChanceAbstraction[] chanceAbstractions, bool areAbstractionsEqual, int samplesCount, int runsCount, double avRelProbabEps, double avPotShareEps, double eqValEps, bool visualize)
        {
            CtMcGen.Tree masterTree = new CtMcGen.Tree();

            int rngSeed = (int)DateTime.Now.Ticks;

            for (int run = 0; run < runsCount; ++run)
            {
                CtMcGen.Tree runTree  = CtMcGen.Generate(gd, chanceAbstractions, areAbstractionsEqual, samplesCount, rngSeed, null);
                string       fileName = Path.Combine(_outDir, String.Format("{0}-{1}-ct.dat", gd.Name, name));
                runTree.Write(fileName);
                masterTree.Read(fileName);

                // Do not use the timer anymore because the tests are too fast.
                rngSeed++;
            }

            ChanceTree actCt = masterTree.ConvertToChanceTree();

            VisChanceTree.Show(actCt, Path.Combine(_outDir, String.Format("{0}-{1}-ct.gv", gd.Name, name)));
            VerifyChanceTree.VerifyS(actCt);
            ChanceTree expCt = CreateChanceTreeByAbstraction.CreateS(gd, chanceAbstractions);

            Assert.AreEqual(expCt.PlayersCount, actCt.PlayersCount);
            CompareChanceTrees cmp = new CompareChanceTrees();

            cmp.IsVerbose = visualize;
            cmp.Output    = Console.Out;
            cmp.Compare(expCt, actCt);
            VisChanceTree.Show(expCt, Path.Combine(_outDir, String.Format("{0}-{1}-ct-exp.gv", gd.Name, name)));
            Assert.Less(cmp.AverageRelProbabDiff, avRelProbabEps);
            for (int p = 0; p < chanceAbstractions.Length; ++p)
            {
                Assert.Less(cmp.AveragePotShareDiff[p], avRelProbabEps);
            }

            ActionTree at = CreateActionTreeByGameDef.Create(gd);

            double [] actEqValues, expEqValues;
            EqLp.Solve(at, actCt, out actEqValues);
            EqLp.Solve(at, expCt, out expEqValues);
            for (int p = 0; p < chanceAbstractions.Length; ++p)
            {
                if (visualize)
                {
                    Console.WriteLine("Eq pos: {0} exp: {1} act: {2}", p, expEqValues[p], actEqValues[p]);
                }
                Assert.AreEqual(expEqValues[p], actEqValues[p], eqValEps);
            }
            if (visualize)
            {
                Console.WriteLine();
            }
        }
Exemple #5
0
        private GameValue Solve(TestParams testParams, bool visualize, ConfigureSolver configureSolver)
        {
            if (visualize)
            {
                VisActionTree.Show(testParams.ActionTree,
                                   Path.Combine(_outDir, String.Format("{0}-at.gv", testParams.Name)));
                VisChanceTree.Show(testParams.ChanceTree,
                                   Path.Combine(_outDir, String.Format("{0}-ct.gv", testParams.Name)));
                for (int p = 0; p < testParams.StrategyTrees.Length; ++p)
                {
                    VisStrategyTree.Show(testParams.StrategyTrees[p],
                                         Path.Combine(_outDir, string.Format("{0}-st-{1}.gv", testParams.Name, p)));
                }
            }

            // Make sure input is correct.
            for (int p = 0; p < testParams.ChanceTree.Nodes[0].Position; ++p)
            {
                string errorText;
                Assert.IsTrue(VerifyAbsStrategy.Verify(testParams.StrategyTrees[p], p, 0.000001, out errorText), errorText);
            }

            GameValue gv = new GameValue {
                ChanceTree = testParams.ChanceTree, ActionTree = testParams.ActionTree, Strategies = testParams.StrategyTrees
            };

            gv.PrepareVis = visualize;

            if (configureSolver != null)
            {
                configureSolver(gv);
            }

            gv.Solve();

            if (visualize)
            {
                for (int p = 0; p < testParams.ChanceTree.PlayersCount; ++p)
                {
                    GameValue.Vis.Show(gv, p, Path.Combine(_outDir, String.Format("{0}-{1}-val.gv", testParams.Name, p)));
                }
            }

            Assert.AreEqual(2, gv.Values.Length);
            for (int p = 0; p < testParams.ChanceTree.PlayersCount; ++p)
            {
                Console.WriteLine("Game value pos {0}: {1}", p, gv.Values[p]);
                Assert.AreEqual(testParams.ExpectedResult[p], gv.Values[p], testParams.Epsilon);
            }
            return(gv);
        }
        public void Test_LeducHe()
        {
            GameDefinition gd = XmlSerializerExt.Deserialize <GameDefinition>(
                Props.Global.Expand("${bds.DataDir}ai.pkr.metastrategy/leduc-he.gamedef.xml"));

            ChanceTree ct = CreateChanceTreeByGameDef.Create(gd);

            using (TextWriter w = new StreamWriter(File.Open(Path.Combine(_outDir, "leduc-ct.gv"), FileMode.Create)))
            {
                VisChanceTree vis = new VisChanceTree {
                    Output = w, CardNames = gd.DeckDescr.CardNames
                };
                vis.Show(ct);
            }
        }
Exemple #7
0
        private void Solve(TestParams testParams, bool visualize, ConfigureSolver configureSolver)
        {
            if (visualize)
            {
                VisActionTree.Show(testParams.ActionTree,
                                   Path.Combine(_outDir, String.Format("{0}-at.gv", testParams.Name)));
                VisChanceTree.Show(testParams.ChanceTree,
                                   Path.Combine(_outDir, String.Format("{0}-ct.gv", testParams.Name)));
            }

            StrategyTree [] eqStrategies = new StrategyTree[testParams.ChanceTree.PlayersCount];

            string error;

            for (int heroPos = 0; heroPos < testParams.ChanceTree.PlayersCount; ++heroPos)
            {
                // Create and configure EqLp solver
                EqLp solver = new EqLp
                {
                    HeroPosition = heroPos,
                    ChanceTree   = testParams.ChanceTree,
                    ActionTree   = testParams.ActionTree,
                };

                if (configureSolver != null)
                {
                    configureSolver(solver);
                }

                // Solve EqLp
                solver.Solve();
                eqStrategies[heroPos] = solver.Strategy;

                if (visualize)
                {
                    VisStrategyTree.Show(solver.Strategy,
                                         Path.Combine(_outDir, string.Format("{0}-eq-{1}.gv", testParams.Name, heroPos)));
                }

                // Verify the eq value and strategy
                Assert.AreEqual(testParams.ExpectedResult[heroPos], solver.Value, testParams.Epsilon, "Wrong eq value");
                Assert.IsTrue(VerifyAbsStrategy.Verify(solver.Strategy, solver.HeroPosition, 1e-7, out error), error);
            }
            // Verify eq, use another (better) epsilon because EqLp and VerifyEq have better precision
            // than most of the reference game solvers like OCFR.
            Assert.IsTrue(VerifyEq.Verify(testParams.ActionTree, testParams.ChanceTree, eqStrategies, 1e-7, out error), error);
        }
Exemple #8
0
        public void Test_Eq()
        {
            GameDefinition gd = XmlSerializerExt.Deserialize <GameDefinition>("kuhn8.gamedef.xml");
            ChanceTree     ct = CreateChanceTreeByGameDef.Create(gd);

            VisChanceTree.Show(ct, "kuhn8-ct.gv");
            ActionTree at = CreateActionTreeByGameDef.Create(gd);

            VisActionTree.Show(at, "kuhn8-at.gv");



            double[]        values;
            StrategyTree [] strategies = EqLp.Solve(at, ct, out values);
            Console.WriteLine("Kuhn8 eq values: {0}, {1}", values[0], values[1]);
            VisStrategyTree.Show(strategies[0], "kuhn8-eq-0.gv");
            VisStrategyTree.Show(strategies[1], "kuhn8-eq-1.gv");

            // Make strategy for T same as for Q
            //strategies[0].Nodes[strategies[0].FindNode("0d0 0p0", null)].Probab = 0.5;
            //strategies[0].Nodes[strategies[0].FindNode("0d0 0p0 1p1 0p1", null)].Probab = 0.5;
            //strategies[0].Nodes[strategies[0].FindNode("0d0 0p1", null)].Probab = 0.5;


            // Make strategy for Q same as for T
            strategies[0].Nodes[strategies[0].FindNode("0d2 0p0", null)].Probab         = 0;
            strategies[0].Nodes[strategies[0].FindNode("0d2 0p0 1p1 0p1", null)].Probab = 0;
            strategies[0].Nodes[strategies[0].FindNode("0d2 0p1", null)].Probab         = 1;

            VisStrategyTree.Show(strategies[0], "kuhn8-eq-0-adj.gv");


            Br br = new Br {
                ActionTree = at, ChanceTree = ct, HeroPosition = 1
            };

            br.Strategies = new StrategyTree[] { strategies[0], null };
            br.Solve();
            StrategyTree br0 = br.Strategies[1];

            Console.WriteLine("Br against pos 0: {0}", br.Value);
            VisStrategyTree.Show(strategies[1], "kuhn8-eq-br-0.gv");
        }
        public void Test_Kuhn()
        {
            GameDefinition gd = XmlSerializerExt.Deserialize <GameDefinition>(
                Props.Global.Expand("${bds.DataDir}ai.pkr.metastrategy/kuhn.gamedef.xml"));

            IChanceAbstraction[] abstractions = new IChanceAbstraction [] { new KuhnChanceAbstraction(), new KuhnChanceAbstraction() };

            ChanceTree ct  = CreateChanceTreeByGameDef.Create(gd);
            ChanceTree act = CreateChanceTreeByAbstraction.CreateS(gd, abstractions);

            VerifyChanceTree.VerifyS(act);

            VisChanceTree.Show(act, Path.Combine(_outDir, gd.Name + "-abct.gv"));

            UInt16[] activePlayersOne = ActivePlayers.Get(gd.MinPlayers, 1, 1);
            UInt16[] activePlayersAll = ActivePlayers.Get(gd.MinPlayers, 1, gd.MinPlayers);
            int      maxDepth         = gd.RoundsCount * gd.MinPlayers;

            Assert.AreEqual(act.PlayersCount, gd.MinPlayers);
            Assert.AreEqual(ct.NodesCount, act.NodesCount);
            double[] expPotShares = new double[gd.MinPlayers];
            double[] actPotShares = new double[gd.MinPlayers];
            for (int i = 0; i < ct.NodesCount; ++i)
            {
                Assert.AreEqual(ct.Nodes[i].Position, act.Nodes[i].Position);
                Assert.AreEqual(ct.Nodes[i].Card, act.Nodes[i].Card); // Kuhn abstraction has the same card
                Assert.AreEqual(ct.Nodes[i].Probab, act.Nodes[i].Probab);
                if (i == 0)
                {
                    continue;
                }
                UInt16[] activePlayers = ct.GetDepth(i) == maxDepth ? activePlayersAll : activePlayersOne;
                foreach (UInt16 ap in activePlayers)
                {
                    ct.Nodes[i].GetPotShare(ap, expPotShares);
                    act.Nodes[i].GetPotShare(ap, actPotShares);
                    Assert.AreEqual(expPotShares, actPotShares, String.Format("Node: {0}, ap: {1}", i, ap));
                }
            }
        }
        public void Test_CallKK()
        {
            string outDir = Path.Combine(_outDir, "call-KK");

            Directory.CreateDirectory(outDir);
            HePocketKind[] sbPockets = new HePocketKind[] { HePocketKind._AA, HePocketKind._KK, HePocketKind._AKs };
            HePocketKind[] bbPockets = new HePocketKind[] { HePocketKind._AA, HePocketKind._KK, HePocketKind._QQ, HePocketKind._AKs };

            string     xmlAt = Props.Global.Expand("${bds.DataDir}ai.pkr.holdem.learn/bigcards-pf-1.xml");
            ActionTree at    = XmlToActionTree.Convert(xmlAt);

            VisActionTree.Show(at, Path.Combine(outDir, "at.gv"));

            BigCardsPreflop bc = new BigCardsPreflop();

            bc.Solve(at, sbPockets, bbPockets);

            Console.WriteLine("Game values: {0}, {1}", bc.GameValues[0], bc.GameValues[1]);
            VisChanceTree.Show(bc.Ct, Path.Combine(outDir, "ct.gv"));
            VisStrategyTree.Show(bc.Strategies[0], Path.Combine(outDir, "st-0.gv"));
            VisStrategyTree.Show(bc.Strategies[1], Path.Combine(outDir, "st-1.gv"));
        }
Exemple #11
0
        private void CreateAndVerifyPlayerTrees(GameDefinition gd, ChanceTree ct, double [] expectedProbabs)
        {
            for (int pos = 0; pos < gd.MinPlayers; ++pos)
            {
                ChanceTree pct = ExtractPlayerChanceTree.ExtractS(ct, pos);

                string fileName = string.Format("{0}-{1}.gv", gd.Name, pos);
                using (TextWriter w = new StreamWriter(File.Open(Path.Combine(_outDir, fileName), FileMode.Create)))
                {
                    VisChanceTree vis = new VisChanceTree {
                        Output = w, CardNames = gd.DeckDescr.CardNames
                    };
                    vis.Show(pct);
                }
                Assert.AreEqual(1, pct.PlayersCount);
                Assert.AreEqual(expectedProbabs.Length, pct.NodesCount);
                VerifyChanceTree.VerifyS(pct);
                for (int i = 0; i < expectedProbabs.Length; ++i)
                {
                    Assert.AreEqual(expectedProbabs[i], pct.Nodes[i].Probab, 0.00000000001, string.Format("Node {0}", i));
                }
            }
        }
Exemple #12
0
        /// <summary>
        /// Runs the solver with the specified parameters..
        /// </summary>
        /// <param name="iterCounts">Number of iterations for each run, -1 - unlimited.</param>
        private StrategyTree RunSolver(TestParams testParams, bool visualize, bool trace, int [] iterCounts, ConfigureSolver configureSolver)
        {
            int playersCount = testParams.ChanceTree.PlayersCount;

            string baseDir = Path.Combine(_outDir, testParams.Name);

            DirectoryExt.Delete(baseDir);
            Directory.CreateDirectory(baseDir);

            string inputDir = Path.Combine(baseDir, "input");

            Directory.CreateDirectory(inputDir);
            string traceDir = Path.Combine(baseDir, "trace");

            if (trace)
            {
                Directory.CreateDirectory(traceDir);
            }

            string chanceTreeFile = Path.Combine(inputDir, "ct.dat");
            string actionTreeFile = Path.Combine(inputDir, "at.dat");

            testParams.ChanceTree.Write(chanceTreeFile);
            testParams.ActionTree.Write(actionTreeFile);

            if (visualize)
            {
                VisActionTree.Show(testParams.ActionTree, actionTreeFile + ".gv");
                VisChanceTree.Show(testParams.ChanceTree, chanceTreeFile + ".gv");
            }


            int  runs   = iterCounts.Length;
            Breb solver = null;

            for (int r = 0; r < runs; ++r)
            {
                // Create and configure a solver
                solver = new Breb
                {
                    GameDef        = testParams.GameDef,
                    ActionTreeFile = actionTreeFile,
                    ChanceTreeFile = chanceTreeFile,
                    OutputPath     = baseDir,
                    SnapshotsCount = 2,
                    Epsilon        = testParams.Epsilon,
                    ThreadsCount   = DEFAULT_THREADS_COUNT,
                };
                if (trace)
                {
                    solver.TraceDir = traceDir;
                }
                solver.MaxIterationCount = iterCounts[r];
                if (configureSolver != null)
                {
                    configureSolver(solver);
                }
                solver.Solve();
            }

            string       fileName   = solver.CurrentSnapshotInfo.StrategyFile;
            StrategyTree eqStrategy = StrategyTree.Read <StrategyTree>(fileName);

            if (visualize)
            {
                VisStrategyTree.Show(eqStrategy, fileName + ".gv");
            }
            return(eqStrategy);
        }
Exemple #13
0
        private static bool ProcessChanceTree()
        {
            ChanceTree tree;

            if (_cmdLine.Input != "")
            {
                if (_inputFormat == ".dat")
                {
                    tree = UFTree.Read <ChanceTree>(_cmdLine.Input);
                }
                else if (_inputFormat == ".txt")
                {
                    tree = DumpChanceTree.FromTxt(_cmdLine.Input);
                }
                else
                {
                    Console.Error.WriteLine("Unsupported input format '{0}' for tree kind '{1}'", _inputFormat, _cmdLine.TreeKind);
                    return(false);
                }
            }
            else
            {
                tree = CreateChanceTreeByGameDef.Create(_gd);
                if (_cmdLine.TreeKind == "chance-player")
                {
                    ChanceTree pt = ExtractPlayerChanceTree.ExtractS(tree, _cmdLine.Position);
                    tree = pt;
                }
            }

            if (_outputFormat == ".gv")
            {
                using (TextWriter w = new StreamWriter(File.Open(_cmdLine.Output, FileMode.Create)))
                {
                    VisChanceTree vis = new VisChanceTree {
                        Output = w
                    };
                    if (_gd != null)
                    {
                        vis.CardNames = _gd.DeckDescr.CardNames;
                    }
                    if (_cmdLine.ClearExpr)
                    {
                        vis.ShowExpr.Clear();
                    }
                    vis.ShowExprFromString(_cmdLine.ShowExpr);
                    vis.PruneIfExt = (t, n, s, d) => s[d].Round > _cmdLine.MaxRound;
                    vis.MatchPath  = _cmdLine.MatchPath;
                    vis.Show(tree);
                }
            }
            else if (_outputFormat == ".dat")
            {
                tree.Write(_cmdLine.Output);
            }
            else if (_outputFormat == ".txt")
            {
                DumpChanceTree.ToTxt(tree, _cmdLine.Output);
            }
            else
            {
                Console.Error.WriteLine("Unsupported output format '{0}' for tree kind '{1}'", _outputFormat, _cmdLine.TreeKind);
                return(false);
            }
            return(true);
        }
Exemple #14
0
        private void Solve(TestParams testParams, bool visualize, ConfigureSolver configureSolver)
        {
            if (visualize)
            {
                VisActionTree.Show(testParams.ActionTree,
                                   Path.Combine(_outDir, String.Format("{0}-at.gv", testParams.Name)));
                VisChanceTree.Show(testParams.ChanceTree,
                                   Path.Combine(_outDir, String.Format("{0}-ct.gv", testParams.Name)));
                for (int p = 0; p < testParams.StrategyTrees.Length; ++p)
                {
                    VisStrategyTree.Show(testParams.StrategyTrees[p],
                                         Path.Combine(_outDir, string.Format("{0}-st-{1}.gv", testParams.Name, p)));
                }
            }

            // Make sure input is correct.
            for (int p = 0; p < testParams.ChanceTree.Nodes[0].Position; ++p)
            {
                string errorText;
                Assert.IsTrue(VerifyAbsStrategy.Verify(testParams.StrategyTrees[p], p, 0.000001, out errorText), errorText);
            }

            for (int heroPos = 0; heroPos < testParams.ChanceTree.PlayersCount; ++heroPos)
            {
                // Create and configure Br solver
                Br br = new Br
                {
                    HeroPosition = heroPos,
                    ChanceTree   = testParams.ChanceTree,
                    ActionTree   = testParams.ActionTree,
                };
                br.Strategies = new StrategyTree[testParams.ChanceTree.PlayersCount];
                for (int opp = 0; opp < testParams.ChanceTree.PlayersCount; ++opp)
                {
                    if (opp == heroPos)
                    {
                        continue;
                    }
                    br.Strategies[opp] = testParams.StrategyTrees[opp];
                }
                br.PrepareVis = visualize;

                if (configureSolver != null)
                {
                    configureSolver(br);
                }

                // Solve Br
                br.Solve();

                if (visualize)
                {
                    Br.Vis.Show(br, Path.Combine(_outDir, String.Format("{0}-br-{1}.gv", testParams.Name, heroPos)));
                }

                // Verify the Br value and strategy

                Assert.AreEqual(testParams.ExpectedResult[heroPos], br.Value, testParams.Epsilon, "Wrong BR value");

                string error;
                Assert.IsTrue(VerifyAbsStrategy.Verify(br.Strategies[br.HeroPosition], br.HeroPosition, out error),
                              error);

                // Verify Br strategy has the expected value by running an independent GameValue algo on it.
                GameValue gv = new GameValue
                {
                    ActionTree = br.ActionTree,
                    ChanceTree = br.ChanceTree,
                    Strategies = br.Strategies
                };
                gv.Solve();
                Assert.AreEqual(testParams.ExpectedResult[heroPos], gv.Values[br.HeroPosition], testParams.Epsilon, "Wrong GameValue value");
            }
        }