public void hashDifferentBoards()
        {
            var first  = BoardFactory.LoadBoardFromFen("rnbqkbnr/pppp1ppp/8/4p3/2B1P3/8/PPPP1PPP/RNBQK1NR b KQkq - 2 2");
            var second = BoardFactory.LoadBoardFromFen("rnbqkbnr/pppp1ppp/8/4p3/2B1PP2/8/PPPP2PP/RNBQK1NR b KQkq - 2 2");

            Assert.AreNotEqual(HashBoard.hash(first), HashBoard.hash(second));
        }
        public HashSet <ulong> findTiedBoard(Board board, Stack <Move> history)
        {
            Dictionary <ulong, int> occurredPositions = new Dictionary <ulong, int>();
            HashSet <ulong>         tiedPositions     = new HashSet <ulong>();

            // take copies so we do not modify the original collection
            var          boardCopy   = board.CreateCopy();
            Stack <Move> historyCopy = new Stack <Move>(history);
            var          hash        = HashBoard.hash(board);

            while (historyCopy.Count != 0)
            {
                if (!occurredPositions.ContainsKey(hash))
                {
                    occurredPositions.Add(hash, 0);
                }
                // count how many times the position has occured
                occurredPositions[hash]++;
                var move = historyCopy.Pop();
                boardCopy.UndoMove(move);
                hash = HashBoard.ApplyMove(board, move, hash);
            }

            foreach (var position in occurredPositions)
            {
                if (position.Value >= 2)
                {
                    // when a position occurs 3 times
                    tiedPositions.Add(position.Key);
                }
            }

            return(tiedPositions);
        }
        public void blackAndWhiteDontHashToTheSameValue()
        {
            var whiteBoard = BoardFactory.LoadBoardFromFen("r3k2r/8/8/2pP4/8/8/8/R3K2R w KQkq c6 0 2");
            var blackBoard = BoardFactory.LoadBoardFromFen("r3k2r/8/8/2pP4/8/8/8/R3K2R b KQkq c6 0 2");


            Assert.AreNotEqual(HashBoard.hash(whiteBoard), HashBoard.hash(blackBoard));
        }
        public void enpassantTargetMakesADifference()
        {
            var noEnpassant = BoardFactory.LoadBoardFromFen("rnb1kbnr/1ppp1ppp/p3p3/8/1P1PP2q/5N2/P1P2PPP/RNBQKB1R b KQkq - 1 0");
            var enpassant   = BoardFactory.LoadBoardFromFen("rnb1kbnr/1ppp1ppp/p3p3/8/1P1PP2q/5N2/P1P2PPP/RNBQKB1R b KQkq b3 1 0");

            var enpassantHash = HashBoard.hash(enpassant);

            Assert.AreNotEqual(HashBoard.hash(noEnpassant), enpassantHash);
        }
        // navnet skifter til noUnionFunction i anden test
        public void hashFunktion()
        {
            ulong combined = 0;
            var   board    = ChessGame.StartGame().board;

            for (int i = 0; i < 1000000; i++)
            {
                combined += HashBoard.hash(board);
            }
        }
        private Board incrementalUpdate(string fen, int fromPosition, int toPosition, Piece promotion = Piece.EMPTY)
        {
            var board = BoardFactory.LoadBoardFromFen(fen);

            var boardHash = HashBoard.hash(board);

            var move = board.FindMove(fromPosition, toPosition, promotion);

            var nextBoardHash = HashBoard.ApplyMove(board, move, boardHash);

            board.Move(move);

            var expectedHash = HashBoard.hash(board);

            Assert.AreEqual(expectedHash, nextBoardHash);

            return(board);
        }
        public void whiteBigPawnMoveBecomesSamePosition()
        {
            /*
             * Starting position (White to play)
             +---------------+
             |r n b q k b n r| 8
             |p p p p _ p p p| 7
             |_ _ _ _ _ _ _ _| 6
             |_ _ _ _ p _ _ _| 5
             |_ _ B _ P _ _ _| 4
             |_ _ _ _ _ _ _ _| 3
             |P P P P _ P P P| 2
             |R N B Q K _ N R| 1
             +---------------+
             * A B C D E F G H
             * F2 -> F4
             +---------------+
             |r n b q k b n r| 8
             |p p p p _ p p p| 7
             |_ _ _ _ _ _ _ _| 6
             |_ _ _ _ p _ _ _| 5
             |_ _ B _ P P _ _| 4
             |_ _ _ _ _ _ _ _| 3
             |P P P P _ _ P P| 2
             |R N B Q K _ N R| 1
             +---------------+
             * A B C D E F G H
             */
            var board = BoardFactory.LoadBoardFromFen("rnbqkbnr/pppp1ppp/8/4p3/2B1P3/8/PPPP1PPP/RNBQK1NR w KQkq - 2 2");

            var moves = board.GetMoves();

            var boardHash = HashBoard.hash(board);

            var move = moves.FindTargetPosition(BoardStateOffset.F5);

            var nextBoardHash = HashBoard.ApplyMove(board, move, boardHash);

            board.Move(move);

            var expectedHash = HashBoard.hash(board);

            Assert.AreEqual(expectedHash, nextBoardHash);
        }
        public void canPromote()
        {
            /*
             * Starting position (White to play)
             +---------------+
             |r _ _ _ k _ _ r| 8
             |_ _ _ _ _ _ _ _| 7
             |_ _ _ _ _ _ _ _| 6
             |_ _ p P _ _ _ _| 5
             |_ _ _ _ _ _ _ _| 4
             |_ _ _ _ _ _ _ _| 3
             |_ _ _ _ _ _ _ _| 2
             |R _ _ _ K _ _ R| 1
             +---------------+
             * A B C D E F G H
             * D5 -> C6
             +---------------+
             |r _ _ _ k _ _ r| 8
             |_ _ _ _ _ _ _ _| 7
             |_ _ P _ _ _ _ _| 6
             |_ _ _ _ _ _ _ _| 5
             |_ _ _ _ _ _ _ _| 4
             |_ _ _ _ _ _ _ _| 3
             |_ _ _ _ _ _ _ _| 2
             |R _ _ _ K _ _ R| 1
             +---------------+
             * A B C D E F G H
             */
            var board = BoardFactory.LoadBoardFromFen("r3k2r/8/8/2pP4/8/8/8/R3K2R w KQkq c6 0 2");

            var moves = board.GetMoves();

            var boardHash = HashBoard.hash(board);

            var move = moves.FindTargetPosition(BoardStateOffset.D5, BoardStateOffset.C6);

            var nextBoardHash = HashBoard.ApplyMove(board, move, boardHash);

            board.Move(move);
            board.UndoMove(move);
            var previousHash = HashBoard.ApplyMove(board, move, nextBoardHash);

            Assert.AreEqual(boardHash, previousHash);
        }
Exemple #9
0
        // start by solving your own threads problems
        // afterwards start solving moves that belong to other that have not been started yet
        // if there are only started moves left then start working a move that is already beeing worked on (they might have a worse starting minimum score)
        internal void MinMaxWorker(AITask aiTask)
        {
            currentTask = aiTask;
            Board               board          = aiTask.board;
            List <Move>         moves          = aiTask.moves[aiTask.taskId];
            int                 depth          = aiTask.depth;
            Action <SolvedMove> onMoveComplete = aiTask.onMoveComplete;

            lock (stateLock) {
                max = float.MaxValue;
                min = float.MinValue;
                alreadySolved.Clear();
                begunMoves.Clear();
            }

            bestScore = float.MinValue;

            boardHash = HashBoard.hash(board);

            SolvedMove lastSolvedMove = null;

            // ------------ start working on this threads moves ------------------
            foreach (var move in moves)
            {
                // check that the other threads have not begun the next move yet
                if (!alreadySolved.Contains(move) && !begunMoves.Contains(move))
                {
                    lastSolvedMove = MinMaxMove(aiTask, move, lastSolvedMove);
                }
            }

            // ------------ start searching for other threads unfinished work --------------
            IEnumerable <Move> availableMoves = new List <Move>();
            Random             random         = new Random();

            do
            {
                availableMoves = aiTask.moves.SelectMany((threadsMoves) => {
                    return(threadsMoves.Where(move => !alreadySolved.Contains(move) && !begunMoves.Contains(move)));
                }).ToList();

                // use random to avoid conflicts
                var nextMove = availableMoves.RandomElementUsing(random);
                if (MoveHelper.isValidMove(nextMove))
                {
                    lastSolvedMove = MinMaxMove(aiTask, nextMove, lastSolvedMove);
                }
            } while (availableMoves.Count() > 0);

            // ------------ start searching for other threads begun but unfinished work --------------
            do
            {
                availableMoves = aiTask.moves.SelectMany((threadsMoves) => {
                    return(threadsMoves.Where(move => !alreadySolved.Contains(move)));
                }).ToList();

                // use random to avoid conflicts
                var nextMove = availableMoves.RandomElementUsing(random);
                if (MoveHelper.isValidMove(nextMove))
                {
                    lastSolvedMove = MinMaxMove(aiTask, nextMove, lastSolvedMove);
                }
            } while (availableMoves.Count() > 0);

            if (lastSolvedMove != null)
            {
                aiTask.onMoveComplete(lastSolvedMove);
            }
        }
Exemple #10
0
        // should only be called by MinMaxWorker, since it initializes a bunch of stuff
        private SolvedMove MinMaxMove(AITask aiTask, Move move, SolvedMove lastSolvedMove)
        {
            if (lastSolvedMove != null)
            {
                lastSolvedMove.startSolvingMove = move;
                aiTask.onMoveComplete(lastSolvedMove);
            }
            Stopwatch stopWatch = new Stopwatch();

            stopWatch.Start();
            boardHash = HashBoard.ApplyMove(aiTask.board, move, boardHash);
            ulong currentBoardHash = boardHash;

            lock (stateLock) {
                if (alreadySolved.Contains(move))
                {
                    // if the hash has board has already been analyzed then skip
                    boardHash = HashBoard.ApplyMove(aiTask.board, move, boardHash);
                    return(null);
                }
                if (min > bestScore)
                {
                    bestScore = min;
                }
            }
            aiTask.board.Move(move);
            var startedFromMin    = min;
            var detectWinnerMoves = aiTask.board.GetMoves();
            // check for winner
            var winner = aiTask.board.detectWinner(detectWinnerMoves);

            if (winner != Winner.NONE)
            {
                float score = 0;
                if ((winner == Winner.WINNER_WHITE || winner == Winner.WINNER_BLACK))
                {
                    // if a checkmate is found then no deeper moves matter since we are going to play that move
                    score = float.MaxValue - aiTask.board.VirtualLevel;
                }
                else if (winner == Winner.DRAW)
                {
                    score = 0;
                }
                lock (stateLock) {
                    alreadySolved.Add(move);

                    min = Math.Max(score, min);
                }
                stopWatch.Stop();
                return(new SolvedMove()
                {
                    startFromMin = min,
                    min = min,
                    max = max,
                    durationMS = stopWatch.ElapsedMilliseconds,
                    move = new EvaluatedMove()
                    {
                        move = move,
                        score = score
                    },
                    taskId = currentTask.taskId,
                });
            }
            if (aiTask.tiedPositions.Contains(currentBoardHash))
            {
                float score = 0;
                lock (stateLock) {
                    alreadySolved.Add(move);

                    min = Math.Max(score, min);
                }
                return(new SolvedMove()
                {
                    startFromMin = min,
                    min = min,
                    max = max,
                    durationMS = stopWatch.ElapsedMilliseconds,
                    taskId = currentTask.taskId,
                    move = new EvaluatedMove()
                    {
                        move = move,
                        score = score,
                    },
                });
            }


            aiTask.board.VirtualLevel++;
            var moveScore = minmax.MinMax(aiTask.board, aiTask.depth, aiTask.tiedPositions, false, min, max);

            aiTask.board.VirtualLevel--;
            aiTask.board.UndoMove(move);
            boardHash = HashBoard.ApplyMove(aiTask.board, move, boardHash);
            lock (stateLock) {
                if (alreadySolved.Contains(move))
                {
                    //Console.WriteLine($"collision! refound found move! {MoveHelper.ReadableMove(move)}");
                    // if the hash has board has already been analyzed before this thread managed to do it, then skip
                    return(null);
                }

                // optimize for player
                if (moveScore > bestScore)
                {
                    bestScore = moveScore;
                }

                min = Math.Max(moveScore, min);


                alreadySolved.Add(move);
            }
            // outside of stateLock because otherwise it will trigger will trigger the lock in moveSolved
            Thread t = Thread.CurrentThread;

            stopWatch.Stop();
            return(new SolvedMove()
            {
                startFromMin = startedFromMin,
                min = min,
                max = max,
                durationMS = stopWatch.ElapsedMilliseconds,
                move = new EvaluatedMove()
                {
                    move = move,
                    score = moveScore
                },
                taskId = currentTask.taskId
            });
        }
Exemple #11
0
        public List <EvaluatedMove> MinMaxList(Board board, int depth, HashSet <ulong> tiedPositions = null, bool maximizing = true, float min = float.MinValue, float max = float.MaxValue)
        {
            boardHash = HashBoard.hash(board);

            if (tiedPositions == null)
            {
                tiedPositions = new HashSet <ulong>();
            }

            List <EvaluatedMove> movePoints = new List <EvaluatedMove>();
            //var bestMove = MinMaxInternal(board, depth, maximizing, min, max);
            var bestMove = maximizing ? float.MinValue : float.MaxValue;

            var moveList = layeredLists[board.VirtualLevel];

            moveList.Clear();

            var moves = board.GetMoves(moveList);

            var winner = board.detectWinner(moves);


            foreach (var move in moves)
            {
                byte myTurn = board.IsWhiteTurn;
                boardHash = HashBoard.ApplyMove(board, move, boardHash);
                board.Move(move);

                board.VirtualLevel++;

                var attacked = board.Attacked(board.GetKingPosition(myTurn), myTurn);
                if (attacked)
                {
                    // if the king is under attack after making the move then it is not a valid move, in which case ignore the move
                    board.VirtualLevel--;
                    board.UndoMove(move);
                    boardHash = HashBoard.ApplyMove(board, move, boardHash);
                    continue;
                }
                var moveScore = MinMax(board, depth, tiedPositions, !maximizing, min, max);
                movePoints.Add(new EvaluatedMove()
                {
                    move  = move,
                    score = moveScore,
                });

                board.VirtualLevel--;
                board.UndoMove(move);
                boardHash = HashBoard.ApplyMove(board, move, boardHash);

                if (maximizing)
                {
                    // optimize for player
                    if (moveScore > bestMove)
                    {
                        bestMove = moveScore;
                    }
                    min = Math.Max(moveScore, min);
                }
                else
                {
                    if (moveScore < bestMove)
                    {
                        bestMove = moveScore;
                    }
                    max = Math.Min(moveScore, max);
                }
            }

            // sort the moves in descending order
            //movePoints.Sort((a, b) => (a.score < b.score) ? 1 : -1);
            movePoints = movePoints.OrderBy(move => move.score).Reverse().ToList();

            return(movePoints);
        }
Exemple #12
0
        protected float MinMaxInteral(Board board, int depth, HashSet <ulong> tiedPositions, bool maximizing = true, float min = float.MinValue, float max = float.MaxValue)
        {
            var optimizeForColor = maximizing ? 1 : 0;
            var minimizeForColor = optimizeForColor ^ 1;

            float bestMove = maximizing ? float.MinValue : float.MaxValue;

            if (tiedPositions.Contains(boardHash))
            {
                // if the position is tied due to repetition then return 0;
                return(0);
            }

            var moveList = layeredLists[board.VirtualLevel];

            moveList.Clear();
            //var moves = Board.GetMoves(board, moveList);

            var moves = board.GetMoves(moveList);



            if (board.VirtualLevel >= depth)
            {
                // if we have reached max depth assign a score.
                var winner = board.detectWinner(moves);
                if ((winner == Winner.WINNER_WHITE || winner == Winner.WINNER_BLACK))
                {
                    if (maximizing)
                    {
                        // if a checkmate is found then no deeper moves matter since we are going to play that move
                        return(float.MinValue + board.VirtualLevel);
                    }
                    else
                    {
                        return(float.MaxValue - board.VirtualLevel);
                    }
                }
                else if (winner == Winner.DRAW)
                {
                    return(0);
                }

                float score = EvalBoard.evalBoard(board, moves);

                int isWhite = ((board.IsWhiteTurn ^ depth) & 1);

                if (isWhite != 1)
                {
                    // if black started the query then optimize for black
                    score *= -1;
                }
                return(score);
            }

            // because detectWinner requires checking for valid moves, which is slow only do it for end nodes
            // for all other cases reimplement reimplement the logic locally
            if (board.hasInsufficientMaterialOrTimeLimit())
            {
                return(0);
            }



            // hasValidMove is used to track if the player has a valid move they can play,
            // if not this is used to declare a winner
            bool foundValidMove = false;

            foreach (MoveAndEvaluatedPosition moveAndEvluated in moves
                     .Select(GetMoveAndEvaluatedPositionFromMove)
                     .OrderByDescending(OrderMoveAndEvaluatedPositionFromMove))
            {
                var move = moveAndEvluated.move;
                //foreach (var move in moves) {
                byte myTurn = board.IsWhiteTurn;

                //var calculatedBeforeHash = HashBoard.hash(board);
                //var beforeHash = boardHash;
                boardHash = HashBoard.ApplyMove(board, move, boardHash);
                board.Move(move);
                //var expectedHash = HashBoard.hash(board);
                //if (expectedHash != boardHash) {
                //    Console.WriteLine("ARG");
                //    Console.WriteLine(calculatedBeforeHash);
                //    Console.WriteLine(beforeHash);
                //}

                board.VirtualLevel++;

                var attacked = board.Attacked(board.GetKingPosition(myTurn), myTurn);
                if (attacked)
                {
                    // if the king is under attack after making the move then it is not a valid move, in which case ignore the move
                    board.VirtualLevel--;
                    board.UndoMove(move);
                    boardHash = HashBoard.ApplyMove(board, move, boardHash);
                    continue;
                }
                foundValidMove = true;

                EvaluatedPosition existingScore = moveAndEvluated.evaluatedPosition;
                bool  useCache          = false;
                float moveScore         = 0;
                int   movesFromMaxDepth = depth - board.VirtualLevel;

                if (moveAndEvluated.hasEvaluatedMove)
                {
                    // subtract the difference between the 2 depths to give previous best moves a disadvantage
                    // because a previous move that was explored at less depth has less knowlegde than the current best
                    if (existingScore.distanceToEndSearch - depth + existingScore.depth >= movesFromMaxDepth)
                    {
                        useCache  = true;
                        moveScore = existingScore.score;
                    }
                }
                if (!useCache)
                {
                    moveScore = MinMax(board, depth, tiedPositions, !maximizing, min, max);

                    moveScores[boardHash] = new EvaluatedPosition()
                    {
                        distanceToEndSearch = (byte)movesFromMaxDepth,
                        score = moveScore,
                        depth = (byte)depth,
                        //fen = board.simplifiedFEN,
                    };
                }

                board.VirtualLevel--;
                board.UndoMove(move);
                // undo previous hash
                boardHash = HashBoard.ApplyMove(board, move, boardHash);

                if (maximizing)
                {
                    // optimize for player
                    if (moveScore > bestMove)
                    {
                        bestMove = moveScore;
                    }
                    min = Math.Max(moveScore, min);
                    if (min > max)
                    {
                        return(bestMove);
                    }
                }
                else
                {
                    if (moveScore < bestMove)
                    {
                        bestMove = moveScore;
                    }
                    max = Math.Min(moveScore, max);
                    if (min > max)
                    {
                        return(bestMove);
                    }
                }
            }
            if (!foundValidMove)
            {
                if (maximizing)
                {
                    return(float.MinValue + board.VirtualLevel);
                    // if a checkmate is found then no deeper moves matter since we are going to play that move
                }
                else
                {
                    return(float.MaxValue - board.VirtualLevel);
                }
            }


            return(bestMove);
        }
Exemple #13
0
 public float MinMax(Board board, int depth, HashSet <ulong> tiedPositions, bool maximizing = true, float min = float.MinValue, float max = float.MaxValue)
 {
     // this function
     boardHash = HashBoard.hash(board);
     return(MinMaxInteral(board, depth, tiedPositions, maximizing, min, max));
 }
 public void hasDifferentBitStrings()
 {
     Assert.AreNotEqual(HashBoard.pieceHash(0, Piece.EMPTY), HashBoard.pieceHash(1, Piece.EMPTY));
 }