private void RefreshPathsFeature(IBoardState <ChessPieceEntity> boardState, Colours currentPlayer) { if (FeatureFlags.CachingPaths) { // Need proper boardstate key I think, currently a few tests fail, I guess around some state related // so something not encoded in the textboard (enpassant and castle viability namely) var stateKey = ChessGameConvert.SerialiseBoard(boardState); if (_stateCache.TryGetValue(stateKey, out var items)) { boardState.UpdatePaths(items); } else { RefreshChessPaths(boardState, currentPlayer); if (FeatureFlags.CachingPaths) { _stateCache.Add(stateKey, boardState.GetItems().ToArray()); } } } else { RefreshChessPaths(boardState, currentPlayer); } }
/// <summary> /// /// </summary> /// <param name="boardState"></param> /// <param name="move"></param> /// <param name="performCheckTest"> /// This has a small performance impact when reverse engineering SAN from the board move /// (approx 20ms a game on my rig) which is why it's optional and defaults to off</param> /// <returns></returns> public StandardAlgebraicNotation BuildFrom(IBoardState <ChessPieceEntity> boardState, BoardMove move, bool performCheckTest = false) { var fromItem = boardState.GetItem(move.From); var piece = fromItem.Item.Piece; int?fromFile = null; int?fromRank = null; var toFile = move.To.X; var toRank = move.To.Y; var moveType = boardState.IsEmpty(move.To) ? SanMoveTypes.Move : SanMoveTypes.Take; // Are they any other pieces, // of same type as the from item // that can also move to the same location var otherPieces = boardState.GetItems() .Where(i => !i.Location.Equals(move.From)) .Where(i => i.Item.Is(fromItem.Item.Player, fromItem.Item.Piece)) .ThatCanMoveTo(move.To); // TODO: That I need this resharper disable is probably a smell // ReSharper disable PossibleMultipleEnumeration if (otherPieces.Any()) { fromFile = move.From.X; var file = fromFile; otherPieces = otherPieces .Where(i => i.Location.X == file); } if (otherPieces.Any()) { fromRank = move.From.Y; otherPieces = new List <LocatedItem <ChessPieceEntity> >(); } if (otherPieces.Any()) { Throw.InvalidSan($"Unable to disambiguate {move}"); } // ReSharper restore PossibleMultipleEnumeration if (piece == ChessPieceName.Pawn && moveType == SanMoveTypes.Take) { fromFile = fromItem.Location.X; } ChessPieceName?promotionPiece = null; if (move.ExtraData is ChessPieceEntityFactory.ChessPieceEntityFactoryTypeExtraData data) { promotionPiece = data.PieceName; } var inCheck = performCheckTest && _checkDetectionService.DoesMoveCauseCheck(boardState, move); return(new StandardAlgebraicNotation(piece, fromFile, fromRank, toFile, toRank, moveType, promotionPiece, inCheck)); }
private static void RefreshChessPaths(IBoardState <ChessPieceEntity> boardState, Colours whoseTurn) { // NOTE: Kings cannot move in to check, so we regenerate their state last so the know // all of the enemy piece attack paths var nonKings = boardState.GetItems().Where(i => i.Item.EntityType != (int)ChessPieceName.King); var kings = boardState.GetItems().Where(i => i.Item.EntityType == (int)ChessPieceName.King).ToList(); boardState.RefreshPathsFor(nonKings); // NOTE: Kings can't move next to each other, so we update the enemy kings state first so that we can // so when regen the friendly kings state it knows where the enemy kings can move to, and therefore // where it cannot if (whoseTurn == Colours.White) { boardState.RefreshPathsFor(kings.Where(k => k.Item.Owner == (int)Colours.Black)); boardState.RefreshPathsFor(kings.Where(k => k.Item.Owner == (int)Colours.White)); } else { boardState.RefreshPathsFor(kings.Where(k => k.Item.Owner == (int)Colours.White)); boardState.RefreshPathsFor(kings.Where(k => k.Item.Owner == (int)Colours.Black)); } }
public void RefreshAllPaths(IBoardState <ChessPieceEntity> boardState, int currentPlayer) { RefreshPathsFeature(boardState, (Colours)currentPlayer); // NOTE: IMPORTANT: Kings must be evaluated last to ensure that moves // from other pieces that would cause check are generated first! // kings have an EntityType of int.MaxValue var boardStateGetAllItemLocations = boardState.GetItems() .OrderBy(i => i.Item.EntityType) .Select(i => i.Location).ToList(); foreach (var loc in boardStateGetAllItemLocations) { RemovePathsThatContainMovesThatLeaveUsInCheck(boardState, loc); } }
// TODO: Needs tests public PlayerState CurrentPlayerState( IBoardState <ChessPieceEntity> boardState, Colours currentPlayer ) { var king = boardState.GetItems((int)currentPlayer, (int)ChessPieceName.King).Single(); var locationUnderCheck = IsLocationUnderCheck(boardState, king.Location, king.Item.Player); if (locationUnderCheck.result) { return(CheckForCheckMate(boardState, king, locationUnderCheck.attacker)); } return(PlayerState.None); }
public void RefreshAllPaths(IBoardState <TEntity> boardState, int currentPlayer) => boardState.GetItems().ToList() .ForEach(boardState.RegenerateValidatedPaths);