示例#1
0
        public void TestTranspositionTableBucketConstruction()
        {
            var entry1 = new TranspositionTableEntry(
                0x1234567890A,
                GameMove.FromStringNotation("a1b1"),
                EvaluationScore.Mate,
                EvaluationScore.Zero,
                ScoreBound.Exact,
                3);

            var entry2 = new TranspositionTableEntry(
                0xABCDEF9876,
                GameMove.FromStringNotation("g7g8q"),
                new EvaluationScore(1001),
                new EvaluationScore(997),
                ScoreBound.Lower,
                11);

            Assert.That(entry1.Key, Is.Not.EqualTo(entry2.Key));
            Assert.That(entry1.BestMove, Is.Not.EqualTo(entry2.BestMove));
            Assert.That(entry1.Score, Is.Not.EqualTo(entry2.Score));
            Assert.That(entry1.LocalScore, Is.Not.EqualTo(entry2.LocalScore));
            Assert.That(entry1.Bound, Is.Not.EqualTo(entry2.Bound));
            Assert.That(entry1.Depth, Is.Not.EqualTo(entry2.Depth));

            var bucket = new TranspositionTableBucket
            {
                Entry1 = entry1,
                Entry2 = entry2
            };

            Assert.That(bucket.Entry1, Is.EqualTo(entry1));
            Assert.That(bucket.Entry2, Is.EqualTo(entry2));
        }
示例#2
0
        public void TranspositionTable_FillAndReplacePerfTest()
        {
            TimeSpan sum        = TimeSpan.Zero;
            int      iterations = 1000;

            for (int i = 0; i < iterations; i++)
            {
                TranspositionTable tt = new TranspositionTable(TranspositionTable.DefaultSizeInBytes / 1024);
                Assert.IsNotNull(tt);

                Stopwatch sw = Stopwatch.StartNew();

                // Fill
                for (int j = 0; j < tt.Capacity; j++)
                {
                    ulong key = (ulong)j;
                    tt.Store(key, CreateMaxEntry(j));
                }

                // Replace
                for (int j = tt.Capacity - 1; j >= 0; j--)
                {
                    TranspositionTableEntry newEntry = CreateMaxEntry(j);
                    newEntry.Depth++;
                    ulong key = (ulong)j;
                    tt.Store(key, newEntry);
                }

                sw.Stop();

                sum += sw.Elapsed;
            }

            Trace.WriteLine(string.Format("Average Ticks: {0}", sum.Ticks / iterations));
        }
        public void SettingDepthReturnsDepthAgeWithDepth()
        {
            var test = new TranspositionTableEntry();

            test.Depth = 5;
            test.Age   = 3;

            Assert.Equal(0xC5, test.DepthAge); //1100 0101//
        }                                      //age^   ^depth
        }                                      //age^   ^depth

        [Fact] void SettingAgeReturnsDepthAgeWithAge()
        {
            var test = new TranspositionTableEntry();

            test.Age = 3;

            var b = test.DepthAge;

            Assert.True((b & 0xC0) == 0xC0, "Age bits incorrect");
        }
示例#5
0
        private TranspositionTableEntry CreateMaxEntry(int id)
        {
            TranspositionTableEntry te = new TranspositionTableEntry
            {
                Depth    = 0,
                Type     = TranspositionTableEntryType.Exact,
                Value    = 0,
                BestMove = new Move(PieceName.WhiteSoldierAnt1, new Position(0, 0, 0, 0))
            };

            return(te);
        }
示例#6
0
        public void TestTranspositionTable()
        {
            var transpositionTable = new TranspositionTable(FakeLogger.Instance, TranspositionTableHelper.SizeInMegaBytesRange.Lower);

            Assert.That(transpositionTable.Version, Is.Not.EqualTo(0));

            const long       Key      = 0x12345678ABCDEF01L;
            const long       OtherKey = 0x987654321L;
            const ScoreBound Bound    = ScoreBound.Exact;
            const int        Depth    = CommonEngineConstants.MaxPlyDepthUpperLimit;

            var bestMove   = GameMove.FromStringNotation("b2b1q");
            var score      = EvaluationScore.Mate;
            var localScore = new EvaluationScore(-789);

            var entry = new TranspositionTableEntry(Key, bestMove, score, localScore, Bound, Depth);

            transpositionTable.Save(ref entry);
            Assert.That(entry.Version, Is.EqualTo(transpositionTable.Version));

            Assert.That(transpositionTable.ProbeCount, Is.EqualTo(0));
            Assert.That(transpositionTable.HitCount, Is.EqualTo(0));

            var foundEntry1 = transpositionTable.Probe(Key);

            Assert.That(transpositionTable.ProbeCount, Is.EqualTo(1));
            Assert.That(transpositionTable.HitCount, Is.EqualTo(1));
            Assert.That(foundEntry1.HasValue, Is.True);
            Assert.That(foundEntry1.Value.Key, Is.EqualTo(Key));
            Assert.That(foundEntry1.Value.BestMove, Is.EqualTo(bestMove));
            Assert.That(foundEntry1.Value.Score, Is.EqualTo(score));
            Assert.That(foundEntry1.Value.LocalScore, Is.EqualTo(localScore));
            Assert.That(foundEntry1.Value.Bound, Is.EqualTo(Bound));
            Assert.That(foundEntry1.Value.Depth, Is.EqualTo(Depth));
            Assert.That(foundEntry1.Value.Version, Is.EqualTo(transpositionTable.Version));

            var foundEntry2 = transpositionTable.Probe(OtherKey);

            Assert.That(transpositionTable.ProbeCount, Is.EqualTo(2));
            Assert.That(transpositionTable.HitCount, Is.EqualTo(1));
            Assert.That(foundEntry2.HasValue, Is.False);
        }
示例#7
0
        public void TestTranspositionTableEntryConstruction()
        {
            const long       Key   = 0x12345678ABCDEF01L;
            const ScoreBound Bound = ScoreBound.Exact;
            const int        Depth = CommonEngineConstants.MaxPlyDepthUpperLimit;

            var bestMove   = GameMove.FromStringNotation("b2b1q");
            var score      = EvaluationScore.Mate;
            var localScore = new EvaluationScore(-789);

            var entry = new TranspositionTableEntry(Key, bestMove, score, localScore, Bound, Depth);

            Assert.That(entry.Key, Is.EqualTo(Key));
            Assert.That(entry.BestMove, Is.EqualTo(bestMove));
            Assert.That(entry.Score, Is.EqualTo(score));
            Assert.That(entry.LocalScore, Is.EqualTo(localScore));
            Assert.That(entry.Bound, Is.EqualTo(Bound));
            Assert.That(entry.Depth, Is.EqualTo(Depth));
            Assert.That(entry.Version, Is.EqualTo(0));
        }
        /// <summary>
        /// Using NegaMax with Alpha-Beta Pruning and Transposition Table to determine the best possible move.
        /// </summary>
        /// <param name="gameState"> Current GameState. </param>
        /// <param name="depth"> How many layers of branches to foresee. </param>
        /// <param name="alpha">  Maximum to prune out. </param>
        /// <param name="beta">  Minimum to prune out. </param>
        /// <param name="maximizingPlayer">  Current Player. True = caller. </param>
        /// <returns>  The evaluated score. </returns>
        private float NegaMax(GameStateReader gameState, int depth, float alpha = float.NegativeInfinity, float beta = float.PositiveInfinity, bool maximizingPlayer = true /*, bool allowNullMove = true*/)
        {
            #region Transposition Table retrieval

            var  origAlpha   = alpha;
            long zobristHash = gameState.zobristHash;
            if (transpositionTable.ContainsKey(zobristHash))
            {
                var ttEntry = transpositionTable[zobristHash];
                var value   = ttEntry.value;
                if (!maximizingPlayer)
                {
                    value *= -1;
                }
                if (ttEntry.depth >= depth)
                {
                    switch (ttEntry.flag)
                    {
                    case TranspositionTableEntry.Flag.EXACT:
                        return(value);

                    case TranspositionTableEntry.Flag.UPPERBOUND:
                        alpha = Mathf.Max(alpha, value);
                        break;

                    case TranspositionTableEntry.Flag.LOWERBOUND:
                        beta = Mathf.Min(beta, value);
                        break;
                    }
                    if (alpha >= beta)
                    {
                        return(value);
                    }
                }
            }

            #endregion

            #region End Node

            if (gameState.GameOver)
            {
                return(EvaluateBoardState(gameState) * (maximizingPlayer ? 1 : -1));
            }

            if (depth == 0)
            {
                if (gameState.Quiet())
                {
                    return(EvaluateBoardState(gameState) * (maximizingPlayer ? 1 : -1));
                }
                else
                {
                    return(Quiesce(gameState, quiesceDepth, !maximizingPlayer, alpha, beta));
                }
            }

            #endregion

            #region Search Tree

            var bestEval     = float.NegativeInfinity;
            var bestMove     = new Vector2Int[2];
            var orderedMoves = gameState.PossibleMovesQuick(maximizingPlayer);
            orderedMoves = OrderMoves(orderedMoves, gameState, depth);

            foreach (var child in orderedMoves)
            {
                gameState.MakeMove(child);
                var eval = -NegaMax(gameState, depth - 1, -beta, -alpha, !maximizingPlayer);
                gameState.UndoMove();
                if (eval > bestEval)
                {
                    bestEval = eval;
                    bestMove = child;
                    alpha    = Mathf.Max(alpha, bestEval);
                    if (alpha >= beta)
                    {
                        if (gameState.IsntCapture(child[1]))
                        {
                            int ply = this.depth - depth;
                            killerMoves[ply, 1] = killerMoves[ply, 0];
                            killerMoves[ply, 0] = child;
                            historyMoves[child[0].x, child[0].y, child[1].x, child[1].y] += depth * depth;
                        }
                        break;
                    }
                }
            }

            #endregion

            #region Add to Transposition Table.

            var newEntry = new TranspositionTableEntry {
                depth  = depth,
                value  = bestEval * (maximizingPlayer ? 1 : -1),
                pvMove = bestMove
            };
            switch (bestEval)
            {
            case float value when value <= origAlpha:
                newEntry.flag = TranspositionTableEntry.Flag.UPPERBOUND;
                break;

            case float value when value >= beta:
                newEntry.flag = TranspositionTableEntry.Flag.LOWERBOUND;
                break;

            default:
                newEntry.flag = TranspositionTableEntry.Flag.EXACT;
                break;
            }
            transpositionTable[zobristHash] = newEntry;

            #endregion

            #region Return result

            return(bestEval);

            #endregion
        }
        /// <summary>
        /// Using (Root) NegaMax with Alpha-Beta Pruning and Transposition Table to determine the best possible move.
        /// </summary>
        /// <param name="gameState"> Current GameState. </param>
        /// <param name="depth"> How many layers of branches to foresee. </param>
        /// <param name="alpha">  Maximum to prune out. </param>
        /// <param name="beta">  Minimum to prune out. </param>
        /// <param name="maximizingPlayer">  Current Player. True = caller. </param>
        /// <returns>  The best move. </returns>
        private Vector2Int[] RootNegaMax(GameStateReader gameState, int depth, float alpha = float.NegativeInfinity, float beta = float.PositiveInfinity, bool maximizingPlayer = true /*, bool allowNullMove = true*/)
        {
            #region Inititalize Variables

            var  origAlpha   = alpha;
            long zobristHash = gameState.zobristHash;

            var bestEval     = float.NegativeInfinity;
            var orderedMoves = gameState.PossibleMovesQuick(maximizingPlayer);
            orderedMoves = OrderMoves(orderedMoves, gameState, depth);
            var bestMove = orderedMoves[0];

            #endregion

            #region Search Tree

            foreach (var child in orderedMoves)
            {
                gameState.MakeMove(child);
                var eval = -NegaMax(gameState, depth - 1, -beta, -alpha, !maximizingPlayer);
                gameState.UndoMove();
                if (eval > bestEval)
                {
                    bestEval = eval;
                    bestMove = child;
                    alpha    = Mathf.Max(alpha, bestEval);
                    if (alpha >= beta)
                    {
                        if (gameState.IsntCapture(child[1]))
                        {
                            int ply = this.depth - depth;
                            killerMoves[ply, 1] = killerMoves[ply, 0];
                            killerMoves[ply, 0] = child;
                            historyMoves[child[0].x, child[0].y, child[1].x, child[1].y] += depth * depth;
                        }
                        break;
                    }
                }
            }

            #endregion

            #region Add to Transposition Table.

            var newEntry = new TranspositionTableEntry {
                depth  = depth,
                value  = bestEval,
                pvMove = bestMove
            };
            switch (bestEval)
            {
            case float value when value <= origAlpha:
                newEntry.flag = TranspositionTableEntry.Flag.UPPERBOUND;
                break;

            case float value when value >= beta:
                newEntry.flag = TranspositionTableEntry.Flag.LOWERBOUND;
                break;

            default:
                newEntry.flag = TranspositionTableEntry.Flag.EXACT;
                break;
            }
            transpositionTable[zobristHash] = newEntry;

            #endregion

            #region Return result

            return(bestMove);

            #endregion
        }