private bool mayKingMoveOrthogonallyHere(AttackedSquare clearMove, IEnumerable <AttackedSquare> attacksOnKing, List <AttackedSquare> attacks) { var kingSquare = (Square)attacksOnKing.First(); //need to detect if we're moving into check var anybodyAttackingThisSquare = attacks.Any(a => a.Index == clearMove.Index && a.AttackingSquare.Piece.Color != kingSquare.Piece.Color); if (anybodyAttackingThisSquare) { return(false); } //now make sure we're not ignoring the issue where an attack isn't displayed because the king was blocking the square //that he would move into, that is still being attacked by the original attacker. var isRankMove = GeneralEngine.GivenOrthogonalMove_IsItARankMove(clearMove.AttackingSquare.Index, clearMove.Index); //find all attackers who attack orthogonally and determine if they are on the same line var orthogonalAttacksOnKing = attacksOnKing.Where(a => orthogonalAttackers.Contains(a.AttackingSquare.Piece.PieceType)); foreach (var x in orthogonalAttacksOnKing) { var oxs = getEntireOrthogonalLine(isRankMove ? false : true, x); //if oxs contains the clearMove.Index, then the king has not moved out of check if (oxs.Contains(clearMove.Index)) { return(false); } } return(true); }
/// <summary> /// In the future, I should be passing back all the moves rather than a boolean /// that way the engine can have metadata about all the possible moves that /// could be taken and maybe offer a suggestion. /// </summary> /// <param name="attacksOnKing"></param> /// <param name="teamAttacks"></param> /// <returns></returns> private bool interpositionsExist(IEnumerable <AttackedSquare> attacksOnKing, IEnumerable <AttackedSquare> teamAttacks) { foreach (var attackOnKing in attacksOnKing) { var attackIsOrthogonal = GeneralEngine.IsOrthogonal(attackOnKing.AttackingSquare.Index, attackOnKing.Index); var attackIsDiagonal = DiagonalEngine.IsDiagonal(attackOnKing.AttackingSquare.Index, attackOnKing.Index); var range = new List <Square>(); if (attackIsOrthogonal) { var isRankMove = GeneralEngine.GivenOrthogonalMove_IsItARankMove(attackOnKing.AttackingSquare.Index, attackOnKing.Index); var oxs = getEntireOrthogonalLine(isRankMove, attackOnKing, true); var ixs = teamAttacks.Select(a => a.Index).Intersect(oxs); if (ixs.Any()) { return(true); } } else if (attackIsDiagonal) { var dxs = DiagonalEngine.GetDiagonalLine(attackOnKing.AttackingSquare.Index, attackOnKing.Index); //if dxs contains the clearMove.Index, then the king has not moved out of check var ixs = teamAttacks.Select(a => a.Index).Intersect(dxs); if (ixs.Any()) { return(true); } } //else it could be a knight attack, but no interpositions would exist } return(false); }
public List <AttackedSquare> GetOrthogonalLine(GameState gameState, Square square, Direction direction) { var attacks = new List <AttackedSquare>(); var currentPosition = square.Index; var lineTerminator = getOrthogonalLineTerminator(direction, currentPosition); var iterator = getIteratorByDirectionEnum(direction); var nextPositionInTheLine = currentPosition + iterator; for (var position = nextPositionInTheLine; position != lineTerminator; position = position + iterator) { var isValidCoordinate = GeneralEngine.IsValidCoordinate(position); if (!isValidCoordinate) { break; } var moveViability = GeneralEngine.DetermineMoveViability(gameState, square.Piece, position); //these conditions shouldn't occur if (!moveViability.IsValidCoordinate || moveViability.SquareToAdd == null) { continue; } var attack = new AttackedSquare(square, moveViability.SquareToAdd, isProtecting: moveViability.IsTeamPiece); attacks.Add(attack); if (moveViability.SquareToAdd.Occupied) { break; } } return(attacks); }
private bool isValidKnightMove(int position, int tempPosition, int file, int rank) { var isValidCoordinate = GeneralEngine.IsValidCoordinate(tempPosition); if (!isValidCoordinate) { return(false); } var tempCoord = NotationEngine.PositionToCoordinate(tempPosition); var tempFile = NotationEngine.FileToInt(tempCoord[0]); var tempRank = (int)tempCoord[1]; var fileDiff = Math.Abs(tempFile - file); var rankDiff = Math.Abs(tempRank - rank); if (fileDiff > 2 || fileDiff < 1) { return(false); } if (rankDiff > 2 || rankDiff < 1) { return(false); } return(true); }
public void IsOrthogonal() { Assert.IsTrue(GeneralEngine.IsOrthogonal(0, 7)); Assert.IsTrue(GeneralEngine.IsOrthogonal(7, 0)); Assert.IsTrue(GeneralEngine.IsOrthogonal(56, 0)); Assert.IsTrue(GeneralEngine.IsOrthogonal(0, 56)); Assert.IsFalse(GeneralEngine.IsOrthogonal(0, 9)); }
private bool kingMovesExist(GameState gameState, Color kingColor, IEnumerable <AttackedSquare> attacksOnKing) { var opponentAttacks = gameState.Attacks.Where(a => a.AttackingSquare.Piece.Color == kingColor.Reverse()).ToList(); var kingMoves = gameState.Attacks .Where(a => a.AttackingSquare.Piece.PieceType == PieceType.King && a.AttackingSquare.Piece.Color == kingColor && !a.IsProtecting ).ToList(); var clearMoves = kingMoves.Select(a => a.Index).Except(opponentAttacks.Select(b => b.Index)); if (!clearMoves.Any()) { return(false); } //todo: there is a bug here: when the king is checked like so (5rk1/5pbp/5Qp1/8/8/8/5PPP/3q2K1 w - - 0 1) //he is boxed in, but h1 looks a valid move for him because the Queen doesn't currently have it as an attack square //because the King is blocking it. //fix this by examining if the king is moving orthoganally or diagonally and determine if any attackers are on that line as well var clearMoveCount = clearMoves.Count(); foreach (var clearMoveIndex in clearMoves) { //clearMove.AttackerSquare is the king here //clearMove.Index is where he is going var clearMove = kingMoves.GetSquare(clearMoveIndex); var isOrthogonal = GeneralEngine.IsOrthogonal(clearMove.AttackingSquare.Index, clearMove.Index); var _mayKingMoveHere = false; if (isOrthogonal) { _mayKingMoveHere = this.mayKingMoveOrthogonallyHere(clearMove, attacksOnKing, gameState.Attacks); if (!_mayKingMoveHere) { clearMoveCount--; } } else { //has to be diagonal _mayKingMoveHere = this.mayKingMoveDiagonallyHere(clearMove, attacksOnKing, gameState.Attacks); if (!_mayKingMoveHere) { clearMoveCount--; } } } return(clearMoveCount > 0); }
private static (bool IsValid, bool CanAttackOccupyingPiece) basicMoveValidity(Color pieceColor, Square square) { if (!square.Occupied) { // go for it return(true, true); } // if it is your teammate, you can't do that if (GeneralEngine.IsTeamPiece(pieceColor, square.Piece)) { return(true, true); } else { return(true, false); } }
private void getKnightAttacks(GameState gameState, Square square, List <AttackedSquare> accumulator) { var squares = gameState.Squares; var currentPosition = square.Index; var pieceColor = square.Piece.Color; var attacks = new List <AttackedSquare>(); var coord = NotationEngine.PositionToCoordinate(currentPosition); var file = NotationEngine.FileToInt(coord[0]); var rank = (int)coord[1]; var potentialPositions = new List <int> { 6, 10, 15, 17, -6, -10, -15, -17 }; foreach (var potentialPosition in potentialPositions) { var position = currentPosition + potentialPosition; var _isValidKnightMove = isValidKnightMove(currentPosition, position, file, rank); var _isValidMove = isValidMove(accumulator, gameState, square, position, pieceColor); var _isValidCoordinate = GeneralEngine.IsValidCoordinate(position); if (!_isValidKnightMove || !_isValidMove.IsValid || !_isValidCoordinate) { continue; } var attackedSquare = squares.GetSquare(position); if (!attackedSquare.Occupied) { attacks.Add(new AttackedSquare(square, attackedSquare)); } else { attacks.Add(new AttackedSquare(square, attackedSquare, isProtecting: attackedSquare.Piece.Color == pieceColor)); } } if (attacks.Any()) { accumulator.AddRange(attacks); } }
public List <int> GetEntireRank(int rank) { return(GeneralEngine.GetEntireRank(rank)); }
public List <int> GetEntireFile(int file) { return(GeneralEngine.GetEntireFile(file)); }
private void getKingAttacks(GameState gameState, Square square, List <AttackedSquare> accumulator) { var attacks = new List <AttackedSquare>(); var squares = gameState.Squares; var offsets = getKingOffsets(square.Index); var pieceColor = square.Piece.Color; foreach (var offset in offsets) { var destinationPosition = square.Index + offset; var isValidCoordinate = GeneralEngine.IsValidCoordinate(destinationPosition); if (!isValidCoordinate) { continue; } var _isValidMove = isValidMove(accumulator, gameState, square, destinationPosition, pieceColor); if (!_isValidMove.IsValid) { continue; } attacks.Add( new AttackedSquare(square, squares.GetSquare(destinationPosition), isProtecting: !_isValidMove.CanAttackOccupyingPiece) ); } //********************* //castling stuff //********************* var castleAvailability = gameState.CastlingAvailability; if (pieceColor == Color.White) { if (castleAvailability.Contains("K")) { addCastleAttackIfPossible( square, attacks, squares, 7, new List <int> { 5, 6 }, 6 ); } if (castleAvailability.Contains("Q")) { addCastleAttackIfPossible( square, attacks, squares, 0, new List <int> { 1, 2, 3 }, 2 ); } } else if (pieceColor == Color.Black) { if (castleAvailability.Contains("k")) { addCastleAttackIfPossible( square, attacks, squares, 63, new List <int> { 61, 62 }, 62 ); } if (castleAvailability.Contains("q")) { addCastleAttackIfPossible( square, attacks, squares, 56, new List <int> { 57, 58, 59 }, 58 ); } } //********************* //end castling stuff //********************* if (!attacks.Any()) { return; } accumulator.AddRange(attacks); }
private (bool IsValid, bool CanAttackOccupyingPiece) isValidMove(List <AttackedSquare> attacks, GameState gameState, Square locationSquare, int destination, Color pieceColor) { var isValidCoordinate = GeneralEngine.IsValidCoordinate(destination); if (!isValidCoordinate || !gameState.Squares.Any(a => a.Index == destination)) { // throw new Exception($"Invalid position passed: { position }"); // probably doing this just as an easy way to not have to trim squares from every algorithm return(false, false); } var destinationSquare = gameState.Squares.GetSquare(destination); // if you're not a king you've got simpler rules // but if you're the king, you still have to obey the basics var basics = basicMoveValidity(pieceColor, destinationSquare); if (locationSquare.Piece.PieceType != PieceType.King || !basics.CanAttackOccupyingPiece) { return(basics); } // check to make sure that this move would get us out of check var newPositionAttacks = attacks.Where(a => a.Index == destination && a.AttackingSquare.Piece.Color != locationSquare.Piece.Color); if (newPositionAttacks.Any()) { // king can't move into check return(true, false); } // no-one is attacking the destination that we can tell, let's make sure // piece currently being attacked? var attacksOnThisPosition = attacks.Where(a => a.Index == locationSquare.Index && a.AttackingSquare.Piece.Color != locationSquare.Piece.Color); if (!attacksOnThisPosition.Any()) { // nobody is attacking this king now, or in the new position return(true, true); } var dangerSquares = attacksOnThisPosition.Where(a => _dangerPieceTypes.Contains(a.AttackingSquare.Piece.PieceType) && a.AttackingSquare.Piece.Color != locationSquare.Piece.Color); if (!dangerSquares.Any()) { // nobody dangerous is attacking this king now, or in the new position return(true, true); } // which direction are we moving? var directionInfo = GeneralEngine.GetDirectionInfo(locationSquare.Index, destination); var directionalDangerPieceTypes = directionInfo.IsDiagonal ? _diagonalDangerPieceTypes : _orthogonalFangerPieceTypes; var directionalDangerSquares = attacksOnThisPosition.Where(a => directionalDangerPieceTypes.Contains(a.AttackingSquare.Piece.PieceType) && a.AttackingSquare.Piece.Color != locationSquare.Piece.Color); foreach (var dangerSquare in directionalDangerSquares) { // is that going to get us out of the attack path that this piece would normally have? var dangerPieceDirectionInfo = GeneralEngine.GetDirectionInfo(dangerSquare.AttackingSquare.Index, locationSquare.Index); if (dangerPieceDirectionInfo.IsDiagonal && directionInfo.IsOrthogonal) { // won't intersect paths continue; } if (dangerPieceDirectionInfo.IsOrthogonal && directionInfo.IsDiagonal) { // won't intersect paths continue; } if (dangerPieceDirectionInfo.IsDiagonal && directionInfo.IsDiagonal) { // are we moving on the same diagonal plane? if (dangerPieceDirectionInfo.DiagonalDirection == directionInfo.DiagonalDirection) { // not safe return(false, false); } } if (dangerPieceDirectionInfo.IsOrthogonal && directionInfo.IsOrthogonal) { // are we moving on the same orthogonal plane? if (dangerPieceDirectionInfo.Direction == directionInfo.Direction) { // not safe return(false, false); } } } // safe return(true, true); }