public string Move(Player player, int startX, int startY, int endX, int endY) { if (GameStatus != Enum.GameStatus.ACTIVE) { return("The game is inactive, Status: " + GameStatus.ToString()); } else if (player != CurrentPlayerTurn) { return("Is not your turn!!"); } var initialSpot = Board.spots[startX][startY]; if (initialSpot.piece == null) { return("No piece in starting position"); } var endSpot = Board.spots[endX][endY]; if (endSpot.piece != null && endSpot.piece.IsWhite == initialSpot.piece.IsWhite) { return("There is a piece of the same color in the ending spot."); } if (initialSpot.piece.CanMove(Board, initialSpot, endSpot)) { Move move = new Move(player, initialSpot, endSpot); MakeMove(Board, move); Moves.Add(move); this.ChangeTurn(); } return(CurrentPlayerTurn.IsWhitePlayer == true ? "White player turn!." : "Black playr turn!"); }
public Move CreateMove() { var move = new Move(); Moves.Add(move); return(move); }
// CREATE public Moves Create(Moves pokemon) { pokemon.Id = nextId(); pokemon.Add(pokemon); SaveMoves(); return(pokemon); }
public SubmittedMoveResponse MakeMove(string origin, string destination, string promotion) { SubmittedMoveResponse response = new SubmittedMoveResponse() { Success = true, Error = null }; if (promotion != null && promotion.Length != 1) { response.Success = false; response.Error = "Invalid promotion."; response.Correct = SubmittedMoveResponse.INVALID_MOVE; return(response); } MoveType type = Current.Game.MakeMove(new Move(origin, destination, Current.Game.WhoseTurn, promotion?[0]), false); if (type == MoveType.Invalid) { response.Success = false; response.Error = "Invalid move."; response.Correct = SubmittedMoveResponse.INVALID_MOVE; } string promotionUpper = promotion?.ToUpperInvariant(); Moves.Add(string.Format("{0}-{1}{2}", origin, destination, promotion == null ? "" : "=" + promotionUpper)); string moveStr = origin + "-" + destination + (promotion != null ? "=" + promotionUpper : ""); return(MakeMoveAndDropCommon(response, moveStr)); }
/// <summary> /// Performs a move. /// </summary> /// <param name="move">The move.</param> /// <returns>A list of the spaces affected by the move, starting with the space /// of the move and including the spaces of all the pieces captured by the move.</returns> private IList <ISpace> Move(ISpace move) { var changedSpaces = new List <ISpace>(); // If move is a pass, return an empty list. if (move != null) { var newBoard = (IList <IList <State> >)Move( Board, CurrentPlayer == State.One, move); for (int row = 0; row < newBoard.Count; ++row) { for (int column = 0; column < newBoard[row].Count; ++column) { if (newBoard[row][column] != Board[row][column]) { changedSpaces.Add(new Space(row, column)); } } } changedSpaces.Remove(move); changedSpaces.Insert(0, move); Board = newBoard; } Moves.Add(move); return(changedSpaces); }
private async Task ExecuteLoadItemsCommand() { if (IsBusy) { return; } IsBusy = true; try { Moves.Clear(); var move = await DataStore.GetItemsAsync(true); foreach (var m in move) { Move temp = m; temp.name = UppercaseFirst(temp.name); Moves.Add(temp); } } catch (Exception ex) { Debug.WriteLine(ex); } finally { IsBusy = false; } }
public void AddPawnMovement(List <ChessPiece> board) { int minX = (int)Math.Max(Position.X - 1, 0); int maxX = (int)Math.Min(Position.X + 1, 7); int y = White ? (int)Position.Y - 1 : (int)Position.Y + 1; for (int x = minX; x <= maxX; x++) { if (!board.Exists(c => c.Position.X == x && c.Position.Y == y && c.White == White)) { int m = (int)(Math.Abs(Position.X - x) + Math.Abs(Position.Y - y)); if ((m == 1 && !board.Exists(c => c.Position.X == x && c.Position.Y == y)) || (m == 2 && board.Exists(c => c.Position.X == x && c.Position.Y == y && c.White != White))) { Moves.Add(new Vector2(x, y)); } } } if (Position.Y == 6 && White && !board.Exists(c => c.Position.X == Position.X && c.Position.Y == Position.Y - 2)) { Moves.Add(new Vector2(Position.X, Position.Y - 2)); } if (Position.Y == 1 && !White && !board.Exists(c => c.Position.X == Position.X && c.Position.Y == Position.Y + 2)) { Moves.Add(new Vector2(Position.X, Position.Y + 2)); } }
private GameStatus PlaySinglePlayerTurn(IPlayer player) { var status = GameStatus.Ongoing; MoveResult moveResult = MoveResult.Unknown; BoardPosition positionPlayed = null; string moveMessage = string.Empty; try { positionPlayed = PlayTurnWithTimeout(player); moveResult = _board.SetToken(_gameTokenFor[player], positionPlayed); } catch (TimeoutException) { moveResult = MoveResult.Timeout; } catch (Exception ex) { moveResult = MoveResult.Exception; moveMessage = ex.Message; } Moves.Add(new Move(_board.Copy(), player, positionPlayed, moveResult, ++_moveCount, moveMessage)); if (moveResult == MoveResult.ValidMove) { status = _checker.GetStatusFrom(_board); } return(status); }
private bool WhiteCaptureKingMoves(int x, int y, Board board) { if (y < 7 && x < 7) { if (board.tiles[x - 1, y + 1].Piece != null) { if (board.tiles[x - 1, y + 1].Piece.Color != Color && board.tiles[x - 1, y + 1].Piece.Type == "King") { Moves.Add(board.tiles[x - 1, y + 1]); return(true); } else if (y > 0 && x < 7) { if (board.tiles[x - 1, y - 1].Piece != null) { if (board.tiles[x - 1, y - 1].Piece.Color != Color && board.tiles[x - 1, y - 1].Piece.Type == "King") { Moves.Add(board.tiles[x - 1, y - 1]); return(true); } } } } } return(false); }
void PuzzleFinished(SubmittedMoveResponse response, bool correct) { CurrentPuzzleEndedUtc = DateTime.UtcNow; response.Correct = correct ? 1 : -1; response.ExplanationSafe = Current.ExplanationSafe; PastPuzzleIds.Add(Current.ID); if (!correct) { Moves.RemoveAt(Moves.Count - 1); FENs.RemoveAt(FENs.Count - 1); Checks.RemoveAt(Checks.Count - 1); response.FEN = FENs[FENs.Count - 1]; ChessGame correctGame = gameConstructor.Construct(Current.Variant, response.FEN); foreach (string move in PossibleVariations.First()) { string[] p = move.Split('-', '='); correctGame.ApplyMove(new Move(p[0], p[1], correctGame.WhoseTurn, p.Length == 2 ? null : new char?(p[2][0])), true); FENs.Add(correctGame.GetFen()); Checks.Add(correctGame.IsInCheck(correctGame.WhoseTurn) ? correctGame.WhoseTurn.ToString().ToLowerInvariant() : null); Moves.Add(move); } } response.ReplayFENs = FENs; response.ReplayChecks = Checks; response.ReplayMoves = Moves; }
private void Player_Move(object sender, MoveEvent e) { var move = e.Move; Moves.Add(move); var player = sender as Player; if (move.MovedPiece is Pawn) { HalfmovesSinceLastCaptureOrPawn = 0; } else { HalfmovesSinceLastCaptureOrPawn++; } var aff = player.Affiliation; if (aff == PlayerAffiliation.Black) { WhosTurn = PlayerAffiliation.White; Movecounter++; } else { WhosTurn = PlayerAffiliation.Black; } }
public MoveScore MakeMove(IMove move, Player p) { List <Box> results = SpeculateMove(move, p); bool foundSlot = false; foreach (Move m in AvailableMoves) { if (move.CompareTo(m) == 0) { m.SetPlayer(p); AvailableMoves.Remove(m); Moves.Add(m); foundSlot = true; break; } } System.Diagnostics.Debug.Assert(foundSlot); MoveScore score = MoveScore.Zero; if (results.Count > 0) { foreach (Box b in results) { m_Boxes.Add(b); score = BumpScore(score); } p.AddScore(results.Count); } return(score); }
private void MakeMove(Move move) { Board.MakeMove(move); Moves.Add(move); Fens.Add(Board.GetFenString()); NextColor = ChessBoard.InvertColor(NextColor); SelectedFen = Fens.Last(); }
////////////// MOVE FUNCTIONS //////////////// /// <summary> /// Learn a move by adding it to the monster's move list. /// </summary> /// <param name="newMove">Move to be learned.</param> public void LearnMove(LearnableMove newMove) { if (Moves.Count > MonsterBase.MaxNumberOfMoves) { return; } Debug.Log($"Learning {newMove.Base.Name}"); Moves.Add(new MoveObj(newMove.Base)); }
public CreatureContext(BattleContext battle, TrainerContext trainer, Creature creature) : base(creature) { Trainer = trainer; foreach (var move in creature.Moves) { Moves.Add(battle.CreateMove(this, move)); } }
//learn a new move public void LearnMove(LearnableMove moveToLearn) { //safety measure for now, max 4 moves if (Moves.Count > MaxNumberOfMoves) { return; } Moves.Add(new Move(moveToLearn.Base)); }
public void LearnMove(LearnableMove moveToLearn) { if (Moves.Count > PokemonBase.MaxNumOfMoves) { return; } Moves.Add(new Move(moveToLearn.Base)); }
public SugarGhubbyCard(bool newcard) : base(newcard) { Picture = "https://cdn.discordapp.com/attachments/460357767484407809/648290945162543124/Sugar_Ghubby.jpg"; Moves.Add(new Swipe(true)); Moves.Add(new SyrupSlide(true)); Moves.Add(new Devour(true)); Moves.Add(new StickyStomp(true)); TotalHP = 1000; CurrentHP = 1000; }
private void LoadMoves() { ByteStream dataStream = ROM.From("Moves"); ByteStream nameStream = ROM.From("MoveNames"); for (int index = 0; index <= 0xfa; index++) { Moves.Add(new GscMove(this, dataStream, nameStream)); } }
public override void EnterMove_number_indication(PGNParser.Move_number_indicationContext context) { if (_lastMoveNumber != 0) { Moves.Add(_lastMoveNumber, new List <string>(_lastMoves)); _lastMoves.Clear(); } _lastMoveNumber++; }
public IkaCard(bool newcard) : base(newcard) { HasPassive = false; HasUltimate = false; Moves.Add(new HystericalLaughter(true)); TotalHP = 500; CurrentHP = 500; }
/// <summary> /// Generate "lazy" moves for this piece, which is all usual legal moves, but also includes moves that put the king in check. /// </summary> /// <param name="moves"> /// Moves list that will be populated with lazy moves. /// </param> /// <param name="movesType"> /// Types of moves to include. e.g. All, or captures-only. /// </param> public void GenerateLazyMoves(Moves moves, Moves.MoveListNames movesType) { Square square; switch (movesType) { case Moves.MoveListNames.All: for (int i = 0; i < moveVectors.Length; i++) { square = Board.GetSquare(this.Base.Square.Ordinal + moveVectors[i]); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } } if (this.CanCastleKingSide) { moves.Add(0, 0, Move.MoveNames.CastleKingSide, this.Base, this.Base.Square, Board.GetSquare(this.Base.Square.Ordinal + 2), null, 0, 0); } if (this.CanCastleQueenSide) { moves.Add(Game.TurnNo, this.Base.LastMoveTurnNo, Move.MoveNames.CastleQueenSide, this.Base, this.Base.Square, Board.GetSquare(this.Base.Square.Ordinal - 2), null, 0, 0); } break; case Moves.MoveListNames.CapturesPromotions: for (int i = 0; i < moveVectors.Length; i++) { square = Board.GetSquare(this.Base.Square.Ordinal + moveVectors[i]); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } } break; } }
public void AddRookMovement(List <ChessPiece> board) { var horizontal = board.Where(p => p.Position.Y == Position.Y && p.Position.X != Position.X).OrderBy(k => Math.Abs(k.Position.X - Position.X)); var vertical = board.Where(p => p.Position.X == Position.X && p.Position.Y != Position.Y).OrderBy(k => Math.Abs(k.Position.Y - Position.Y)); int minX = 0; int maxX = 7; int minY = 0; int maxY = 7; if (horizontal != null && horizontal.Count() > 0) { foreach (var c in horizontal) { if (minX == 0 && c.Position.X < Position.X) { minX = (int)c.Position.X; } else if (maxX == 7 && c.Position.X > Position.X) { maxX = (int)c.Position.X; } } } if (vertical != null && vertical.Count() > 0) { foreach (var c in vertical) { if (minY == 0 && c.Position.Y < Position.Y) { minY = (int)c.Position.Y; } else if (maxY == 7 && c.Position.Y > Position.Y) { maxY = (int)c.Position.Y; } } } for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { if (x != Position.X && y == Position.Y || y != Position.Y && x == Position.X) { if (!board.Exists(c => c.Position.X == x && c.Position.Y == y && c.White == White)) { Moves.Add(new Vector2(x, y)); } } } } }
public void NextTurn() { Moves.RemoveAt(0); Moves.RemoveAt(0); _expectedEnemyMoves.RemoveAt(0); _expectedEnemyMoves.RemoveAt(0); Moves.Add(new Move()); Moves.Add(new Move()); _expectedEnemyMoves.Add(new Move()); _expectedEnemyMoves.Add(new Move()); }
public OdysseusOldCard(bool newcard) : base(newcard) { HasUltimate = false; HasPassive = false; Picture = "https://cdn.discordapp.com/attachments/645751180688883712/682271164588228635/2bcfd5db6dd1f86fa8f3915215cb708d.png"; Moves.Add(new ShotOfTheTrueKing(true)); TotalHP = 10; CurrentHP = 10; }
public OTrojanSoldierCard(bool newcard) : base(newcard) { HasUltimate = false; HasPassive = false; Picture = "https://cdn.discordapp.com/attachments/460357767484407809/682064436927660295/1703925ec2e7220367de1bbb0d5d8588.png"; Moves.Add(new Stab(true)); Moves.Add(new ShieldBlock(true)); TotalHP = 100; CurrentHP = 100; }
public OperationResult AddMove(Move move) { var fork = Board.MoveSnake(CurrentDirection); if (fork.Success) { var moveAdded = Moves.Add(move); return(moveAdded ? OperationResult.BuildSuccess() : OperationResult.BuildFailure(ErrorType.MoveAlreadyExsited)); } return(OperationResult.BuildFailure(fork.ErrorMessage)); }
public SuitorCard(bool newcard) : base(newcard) { HasUltimate = false; HasPassive = false; Picture = "https://cdn.discordapp.com/attachments/667958167078174771/683729206357655637/db2eaa4a3682bf58be49d1d762371ebf.png"; Moves.Add(new Throw(true)); Moves.Add(new Punch(true)); Moves.Add(new Flee(true)); TotalHP = 115; CurrentHP = 115; }
public CuteBunnyCard(bool newcard) : base(newcard) { HasUltimate = false; Picture = "https://cdn.discordapp.com/attachments/460357767484407809/649081931383701504/cute_bun.png"; Moves.Add(new Pomf(true)); Moves.Add(new Kiss(true)); Moves.Add(new Whiskers(true)); Moves.Add(new Cottontail(true)); TotalHP = 500; CurrentHP = 500; }
public Circle() { Moves.Add(new TranslationMove()); Moves.Add(new TranslationMove() { Amplitude = 10 }); Moves.Add(new TranslationMove() { Amplitude = 0.1 }); }
/// <summary> /// Generate "lazy" moves for this piece, which is all usual legal moves, but also includes moves that put the king in check. /// </summary> /// <param name="moves"> /// Moves list that will be populated with lazy moves. /// </param> /// <param name="movesType"> /// Types of moves to include. e.g. All, or captures-only. /// </param> public void GenerateLazyMoves(Moves moves, Moves.MoveListNames movesType) { Square square; switch (movesType) { case Moves.MoveListNames.All: square = Board.GetSquare(this.Base.Square.Ordinal + 33); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal + 18); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal - 14); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal - 31); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal - 33); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal - 18); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal + 14); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal + 31); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } break; case Moves.MoveListNames.CapturesPromotions: square = Board.GetSquare(this.Base.Square.Ordinal + 33); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal + 18); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal - 14); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal - 31); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal - 33); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal - 18); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal + 14); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } square = Board.GetSquare(this.Base.Square.Ordinal + 31); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } break; } }
/// <summary> /// Performs the search foe the best move, using a specialised form of alpha beta search, named Principal Variation Search (PVS) . http://chessprogramming.wikispaces.com/Alpha-Beta http://chessprogramming.wikispaces.com/Principal+Variation+Search /// </summary> /// <param name="player"> The player to play. The player is alternated at each new ply of search. </param> /// <param name="ply"> True depth in plies. Starts at the max search depth and is DECREMENTED as alpha beta get deeper. </param> /// <param name="variableDepth"> Variable depth which starts at the max search depth and is DECREMENTED as alpha beta get deeper. Its value is altered by search extension and reductions. Quiesence starts at depth 0. http://chessprogramming.wikispaces.com/Depth </param> /// <param name="alpha"> Alpha (α) is the lower bound, representing the minimum score that a node must reach in order to change the value of a previous node. http://chessprogramming.wikispaces.com/Alpha </param> /// <param name="beta"> Beta (β) is the upper bound of a score for the node. If the node value exceeds or equals beta, it means that the opponent will avoid this node, since his guaranteed score (Alpha of the parent node) is already greater. Thus, Beta is the best-score the opponent (min-player) could archive so far... http://chessprogramming.wikispaces.com/Beta </param> /// <param name="parentMove"> Move from the parent alpha beta call. </param> /// <param name="principalVariation"> The Principal variation (PV) is a sequence of moves is considered best and therefore expect to be played. This list of moves is collected during the alpha beta search. http://chessprogramming.wikispaces.com/Principal+variation </param> /// <param name="totalExtensionsOrReductions"> Holds a counter indicating the number of search extensions or reductions at the current search depth. A positive nunber indicates there have been extensions in a previous ply, negative indicates a reduction. http://chessprogramming.wikispaces.com/Extensions http://chessprogramming.wikispaces.com/Reductions </param> /// <param name="analysisParentBranch"> When move analysis is enabled, a tree of search moves is collected in this variable, which can be viewed in the GUI. </param> /// <returns> The score of the best move. </returns> /// <exception cref="ForceImmediateMoveException">Raised when the user requests for thinking to be terminated, and immediate move to made.</exception> private int AlphaBetaPvs( Player player, int ply, int variableDepth, int alpha, int beta, Move parentMove, Moves principalVariation, int totalExtensionsOrReductions, Moves analysisParentBranch) { // TODO Try hash and killer moves, before generating all moves. It will save time. // Score of the move being examined in the move loop. int val; // Type of hash entry that will be stored in the Transposition Table. http://chessprogramming.wikispaces.com/Transposition+Table HashTable.HashTypeNames hashType = HashTable.HashTypeNames.Alpha; // The best move found at this node. Assigned if/when alpha is improved. Move bestMove = null; // Indicates that this entire node is a PV Node. http://chessprogramming.wikispaces.com/Node+Types bool isPvNode = false; // A counter of the number of legal moves we've examines in this node so far. int legalMovesAttempted = 0; // Indicates whether the player-to-play is in check at this node. bool isInCheck = player.IsInCheck; // Exit immediately, if instructed to do so. i.e. the user has click the "Move Now" button. if (this.forceExitWithMove) { throw new ForceImmediateMoveException(); } // This node has reached 3-move-repetition, so return the current score, and don't bother searching any deeper. if (parentMove != null && parentMove.IsThreeMoveRepetition) { return player.Score; } if (totalExtensionsOrReductions > this.MaxExtensions) { this.MaxExtensions = totalExtensionsOrReductions; } // Check if this node (position) is in the tranposition (hash) table, and if appropriate, return the score stored there. if (ply != this.SearchDepth && (val = HashTable.ProbeForScore(Board.HashCodeA, Board.HashCodeB, ply, alpha, beta, player.Colour)) != HashTable.NotFoundInHashTable) { // High values of "val" indicate that a checkmate has been found if (val > 1000000 || val < -1000000) { Comment(parentMove, "Mate: " + (this.MaxSearchDepth - variableDepth) + " "); if (this.MaxSearchDepth - variableDepth > 0) { val /= this.MaxSearchDepth - variableDepth; } } return val; } // Depth <= 0 means we're into Quiescence searching if (variableDepth <= 0) { return this.Quiesce( player, ply, variableDepth, alpha, beta, parentMove, principalVariation, analysisParentBranch); } // Generate "lazy" moves (lazy means we include moves that put our own king in check) Moves movesPossible = new Moves(); player.GenerateLazyMoves(movesPossible, Moves.MoveListNames.All); // Stores the PV that is local to this node and it's children. Moves localPrincipalVariation = new Moves(); if (Game.EnableNullMovePruning) { // Adaptive Null-move pruning // http://chessprogramming.wikispaces.com/Null+Move+Pruning int r = variableDepth > 6 ? 3 : 2; if (variableDepth > (r + 1) && parentMove != null && parentMove.Name != Move.MoveNames.NullMove && Game.Stage != Game.GameStageNames.End && !isInCheck) { Move moveNull = new Move(Game.TurnNo, 0, Move.MoveNames.NullMove, null, null, null, null, 0, 0); val = -this.AlphaBetaPvs( player.OpposingPlayer, ply - 1, variableDepth - r - 1, -beta, -beta + 1, moveNull, localPrincipalVariation, totalExtensionsOrReductions, null); if (val >= beta) { return beta; } } } // Get last iteration's best move from the transposition Table Move moveHash = HashTable.ProbeForBestMove(Board.HashCodeA, Board.HashCodeB, player.Colour); // Get Killers Move moveKillerA = KillerMoves.RetrieveA(ply); Move moveKillerB = KillerMoves.RetrieveB(ply); Move moveKillerA2 = KillerMoves.RetrieveA(ply + 2); Move moveKillerB2 = KillerMoves.RetrieveB(ply + 2); // Get move at same ply from previous iteration's principal variation. int indexPv = this.SearchDepth - ply; Move movePv = null; if (indexPv < this.LastPrincipalVariation.Count) { movePv = this.LastPrincipalVariation[indexPv]; } // Sort moves this.SortBestMoves( movesPossible, variableDepth, movePv, moveHash, moveKillerA, moveKillerA2, moveKillerB, moveKillerB2, player); if (ply == this.SearchDepth) { this.TotalPositionsToSearch = movesPossible.Count; this.SearchPositionNo = 0; } foreach (Move move in movesPossible) { // Make the move Move moveMade = move.Piece.Move(move.Name, move.To); this.PositionsSearchedThisTurn++; this.PositionsSearchedThisIteration++; moveMade.DebugComment += move.DebugComment; if (ply == this.SearchDepth) { this.SearchPositionNo++; this.CurrentMoveSearched = moveMade; if (this.SearchMoveConsideredEvent != null) { this.SearchMoveConsideredEvent(); } } // This move put our player in check, so abort, and skip to next move. if (player.IsInCheck) { Move.Undo(moveMade); continue; } legalMovesAttempted++; if (bestMove == null) { bestMove = moveMade; } if (Game.CaptureMoveAnalysisData) { // Add moves to post-move analysis tree, if option set by user if (parentMove == null || parentMove.Name != Move.MoveNames.NullMove) { if (analysisParentBranch != null) { analysisParentBranch.Add(moveMade); } } moveMade.Moves = new Moves(); } int extensionOrReduction = 0; // Extensions // http://chessprogramming.wikispaces.com/Extensions ApplyExtensions(ref extensionOrReduction, movesPossible, moveMade, parentMove); // Reductions // http://chessprogramming.wikispaces.com/Reductions this.ApplyReductions( ref extensionOrReduction, totalExtensionsOrReductions, ply, variableDepth, isPvNode, isInCheck, alpha, player, moveMade, legalMovesAttempted, localPrincipalVariation); if (Game.EnablePvsSearch && isPvNode) { // Attempt a Principal Variation Search (PVS) using a zero window. http://chessprogramming.wikispaces.com/Principal+Variation+Search val = -this.AlphaBetaPvs( player.OpposingPlayer, ply - 1, (variableDepth + extensionOrReduction) - 1, -alpha - 1, -alpha, moveMade, localPrincipalVariation, totalExtensionsOrReductions + extensionOrReduction, moveMade.Moves); if ((val > alpha) && (val < beta)) { // PVS failed. Have to re-search using a full alpha-beta window. Comment(moveMade, "-PVS-WIN- "); if (Game.CaptureMoveAnalysisData && parentMove != null && parentMove.Name != Move.MoveNames.NullMove) { moveMade.Moves.Clear(); } val = -this.AlphaBetaPvs( player.OpposingPlayer, ply - 1, (variableDepth + extensionOrReduction) - 1, -beta, -alpha, moveMade, localPrincipalVariation, totalExtensionsOrReductions + extensionOrReduction, moveMade.Moves); } else { Comment(moveMade, "-F- "); } } else { // Not a PV node, so just do a normal alpha-beta search. val = -this.AlphaBetaPvs( player.OpposingPlayer, ply - 1, (variableDepth + extensionOrReduction) - 1, -beta, -alpha, moveMade, localPrincipalVariation, totalExtensionsOrReductions + extensionOrReduction, moveMade.Moves); } move.Score = moveMade.Score = val; // Take back the move Move.Undo(moveMade); if (val >= beta) { // Test for a beta cut-off http://chessprogramming.wikispaces.com/Beta-Cutoff alpha = beta; moveMade.Beta = beta; hashType = HashTable.HashTypeNames.Beta; bestMove = moveMade; Comment(parentMove, "(CUT) "); if (moveMade.PieceCaptured == null) { // Add this cut move to the history heuristic. HistoryHeuristic.Record(player.Colour, moveMade.From.Ordinal, moveMade.To.Ordinal, ply * ply); // Add this cut move to the killer move heuristic. KillerMoves.RecordPossibleKillerMove(ply, moveMade); } goto Exit; } if (val > alpha) { // Test if the move made can improve alpha. // If so, then it become the new best move, and local PV for this node. http://chessprogramming.wikispaces.com/Node Comment(moveMade, "*PV* " + alpha + "<" + val); isPvNode = true; /* This is a PV node */ alpha = val; hashType = HashTable.HashTypeNames.Exact; bestMove = moveMade; // Collect the Prinicial Variation lock (principalVariation) { principalVariation.Clear(); principalVariation.Add(moveMade); foreach (Move moveCopy in localPrincipalVariation) { principalVariation.Add(moveCopy); } } } moveMade.Alpha = alpha; moveMade.Beta = beta; if (!Game.IsInAnalyseMode && !this.MyBrain.IsPondering && this.SearchDepth > MinSearchDepth && this.MyBrain.ThinkingTimeElpased > this.MaxSearchTimeAllowed) { throw new ForceImmediateMoveException(); } } if (!isPvNode && parentMove != null) { // All positions were search at this node. It's not a PV node, and no beta cut-off occured, // therefore it's an ALL node. http://chessprogramming.wikispaces.com/Node+Types#ALL Comment(parentMove, "(ALL) "); } // Check for Stalemate if (legalMovesAttempted == 0) { alpha = player.Score; } Exit: // Record best move if (bestMove != null) { HashTable.RecordHash( Board.HashCodeA, Board.HashCodeB, ply, alpha, hashType, bestMove.From.Ordinal, bestMove.To.Ordinal, bestMove.Name, player.Colour); } else { HashTable.RecordHash( Board.HashCodeA, Board.HashCodeB, ply, alpha, hashType, -1, -1, Move.MoveNames.NullMove, player.Colour); } return alpha; }
/// <summary> /// The purpose of quiescence search is to only evaluate "quiet" positions, or positions where there are no winning tactical moves to be made. /// This search is needed to avoid the horizon effect. /// http://chessprogramming.wikispaces.com/Quiescence+Search /// </summary> /// <param name="player"> /// The player to play. The player is alternated at each new ply of search. /// </param> /// <param name="ply"> /// True depth in plies. Starts at the max search depth and is DECREMENTED as alpha beta get deeper. /// </param> /// <param name="variableDepth"> /// Depth which starts at one and INCREASES as the search deepens. /// Its value is altered by search extension and reductions. Quiesence starts at depth 0. /// http://chessprogramming.wikispaces.com/Depth /// </param> /// <param name="alpha"> /// Alpha (α) is the lower bound, representing the minimum score that a node must reach in order to change the value of a previous node. /// http://chessprogramming.wikispaces.com/Alpha /// </param> /// <param name="beta"> /// Beta (β) is the upper bound of a score for the node. If the node value exceeds or equals beta, it means that the opponent will avoid this node, /// since his guaranteed score (Alpha of the parent node) is already greater. Thus, Beta is the best-score the opponent (min-player) could archive so far... /// http://chessprogramming.wikispaces.com/Beta /// </param> /// <param name="parentMove"> /// Move from the parent alpha beta call. /// </param> /// <param name="principalVariation"> /// The Principal variation (PV) is a sequence of moves is considered best and therefore expect to be played. /// This list of moves is collected during the alpha beta search. /// http://chessprogramming.wikispaces.com/Principal+variation /// </param> /// <param name="analysisParentBranch"> /// When move analysis is enabled, a tree of search moves is collected in this variable, which can be viewed in the GUI. /// </param> /// <returns> /// The best move for the player. /// </returns> private int Quiesce( Player player, int ply, int variableDepth, int alpha, int beta, Move parentMove, Moves principalVariation, Moves analysisParentBranch) { // Gather some stats if (variableDepth < this.MaxQuiesenceDepthReached) { this.MaxQuiesenceDepthReached = variableDepth; } // Calculate the score this.Evaluations++; int standPat = player.Score; if (standPat > 1000000 || standPat < -1000000) { // TODO Unit test that negative depths produce score that reduce as they get deeper. The purpose here is to make deeper checks score less than shallower ones. // TODO Investigate whether reduced mate scores are constantly reduced when going in and out of hashtable. standPat /= this.MaxSearchDepth - ply; } if (standPat >= beta) { Comment(parentMove, "(Q:PAT-CUT) "); return beta; } if (alpha < standPat) { // Comment(parentMove, "(Q:PAT-ALPHA) "); alpha = standPat; } // Generate moves - Captures and promotions only Moves movesPossible = new Moves(); player.GenerateLazyMoves(0, movesPossible, Moves.MoveListNames.CapturesPromotions, null); // Get move at same ply from previous iteration's principal variation. int indexPv = this.MaxSearchDepth - ply; Move movePv = null; if (indexPv < this.LastPrincipalVariation.Count) { movePv = this.LastPrincipalVariation[indexPv]; } // Sort moves this.SortBestMoves(movesPossible, 0, movePv, null, null, null, null, null, player); Moves movesPv = new Moves(); foreach (Move move in movesPossible) { if (move.Score < 0) { // Losing capture from SEE, so skip this move. continue; } // Make capture, but skip illegal moves Move moveThis = move.Piece.Move(move.Name, move.To); if (player.IsInCheck) { Move.Undo(moveThis); continue; } this.PositionsSearched++; // If this is the deepest stage of iterative deepening, then capture move analysis data. if (Game.CaptureMoveAnalysisData && this.SearchDepth == this.MaxSearchDepth) { // Add moves to post-move analysis tree, if option set by user analysisParentBranch.Add(moveThis); moveThis.Moves = new Moves(); } moveThis.Score = -this.Quiesce( player.OpposingPlayer, ply - 1, variableDepth - 1, -beta, -alpha, parentMove, movesPv, moveThis.Moves); // Undo the capture move Move.Undo(moveThis); if (moveThis.Score >= beta) { Comment(parentMove, "(Q:CUT) "); return beta; } if (moveThis.Score > alpha) { alpha = moveThis.Score; // Comment(parentMove, "(Q:^ALPHA) "); // Collect the Prinicial Variation lock (principalVariation) { principalVariation.Clear(); principalVariation.Add(moveThis); foreach (Move moveCopy in movesPv) { principalVariation.Add(moveCopy); } } } } return alpha; }
/// <summary> /// Performs the search foe the best move, using a specialised form of alpha beta search, named Principal Variation Search (PVS) . /// http://chessprogramming.wikispaces.com/Alpha-Beta /// http://chessprogramming.wikispaces.com/Principal+Variation+Search /// </summary> /// <param name="player"> /// The player to play. The player is alternated at each new ply of search. /// </param> /// <param name="ply"> /// True depth in plies. Starts at the max search depth and is DECREMENTED as alpha beta get deeper. /// </param> /// <param name="variableDepth"> /// Variable depth which starts at the max search depth and is DECREMENTED as alpha beta get deeper. /// Its value is altered by search extension and reductions. Quiesence starts at depth 0. /// http://chessprogramming.wikispaces.com/Depth /// </param> /// <param name="alpha"> /// Alpha (α) is the lower bound, representing the minimum score that a node must reach in order to change the value of a previous node. /// http://chessprogramming.wikispaces.com/Alpha /// </param> /// <param name="beta"> /// Beta (β) is the upper bound of a score for the node. If the node value exceeds or equals beta, it means that the opponent will avoid this node, /// since his guaranteed score (Alpha of the parent node) is already greater. Thus, Beta is the best-score the opponent (min-player) could archive so far... /// http://chessprogramming.wikispaces.com/Beta /// </param> /// <param name="parentMove"> /// Move from the parent alpha beta call. /// </param> /// <param name="principalVariation"> /// The Principal variation (PV) is a sequence of moves is considered best and therefore expect to be played. /// This list of moves is collected during the alpha beta search. /// http://chessprogramming.wikispaces.com/Principal+variation /// </param> /// <param name="totalExtensionsAndReducations"> /// Holds a counter indicating the number of search extensions or reductions at the current search depth. /// A positive nunber indicates there have been extensions in a previous ply, negative indicates a reduction. /// http://chessprogramming.wikispaces.com/Extensions /// http://chessprogramming.wikispaces.com/Reductions /// </param> /// <param name="analysisParentBranch"> /// When move analysis is enabled, a tree of search moves is collected in this variable, which can be viewed in the GUI. /// </param> /// <returns> /// The score of the best move. /// </returns> /// <exception cref="ForceImmediateMoveException"> /// Raised when the user requests for thinking to be terminated, and immediate move to made. /// </exception> private int AlphaBetaPvs( Player player, int ply, int variableDepth, int alpha, int beta, Move parentMove, Moves principalVariation, int totalExtensionsAndReducations, Moves analysisParentBranch) { // TODO Don't generate hash or killer moves. int val; HashTable.HashTypeNames hashType = HashTable.HashTypeNames.Alpha; Move moveBest = null; bool blnPvNode = false; int intLegalMovesAttempted = 0; bool blnIsInCheck = player.IsInCheck; if (this.forceExitWithMove) { throw new ForceImmediateMoveException(); } this.PositionsSearched++; if (parentMove != null && parentMove.IsThreeMoveRepetition) { return player.Score; } if (ply != this.SearchDepth && (val = HashTable.ProbeHash(Board.HashCodeA, Board.HashCodeB, ply, alpha, beta, player.Colour)) != HashTable.NotFoundInHashTable) { // High values of "val" indicate that a checkmate has been found if (val > 1000000 || val < -1000000) { Comment(parentMove, "Mate: " + (this.MaxSearchDepth - variableDepth) + " "); if (this.MaxSearchDepth - variableDepth > 0) { val /= this.MaxSearchDepth - variableDepth; } } return val; } // Generate moves Moves movesPossible = new Moves(); player.GenerateLazyMoves(variableDepth, movesPossible, Moves.MoveListNames.All, null); // Depth <= 0 means we're into Quiescence searching if (variableDepth <= 0) { return this.Quiesce( player, ply, variableDepth, alpha, beta, parentMove, principalVariation, analysisParentBranch); } Moves movesPv = new Moves(); // Adaptive Null-move forward pruning int r = variableDepth > 6 ? 3 : 2; if (variableDepth > (r + 1) && parentMove != null && parentMove.Name != Move.MoveNames.NullMove && Game.Stage != Game.GameStageNames.End && !blnIsInCheck) { Move moveNull = new Move(Game.TurnNo, 0, Move.MoveNames.NullMove, null, null, null, null, 0, 0); val = -this.AlphaBetaPvs( player.OpposingPlayer, ply - 1, variableDepth - r - 1, -beta, -beta + 1, moveNull, movesPv, totalExtensionsAndReducations, null); if (val >= beta) { return beta; } } // Get last iteration's best move from the transposition Table Move moveHash = HashTable.ProbeForBestMove(Board.HashCodeA, Board.HashCodeB, player.Colour); // Get Killers Move moveKillerA = KillerMoves.RetrieveA(ply); Move moveKillerB = KillerMoves.RetrieveB(ply); Move moveKillerA2 = KillerMoves.RetrieveA(ply + 2); Move moveKillerB2 = KillerMoves.RetrieveB(ply + 2); // Get move at same ply from previous iteration's principal variation. int indexPv = this.SearchDepth - ply; Move movePv = null; if (indexPv < this.LastPrincipalVariation.Count) { movePv = this.LastPrincipalVariation[indexPv]; } // Sort moves this.SortBestMoves( movesPossible, variableDepth, movePv, moveHash, moveKillerA, moveKillerA2, moveKillerB, moveKillerB2, player); if (ply == this.SearchDepth) { this.TotalPositionsToSearch = movesPossible.Count; this.SearchPositionNo = 0; } foreach (Move move in movesPossible) { Move moveThis = move.Piece.Move(move.Name, move.To); moveThis.DebugComment += move.DebugComment; if (ply == this.SearchDepth) { this.SearchPositionNo++; this.CurrentMoveSearched = moveThis; if (this.SearchMoveConsideredEvent != null) { this.SearchMoveConsideredEvent(); } } // This move put the player in check, so abort and move onto next move. if (player.IsInCheck) { Move.Undo(moveThis); continue; } intLegalMovesAttempted++; if (moveBest == null) { moveBest = moveThis; } if (Game.CaptureMoveAnalysisData) { // Add moves to post-move analysis tree, if option set by user if (parentMove == null || parentMove.Name != Move.MoveNames.NullMove) { analysisParentBranch.Add(moveThis); } moveThis.Moves = new Moves(); } // http://chessprogramming.wikispaces.com/Extensions // TODO Consider fractional extensions. // Search Extensions int intExtensionOrReduction = 0; if (movesPossible.Count == 1) { // Single Response intExtensionOrReduction = 1; Comment(moveThis, "E-1REP "); } else if (parentMove != null && parentMove.IsEnemyInCheck) { // Check evasion intExtensionOrReduction = 1; Comment(moveThis, "E-CHK "); } else if (parentMove != null && parentMove.PieceCaptured != null && moveThis.PieceCaptured != null && parentMove.PieceCaptured.BasicValue == moveThis.PieceCaptured.BasicValue && parentMove.To == moveThis.To) { // Recapture piece of same basic value (on the same square) intExtensionOrReduction = 1; Comment(moveThis, "E-RECAP "); } else if (moveThis.Piece.Name == Piece.PieceNames.Pawn && ((moveThis.Piece.Player.Colour == Player.PlayerColourNames.White && moveThis.To.Rank == 6) || (moveThis.Piece.Player.Colour == Player.PlayerColourNames.Black && moveThis.To.Rank == 1))) { // Pawn push to 7th rank intExtensionOrReduction = 1; Comment(moveThis, "E-PAWN7 "); } // Reductions // Only reduce if this move hasn't already been extended (or reduced). if (!blnPvNode && intExtensionOrReduction == 0) { if (variableDepth > 2 && !blnIsInCheck && moveThis.PieceCaptured == null && !moveThis.IsEnemyInCheck) { int[] margin = { 0, 0, 0, 5000, 5000, 7000, 7000, 9000, 9000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000 }; // int intLazyEval = this.TotalPieceValue - this.OtherPlayer.TotalPieceValue; int intLazyEval = player.Score; if (alpha > intLazyEval + margin[variableDepth]) { intExtensionOrReduction = -1; Comment(moveThis, "R-MARG" + (margin[variableDepth] / 1000) + " "); } } // Futility Pruning http://chessprogramming.wikispaces.com/Futility+Pruning if (!blnIsInCheck && intExtensionOrReduction == 0) { switch (variableDepth) { case 2: case 3: //case 4: if (moveThis.PieceCaptured == null && !move.IsEnemyInCheck) { int intLazyEval = player.Score; switch (variableDepth) { case 2: // Standard Futility Pruning if (intLazyEval + 3000 <= alpha) { intExtensionOrReduction = -1; Comment(moveThis, "R-FUT3 "); } break; case 3: // Extended Futility Pruning if (intLazyEval + 5000 <= alpha) { intExtensionOrReduction = -1; Comment(moveThis, "R-FUT5 "); } break; case 4: // Deep Futility Pruning if (intLazyEval + 9750 <= alpha) { intExtensionOrReduction = -1; Comment(moveThis, "R-FUT9 "); } break; } } break; } } // Late Move Reductions http://chessprogramming.wikispaces.com/Late+Move+Reductions // Reduce if move is 1) low in the search order and 2) has a poor history score. /* if (!blnIsInCheck && intExtensionOrReduction == 0 && moveThis.PieceCaptured == null) { if (intLegalMovesAttempted > 3) { int historyScore = History.Retrieve(player.Colour, moveThis.From.Ordinal, moveThis.To.Ordinal); if (historyScore == 0) { intExtensionOrReduction = -1; Comment(moveThis, "R-LMR "); } } } */ } /* if (intExtension>0 && intTotalExtensions>=m_intSearchDepth) { intExtension = 0; } */ if (totalExtensionsAndReducations > this.MaxExtensions) { this.MaxExtensions = totalExtensionsAndReducations; Comment(moveThis, "^MaxExt: " + totalExtensionsAndReducations + " "); } /* #if DEBUG // Avoid to break in a zero window research so alpha + 1 < beta if ((alpha + 1 < beta) && DebugMatchVariation(m_intSearchDepth - ply, moveThis)) Debugger.Break(); #endif */ if (blnPvNode) { // Attempt a Principal Variation Search (PVS) using a zero window. http://chessprogramming.wikispaces.com/Principal+Variation+Search val = -this.AlphaBetaPvs( player.OpposingPlayer, ply - 1, (variableDepth + intExtensionOrReduction) - 1, -alpha - 1, -alpha, moveThis, movesPv, totalExtensionsAndReducations + intExtensionOrReduction, moveThis.Moves); if ((val > alpha) && (val < beta)) { // PVS failed. Have to re-search using a full alpha-beta window. Comment(moveThis, "-PVS-WIN- "); if (Game.CaptureMoveAnalysisData && parentMove != null && parentMove.Name != Move.MoveNames.NullMove) { moveThis.Moves.Clear(); } val = -this.AlphaBetaPvs( player.OpposingPlayer, ply - 1, (variableDepth + intExtensionOrReduction) - 1, -beta, -alpha, moveThis, movesPv, totalExtensionsAndReducations + intExtensionOrReduction, moveThis.Moves); } else { Comment(moveThis, "-F- "); } } else { // Not a PV node, so just do a normal alpha-beta search. val = -this.AlphaBetaPvs( player.OpposingPlayer, ply - 1, (variableDepth + intExtensionOrReduction) - 1, -beta, -alpha, moveThis, movesPv, totalExtensionsAndReducations + intExtensionOrReduction, moveThis.Moves); } move.Score = moveThis.Score = val; Move.Undo(moveThis); if (val >= beta) { alpha = beta; moveThis.Beta = beta; hashType = HashTable.HashTypeNames.Beta; moveBest = moveThis; Comment(parentMove, "(CUT) "); if (moveThis.PieceCaptured == null) { // Add this cut move to the history heuristic. History.Record(player.Colour, moveThis.From.Ordinal, moveThis.To.Ordinal, ply * ply); // Add this cut move to the killer move heuristic. KillerMoves.RecordPossibleKillerMove(ply, moveThis); } goto Exit; } if (val > alpha) { Comment(moveThis, "*PV* " + alpha + "<" + val); blnPvNode = true; /* This is a PV node */ alpha = val; hashType = HashTable.HashTypeNames.Exact; moveBest = moveThis; // Collect the Prinicial Variation lock (principalVariation) { principalVariation.Clear(); principalVariation.Add(moveThis); foreach (Move moveCopy in movesPv) { principalVariation.Add(moveCopy); } } // #if DEBUG // Debug.WriteLineIf((ply == m_intSearchDepth) && (ply > 1), string.Format("{0} {1} {2}", ply, PvLine(principalVariation), alpha)); // #endif } moveThis.Alpha = alpha; moveThis.Beta = beta; if (!Game.IsInAnalyseMode && !this.MyBrain.IsPondering && this.SearchDepth > MinSearchDepth && MyBrain.ThinkingTimeElpased > this.MaxSearchTimeAllowed) { throw new ForceImmediateMoveException(); } } if (!blnPvNode && parentMove != null) { Comment(parentMove, "(ALL) "); } // Check for Stalemate if (intLegalMovesAttempted == 0) { alpha = player.Score; } Exit: // Record best move if (moveBest != null) { HashTable.RecordHash( Board.HashCodeA, Board.HashCodeB, ply, alpha, hashType, moveBest.From.Ordinal, moveBest.To.Ordinal, moveBest.Name, player.Colour); } else { HashTable.RecordHash( Board.HashCodeA, Board.HashCodeB, ply, alpha, hashType, -1, -1, Move.MoveNames.NullMove, player.Colour); } return alpha; }
/// <summary> /// Generate "lazy" moves for this piece, which is all usual legal moves, but also includes moves that put the king in check. /// </summary> /// <param name="moves"> /// Moves list that will be populated with lazy moves. /// </param> /// <param name="movesType"> /// Types of moves to include. e.g. All, or captures-only. /// </param> public void GenerateLazyMoves(Moves moves, Moves.MoveListNames movesType) { Square square; switch (movesType) { case Moves.MoveListNames.All: for (int i = 0; i < moveVectors.Length; i++) { square = Board.GetSquare(this.Base.Square.Ordinal + moveVectors[i]); if (square != null && (square.Piece == null || (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } } if (this.CanCastleKingSide) { moves.Add(0, 0, Move.MoveNames.CastleKingSide, this.Base, this.Base.Square, Board.GetSquare(this.Base.Square.Ordinal + 2), null, 0, 0); } if (this.CanCastleQueenSide) { moves.Add(Game.TurnNo, this.Base.LastMoveTurnNo, Move.MoveNames.CastleQueenSide, this.Base, this.Base.Square, Board.GetSquare(this.Base.Square.Ordinal - 2), null, 0, 0); } break; case Moves.MoveListNames.CapturesPromotions: for (int i = 0; i < moveVectors.Length; i++) { square = Board.GetSquare(this.Base.Square.Ordinal + moveVectors[i]); if (square != null && (square.Piece != null && (square.Piece.Player.Colour != this.Base.Player.Colour && square.Piece.IsCapturable))) { moves.Add(0, 0, Move.MoveNames.Standard, this.Base, this.Base.Square, square, square.Piece, 0, 0); } } break; } }