Exemple #1
0
        public void ProcessMoves_WrongNumberOfMoves_ThrowsException()
        {
            var board = new BoardData
            {
                Height = 5,
                Width  = 5,
                Snakes = new List <Snake>
                {
                    new Snake {
                        Body = new List <BodyPartPosition> {
                            new BodyPartPosition(1, 2), new BodyPartPosition(1, 2), new BodyPartPosition(1, 2)
                        }
                    },
                    new Snake {
                        Body = new List <BodyPartPosition> {
                            new BodyPartPosition(3, 2), new BodyPartPosition(3, 2), new BodyPartPosition(3, 2)
                        }
                    }
                }
            };

            int[,] boardArray = GetBoardArray(board);
            var snakeBodies = GetSnakeBodies(board);
            var healths     = GetHealths(board);

            var moves = new LegalMove[] { LegalMove.Down, LegalMove.Down, LegalMove.Down };

            var exception = Assert.Throws <Exception>(() => gameEngine.ProcessMoves(snakeBodies, healths, boardArray, moves));

            Assert.That(exception.Message, Is.EqualTo($"3 moves was provided, but the board has 2 snakes"));
        }
Exemple #2
0
        public void ProcessMoves_TwoSnakesAndMoves_HeadsAreUpdatedCorrectly()
        {
            var board = new BoardData
            {
                Height = 5,
                Width  = 5,
                Snakes = new List <Snake>
                {
                    new Snake {
                        Body = new List <BodyPartPosition> {
                            new BodyPartPosition(1, 2), new BodyPartPosition(1, 2), new BodyPartPosition(1, 2)
                        }
                    },
                    new Snake {
                        Body = new List <BodyPartPosition> {
                            new BodyPartPosition(3, 2), new BodyPartPosition(3, 2), new BodyPartPosition(3, 2)
                        }
                    }
                }
            };

            int[,] boardArray = GetBoardArray(board);
            var snakeBodies = GetSnakeBodies(board);
            var healths     = GetHealths(board);

            var moves = new LegalMove[] { LegalMove.Down, LegalMove.Right };

            (var futureSnakes, var futureHealths) = this.gameEngine.ProcessMoves(snakeBodies, healths, boardArray, moves);

            Assert.That(futureSnakes, Is.Not.Null);
            Assert.That(futureSnakes.Count, Is.EqualTo(2));

            Assert.That(futureSnakes[0], Is.EqualTo(new BodyPartPosition[] { new BodyPartPosition(1, 3), new BodyPartPosition(1, 2), new BodyPartPosition(1, 2) }));
            Assert.That(futureSnakes[1], Is.EqualTo(new BodyPartPosition[] { new BodyPartPosition(4, 2), new BodyPartPosition(3, 2), new BodyPartPosition(3, 2) }));
        }
Exemple #3
0
        public void ProcessMoves_EightSnakes_IsNotTooSlow()
        {
            var board = TestCases.Test12().Board;

            int[,] boardArray = GetBoardArray(board);
            var snakeBodies = GetSnakeBodies(board);
            var healths     = GetHealths(board);

            var moves = new LegalMove[] { LegalMove.Left, LegalMove.Up, LegalMove.Up, LegalMove.Right, LegalMove.Left, LegalMove.Down, LegalMove.Down, LegalMove.Right };

            BodyPartPosition[][] futureSnakes = null;
            int[] futureHealths = null;
            var   stopwatch     = Stopwatch.StartNew();

            for (int i = 0; i < 65536; i++)
            {
                (futureSnakes, futureHealths) = this.gameEngine.ProcessMoves(snakeBodies, healths, boardArray, moves);
            }
            stopwatch.Stop();
            Console.WriteLine(stopwatch.Elapsed);
            Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(100));

            Assert.That(futureSnakes.Count, Is.EqualTo(8));
            Assert.That(futureHealths.All(x => x == 100), Is.True);
        }
Exemple #4
0
 public static string GetSan([NotNull] this Position position, LegalMove move)
 {
     if (position == null)
     {
         throw new ArgumentNullException(nameof(position));
     }
     return(position.GetSanBegin(move) /*+ position.GetSanEnd(move)*/);
 }
Exemple #5
0
        public static Position ToPosition(this LegalMove legalMove)
        {
            var core         = legalMove.ResultPosition;
            var prev         = legalMove.OriginalPosition;
            var piece        = legalMove.Piece;
            var obs          = legalMove.Annotations;
            var color        = prev.Core.Turn;
            var tempPosition = new Position(core, 0, 0, 0, legalMove);

            var newHalfMoveClock =
                piece == PieceType.Pawn || (obs & MoveAnnotations.Capture) != 0
                    ? 0
                    : prev.FiftyMovesClock + 1;

            var newMoveNumber =
                prev.MoveNumber + (color == Color.Black ? 1 : 0);

            var isCheck = core.IsInCheck(core.Turn);
            var noMoves = tempPosition.GetAllLegalMoves().Count == 0;

            var newState = default(GameStates);

            if (isCheck && noMoves)
            {
                newState |= GameStates.Mate;
            }
            if (isCheck && !noMoves)
            {
                newState |= GameStates.Check;
            }
            if (!isCheck && noMoves)
            {
                newState |= GameStates.Stalemate;
            }
            if (newHalfMoveClock >= 50)
            {
                newState |= GameStates.FiftyMoveRule;
            }

            var isRepetition = tempPosition.GetHistory()
                               .Prepend(tempPosition)
                               .Select(p => p.Core)
                               .CountBy()
                               .MaxBy(x => x.Value).Value > 2;

            if (isRepetition)
            {
                newState |= GameStates.Repetition;
            }

            return(new Position(core, newHalfMoveClock,
                                newMoveNumber, newState, legalMove));
        }
Exemple #6
0
        public void ProcessMoves_MoveIntoUpperWallWithFullHealth_SnakeGetsZeroHealth()
        {
            var board = TestCases.Test01().Board;

            int[,] boardArray = GetBoardArray(board);
            var snakeBodies = GetSnakeBodies(board);
            var healths     = GetHealths(board);

            var moves = new LegalMove[] { LegalMove.Up };

            (var futureSnakes, var futureHealths) = this.gameEngine.ProcessMoves(snakeBodies, healths, boardArray, moves);

            Assert.That(futureHealths[0], Is.EqualTo(0));
        }
Exemple #7
0
        public void ProcessMoves_HeadOnCollisionWithFullHealth_ShortestSnakeGetsZeroHealth()
        {
            var board = TestCases.Test04().Board;

            int[,] boardArray = GetBoardArray(board);
            var snakeBodies = GetSnakeBodies(board);
            var healths     = GetHealths(board);

            var moves = new LegalMove[] { LegalMove.Right, LegalMove.Up };

            (var futureSnakes, var futureHealths) = this.gameEngine.ProcessMoves(snakeBodies, healths, boardArray, moves);

            Assert.That(futureHealths[0], Is.EqualTo(100));
            Assert.That(futureHealths[1], Is.EqualTo(0));
        }
Exemple #8
0
        private LegalMove[][] GeneratePossibleMovesPermutations(int numSnakes)
        {
            var allLegalMoves = new LegalMove[] { LegalMove.Up, LegalMove.Down, LegalMove.Left, LegalMove.Right };

            if (numSnakes == 1)
            {
                var moves = new LegalMove[4][];

                for (int i = 0; i < allLegalMoves.Length; i++)
                {
                    moves[i] = new LegalMove[] { allLegalMoves[i] };
                }

                return(moves);
            }
            else
            {
                int numPossibleMoves = (int)Math.Pow(4, numSnakes);
                var moves            = new LegalMove[numPossibleMoves][];

                for (int i = 0; i < allLegalMoves.Length; i++)
                {
                    var possibleMoves = GeneratePossibleMovesPermutations(numSnakes - 1);

                    for (int j = 0; j < possibleMoves.Length; j++)
                    {
                        var possibleMove      = possibleMoves[j];
                        var newPossibleFuture = new LegalMove[possibleMove.Length + 1];
                        newPossibleFuture[0] = allLegalMoves[i];
                        possibleMove.CopyTo(newPossibleFuture, 1);
                        moves[(i * possibleMoves.Length) + j] = newPossibleFuture;
                    }
                }

                return(moves);
            }
        }
Exemple #9
0
    private void onLudoSelected(LudoPiece ludo)
    {
        if (selectedLudo != null)
        {
            clearPreviousHighlight();
        }

        selectedLudo = ludo;
        LegalMove legalMove = getLegalMoveForLudo(ludo);

        if (legalMove.CanMoveOutOfPlay)
        {
            sendMoveOutOfPlay();
        }
        else
        {
            GameObject highlight = createHighlightObject();
            highlight.transform.position = legalMove.AvailableMove.transform.position;
            highlight.GetComponent <Clickable>().AddListener(() =>
            {
                sendMove(legalMove.AvailableMove);
            });
        }
    }
Exemple #10
0
 public WeightedMove(LegalMove move, int newX, int newY)
 {
     this.Move = move;
     this.NewX = newX;
     this.NewY = newY;
 }
Exemple #11
0
        public static AnalyzedMove Validate(this Position source, Move move)
        {
            var whiteKingSquare = source.Core.WhiteKing;
            var blackKingSquare = source.Core.BlackKing;

            var sourceCells = source.Core.Cells;
            var cells       = source.Core.GetCopyOfCells();

            MoveAnnotations notes;
            // Piece in the from cell?
            var moveFrom = move.FromCell;
            var piece    = (Piece)sourceCells[moveFrom];

            if (piece == Piece.EmptyCell)
            {
                notes = EmptyCell;
                return(new IllegalMove(move, source, notes));
            }

            // Side to move?
            var color = piece.Color();

            if (color != source.Core.Turn)
            {
                notes = (MoveAnnotations)piece.PieceType() | WrongSideToMove;
                return(new IllegalMove(move, source, notes));
            }

            // Move to occupied cell?
            var moveTo  = move.ToCell;
            var toPiece = (Piece)sourceCells[moveTo];

            if (toPiece != Piece.EmptyCell && toPiece.Color() == color)
            {
                notes = (MoveAnnotations)piece.PieceType() | ToOccupiedCell;
                return(new IllegalMove(move, source, notes));
            }
            notes = ValidateMove(cells, piece,
                                 moveFrom, moveTo, toPiece, source.Core.CastlingAvailability);
            if (toPiece != Piece.EmptyCell)
            {
                notes |= Capture;
            }
            // ---------------- SetupBoard ---------------------
            if ((notes & AllErrors) != 0)
            {
                return(new IllegalMove(move, source, notes));
            }
            if ((notes & EnPassant) != 0)
            {
                if (source.Core.EnPassant != moveTo % 16)
                {
                    notes |= HasNoEnPassant;
                    return(new IllegalMove(move, source, notes));
                }
            }
            else if ((notes & Promotion) != 0)
            {
                var proposedPromotion = move.PromoteTo;
                if (move.PromoteTo == PieceType.None)
                {
                    notes            |= MissingPromotionHint;
                    proposedPromotion = PieceType.Queen;
                }
                piece = proposedPromotion.With(color);
            }
            else if (move.PromoteTo != PieceType.None)
            {
                notes |= PromotionHintIsNotNeeded;
            }

            cells[moveTo] = (byte)piece;

            switch (piece)
            {
            case Piece.WhiteKing:
                whiteKingSquare = moveTo;
                break;

            case Piece.BlackKing:
                blackKingSquare = moveTo;
                break;
            }

            cells[moveFrom] = 0;
            var enPassantFile = new int?();

            if ((notes & (DoublePush | EnPassant | AllCastlings)) != 0)
            {
                if ((notes & DoublePush) != 0)
                {
                    enPassantFile = moveFrom % 16;
                }
                else if ((notes & EnPassant) != 0)
                {
                    cells[moveTo + (color == Color.White ? -16 : +16)] = 0;
                }
                else
                {
                    switch (notes)
                    {
                    case (King | WK):
                        cells[S.H1] = (byte)Piece.EmptyCell;
                        cells[S.F1] = (byte)Piece.WhiteRook;
                        break;

                    case (King | WQ):
                        cells[S.A1] = (byte)Piece.EmptyCell;
                        cells[S.D1] = (byte)Piece.WhiteRook;
                        break;

                    case (King | BK):
                        cells[S.H8] = (byte)Piece.EmptyCell;
                        cells[S.F8] = (byte)Piece.BlackRook;
                        break;

                    case (King | BQ):
                        cells[S.A8] = (byte)Piece.EmptyCell;
                        cells[S.D8] = (byte)Piece.BlackRook;
                        break;
                    }
                }
            }
            var isUnderCheck = source.Core.Turn == Color.White
                ? cells.IsSquareAttackedByBlack(whiteKingSquare)
                : cells.IsSquareAttackedByWhite(blackKingSquare);

            if (isUnderCheck)
            {
                notes |= MoveToCheck;
            }
            var castlings = source.Core.CastlingAvailability
                            & ~KilledAvailability(moveTo)
                            & ~KilledAvailability(moveFrom);

            // ---------------- ---------- ---------------------
            if ((notes & AllErrors) != 0)
            {
                return(new IllegalMove(move, source, notes));
            }

            var positionCore = new PositionCore(
                cells, color.Invert(), castlings, enPassantFile,
                whiteKingSquare, blackKingSquare);
            var legalMove = new LegalMove(
                move, source, positionCore, notes);

            return(legalMove);
        }
Exemple #12
0
        public static AnalyzedMove Validate(this Position source, Move move)
        {
            var whiteKingSquare = source.Core.WhiteKing;
            var blackKingSquare = source.Core.BlackKing;

            var sourceCells = source.Core.Cells;
            var cells = source.Core.GetCopyOfCells();

            MoveAnnotations notes;
            // Piece in the from cell?
            var moveFrom = move.FromCell;
            var piece = (Piece)sourceCells[moveFrom];
            if (piece == Piece.EmptyCell)
            {
                notes = EmptyCell;
                return new IllegalMove(move, source, notes);
            }

            // Side to move?
            var color = piece.Color();
            if (color != source.Core.Turn)
            {
                notes = (MoveAnnotations)piece.PieceType() | WrongSideToMove;
                return new IllegalMove(move, source, notes);
            }

            // Move to occupied cell?
            var moveTo = move.ToCell;
            var toPiece = (Piece)sourceCells[moveTo];
            if (toPiece != Piece.EmptyCell && toPiece.Color() == color)
            {
                notes = (MoveAnnotations)piece.PieceType() | ToOccupiedCell;
                return new IllegalMove(move, source, notes);
            }
            notes = ValidateMove(cells, piece,
                moveFrom, moveTo, toPiece, source.Core.CastlingAvailability);
            if (toPiece != Piece.EmptyCell) notes |= Capture;
            // ---------------- SetupBoard ---------------------
            if ((notes & AllErrors) != 0)
                return new IllegalMove(move, source, notes);
            if ((notes & EnPassant) != 0)
            {
                if (source.Core.EnPassant != moveTo % 16)
                {
                    notes |= HasNoEnPassant;
                    return new IllegalMove(move, source, notes);
                }
            }
            else if ((notes & Promotion) != 0)
            {
                var proposedPromotion = move.PromoteTo;
                if (move.PromoteTo == PieceType.None)
                {
                    notes |= MissingPromotionHint;
                    proposedPromotion = PieceType.Queen;
                }
                piece = proposedPromotion.With(color);
            }
            else if (move.PromoteTo != PieceType.None)
            {
                notes |= PromotionHintIsNotNeeded;
            }

            cells[moveTo] = (byte)piece;

            switch (piece)
            {
                case Piece.WhiteKing:
                    whiteKingSquare = moveTo;
                    break;
                case Piece.BlackKing:
                    blackKingSquare = moveTo;
                    break;
            }

            cells[moveFrom] = 0;
            var enPassantFile = new int?();
            if ((notes & (DoublePush | EnPassant | AllCastlings)) != 0)
            {
                if ((notes & DoublePush) != 0)
                {
                    enPassantFile = moveFrom % 16;
                }
                else if ((notes & EnPassant) != 0)
                {
                    cells[moveTo + (color == Color.White ? -16 : +16)] = 0;
                }
                else
                    switch (notes)
                    {
                        case (King | WK):
                            cells[S.H1] = (byte)Piece.EmptyCell;
                            cells[S.F1] = (byte)Piece.WhiteRook;
                            break;
                        case (King | WQ):
                            cells[S.A1] = (byte)Piece.EmptyCell;
                            cells[S.D1] = (byte)Piece.WhiteRook;
                            break;
                        case (King | BK):
                            cells[S.H8] = (byte)Piece.EmptyCell;
                            cells[S.F8] = (byte)Piece.BlackRook;
                            break;
                        case (King | BQ):
                            cells[S.A8] = (byte)Piece.EmptyCell;
                            cells[S.D8] = (byte)Piece.BlackRook;
                            break;
                    }
            }
            var isUnderCheck = source.Core.Turn == Color.White
                ? cells.IsSquareAttackedByBlack(whiteKingSquare)
                : cells.IsSquareAttackedByWhite(blackKingSquare);

            if (isUnderCheck)
            {
                notes |= MoveToCheck;
            }
            var castlings = source.Core.CastlingAvailability
                            & ~KilledAvailability(moveTo)
                            & ~KilledAvailability(moveFrom);

            // ---------------- ---------- ---------------------
            if ((notes & AllErrors) != 0)
                return new IllegalMove(move, source, notes);

            var positionCore = new PositionCore(
                cells, color.Invert(), castlings, enPassantFile,
                whiteKingSquare, blackKingSquare);
            var legalMove = new LegalMove(
                move, source, positionCore, notes);
            return legalMove;
        }
Exemple #13
0
        /// <summary>
        ///     Gets the begin of the SAN (standard algebraic notation) for a move - without promotion or check/checkmate
        ///     representation -  before the move is made
        ///     without checking the status of the game or if it's a valid move.
        /// </summary>
        /// <param name="position">The game</param>
        /// <param name="move">The move</param>
        /// <returns></returns>
        public static string GetSanBegin([NotNull] this Position position, LegalMove move)
        {
            if (position == null) throw new ArgumentNullException(nameof(position));

            var sb = new StringBuilder(6);

            if ((move.Annotations & (WK | BK)) != 0)
            {
                sb.Append("O-O");
            }
            else if ((move.Annotations & (WQ | BQ)) != 0)
            {
                sb.Append("O-O-O");
            }
            else
            {
                if ((move.Annotations & Pawn) != 0)
                {
                    if ((move.Annotations & Capture) != 0)
                        sb.Append(move.Move.FromCell.GetFile());
                }
                else
                {
                    sb.Append(((Piece) position.Core.Cells[move.Move.FromCell]).GetSymbol());

                    // TODO: move should have Piece prop?
                    var disambiguationList = new List<int>(
                        from m in position.GetAllLegalMoves()
                        where m.Move.FromCell != move.Move.FromCell
                              && m.Move.ToCell == move.Move.ToCell
                              && position.Core.Cells[move.Move.FromCell] == position.Core.Cells[m.Move.FromCell]
                        select m.Move.FromCell);

                    if (disambiguationList.Count > 0)
                    {
                        var uniqueFile = true;
                        // ReSharper disable LoopCanBeConvertedToQuery
                        // ReSharper disable ForCanBeConvertedToForeach
                        for (var index = 0; index < disambiguationList.Count; index++)
                        {
                            var m = disambiguationList[index];
                            if (move.Move.FromCell.GetFile() == m.GetFile())
                            {
                                uniqueFile = false;
                                break;
                            }
                        }
                        if (uniqueFile)
                        {
                            sb.Append(move.Move.FromCell.GetFile());
                        }
                        else
                        {
                            var uniqueRank = true;
                            for (var i = 0; i < disambiguationList.Count; i++)
                            {
                                var m = disambiguationList[i];
                                if (move.Move.FromCell.GetRank() == m.GetRank())
                                {
                                    uniqueRank = false;
                                    break;
                                }
                            }
                            // ReSharper restore ForCanBeConvertedToForeach
                            // ReSharper restore LoopCanBeConvertedToQuery
                            if (uniqueRank)
                            {
                                sb.Append(move.Move.FromCell.GetRank().ToString(CultureInfo.InvariantCulture));
                            }
                            else
                            {
                                sb.Append(move.Move.FromCell);
                            }
                        }
                    }
                }

                // if there is a capture, add capture notation
                if ((move.Annotations & Capture) == Capture)
                {
                    sb.Append('x');
                }

                // add destination square
                sb.Append(move.Move.ToCell);
            }

            return sb.ToString();
        }
Exemple #14
0
 public static string GetSan([NotNull] this Position position, LegalMove move)
 {
     if (position == null) throw new ArgumentNullException(nameof(position));
     return position.GetSanBegin(move) /*+ position.GetSanEnd(move)*/;
 }
Exemple #15
0
 private void PromoteMove(IList <LegalMove> moves, LegalMove moveToPromote)
 {
     moves.Remove(moveToPromote);
     moves.Insert(0, moveToPromote);
 }
Exemple #16
0
        /// <summary>
        ///     Gets the begin of the SAN (standard algebraic notation) for a move - without promotion or check/checkmate
        ///     representation -  before the move is made
        ///     without checking the status of the game or if it's a valid move.
        /// </summary>
        /// <param name="position">The game</param>
        /// <param name="move">The move</param>
        /// <returns></returns>
        public static string GetSanBegin([NotNull] this Position position, LegalMove move)
        {
            if (position == null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            var sb = new StringBuilder(6);

            if ((move.Annotations & (WK | BK)) != 0)
            {
                sb.Append("O-O");
            }
            else if ((move.Annotations & (WQ | BQ)) != 0)
            {
                sb.Append("O-O-O");
            }
            else
            {
                if ((move.Annotations & Pawn) != 0)
                {
                    if ((move.Annotations & Capture) != 0)
                    {
                        sb.Append(move.Move.FromCell.GetFile());
                    }
                }
                else
                {
                    sb.Append(((Piece)position.Core.Cells[move.Move.FromCell]).GetSymbol());

                    // TODO: move should have Piece prop?
                    var disambiguationList = new List <int>(
                        from m in position.GetAllLegalMoves()
                        where m.Move.FromCell != move.Move.FromCell &&
                        m.Move.ToCell == move.Move.ToCell &&
                        position.Core.Cells[move.Move.FromCell] == position.Core.Cells[m.Move.FromCell]
                        select m.Move.FromCell);

                    if (disambiguationList.Count > 0)
                    {
                        var uniqueFile = true;
                        // ReSharper disable LoopCanBeConvertedToQuery
                        // ReSharper disable ForCanBeConvertedToForeach
                        for (var index = 0; index < disambiguationList.Count; index++)
                        {
                            var m = disambiguationList[index];
                            if (move.Move.FromCell.GetFile() == m.GetFile())
                            {
                                uniqueFile = false;
                                break;
                            }
                        }
                        if (uniqueFile)
                        {
                            sb.Append(move.Move.FromCell.GetFile());
                        }
                        else
                        {
                            var uniqueRank = true;
                            for (var i = 0; i < disambiguationList.Count; i++)
                            {
                                var m = disambiguationList[i];
                                if (move.Move.FromCell.GetRank() == m.GetRank())
                                {
                                    uniqueRank = false;
                                    break;
                                }
                            }
                            // ReSharper restore ForCanBeConvertedToForeach
                            // ReSharper restore LoopCanBeConvertedToQuery
                            if (uniqueRank)
                            {
                                sb.Append(move.Move.FromCell.GetRank().ToString(CultureInfo.InvariantCulture));
                            }
                            else
                            {
                                sb.Append(move.Move.FromCell);
                            }
                        }
                    }
                }

                // if there is a capture, add capture notation
                if ((move.Annotations & Capture) == Capture)
                {
                    sb.Append('x');
                }

                // add destination square
                sb.Append(move.Move.ToCell);
            }

            return(sb.ToString());
        }