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")); }
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) })); }
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); }
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)*/); }
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)); }
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)); }
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)); }
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); } }
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); }); } }
public WeightedMove(LegalMove move, int newX, int newY) { this.Move = move; this.NewX = newX; this.NewY = newY; }
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); }
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; }
/// <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(); }
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)*/; }
private void PromoteMove(IList <LegalMove> moves, LegalMove moveToPromote) { moves.Remove(moveToPromote); moves.Insert(0, moveToPromote); }
/// <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()); }