/// <summary> /// Parses a regular move (without promotion information) fron its CAN. /// </summary> /// <param name="can"></param> /// <returns></returns> public static Move ParseRegularCAN(string can) { Move m = new Move(); try { if (can != null && can.Length >= 4) { m.from = (byte)((('8' - can[1]) << 3) + can[0] - 'a'); m.to = (byte)((('8' - can[3]) << 3) + can[2] - 'a'); } } catch { } return m; }
/// <summary> /// Tries to make a pseudo-legal move. /// Returns true if the move has been actually made, false otherwise. /// </summary> private bool Make(Move move) { // if this is a castling move, move the rook // the king will be moved with the usual move code later if ((move.bits & 2) != 0) { if (InCheck(side)) return false; int from, to; switch (move.to) { case G1: if (color[F1] != EMPTY || color[G1] != EMPTY || Attacks(F1, xside) || Attacks(G1, xside)) return false; from = H1; to = F1; break; case C1: if (color[B1] != EMPTY || color[C1] != EMPTY || color[D1] != EMPTY || Attacks(C1, xside) || Attacks(D1, xside)) return false; from = A1; to = D1; break; case G8: if (color[F8] != EMPTY || color[G8] != EMPTY || Attacks(F8, xside) || Attacks(G8, xside)) return false; from = H8; to = F8; break; case C8: if (color[B8] != EMPTY || color[C8] != EMPTY || color[D8] != EMPTY || Attacks(C8, xside) || Attacks(D8, xside)) return false; from = A8; to = D8; break; default: from = -1; to = -1; break; } // move the rook color[to] = color[from]; piece[to] = piece[from]; color[from] = EMPTY; piece[from] = EMPTY; } // back up information so the move can be taken back history[ply].move = move; history[ply].capture = piece[move.to]; history[ply].castle = castle; history[ply].ep = ep; history[ply].fifty = fifty; ply++; // update the castle, en passant, and fifty-move-draw variables castle &= castleMask[move.from] & castleMask[move.to]; ep = (move.bits & 8) != 0 ? (side == LIGHT ? move.to + 8 : move.to - 8) : -1; fifty = (move.bits & 17) != 0 ? 0 : fifty + 1; // move the piece color[move.to] = side; piece[move.to] = (move.bits & 32) != 0 ? move.promote : piece[move.from]; color[move.from] = EMPTY; piece[move.from] = EMPTY; // erase the pawn if this is an en passant move if ((move.bits & 4) != 0) { color[side == LIGHT ? move.to + 8 : move.to - 8] = EMPTY; piece[side == LIGHT ? move.to + 8 : move.to - 8] = EMPTY; } // switch sides side ^= 1; xside ^= 1; // test for legality // if we can capture the opponent's king, it's an illegal position and the move must be taken back if (InCheck(xside)) { TakeBack(); return false; } return true; }
/// <summary> /// Gets the next move. /// </summary> /// <param name="fen">The board configuration.</param> /// <param name="repetitionMoveCandidate"> /// The move that, if made, it will result a draw by repetition. /// The engine will try to avoid the move if there is a better one. /// </param> /// <param name="depth">Search depth level.</param> /// <returns></returns> public string GetNextMove(string fen, string repetitionMoveCandidate, int depth) { // the engine is still thinking, return if (thinking) { return null; } // initialize the engine try { Initialize(fen); } catch { return null; } int hash; // look in the opening book (OBookMem.cs) in memory buffer... short obMove=0; if (moves <= Settings_Default_OpeningBookMaxMoveNo) obMove=obookmem.OBookGet(hash = GetHashCode()); if(obMove>0) { Move move = new Move(); move.from = (byte)(obMove>> 8); move.to = (byte)(obMove & 255); return move.ToString(); } // look in the opening book if (book != null && moves <= Settings_Default_OpeningBookMaxMoveNo && book.ContainsKey(hash = GetHashCode())) { // get the list of available moves for this board List<short> list = book[hash]; //return a random move from the list short shortMove = list[random.Next(list.Count)]; Move move = new Move(); move.from = (byte)(shortMove >> 8); move.to = (byte)(shortMove & 255); return move.ToString(); } else { // set repetition move // a repetition move cannot be a promotion move, a castling move, a capture move or a pawn move // so the move bits and capture are 0 repMove = Move.ParseRegularCAN(repetitionMoveCandidate); // the depth must be between 1 and ChessEngineMaxDepth if (depth < 1) depth = 1; else if (depth > Settings_Default_ChessEngineMaxDepth) depth = Settings_Default_ChessEngineMaxDepth; // else if (depth > Settings.Default.ChessEngineMaxDepth) // depth = Settings.Default.ChessEngineMaxDepth; // search try { thinking = true; Think(depth); } catch { // the search has ended abruptly or something went wrong } finally { thinking = false; } // pv[0, 0] is the best move return pv[0, 0].ToString(); } }