toSan method

String toSan(
  1. Move move, {
  2. List<Move>? moves,
  3. bool checks = true,
})

Returns the SAN (Standard Algebraic Notation) representation of a move. Optionally, provide moves - a list of legal moves in the current position, which is used to determine the disambiguator. Use this if you need speed and have already generated the list of moves elsewhere. If checks is false, the '+' or '#' part of the SAN string will not be computed, which vastly increases efficiency in cases like PGN parsing.

Implementation

String toSan(Move move, {List<Move>? moves, bool checks = true}) {
  if (move is PassMove) return move.algebraic();
  if (move is GatingMove) {
    String san = '${toSan(move.child, checks: checks)}/'
        '${variant.pieces[move.dropPiece].symbol}';
    if (move.castling) {
      String dropSq = move.dropOnRookSquare
          ? size.squareName(move.child.castlingPieceSquare!)
          : size.squareName(move.from);
      san = '$san$dropSq';
    }
    // a hack, will be reworked eventually
    if (san.contains('+')) {
      san = '${san.replaceAll('+', '')}+';
    }
    if (san.contains('#')) {
      san = '${san.replaceAll('#', '')}#';
    }
    return san;
  }
  if (move is! StandardMove && move is! DropMove) return '';
  String san = '';
  if (move.castling) {
    move = move as StandardMove;
    // if queenside is the only castling option, render it as 'O-O'
    String kingside = 'O-O';
    String queenside = variant.castlingOptions.kingside ? 'O-O-O' : kingside;
    san = ([Castling.k, Castling.bk].contains(move.castlingDir))
        ? kingside
        : queenside;
  } else {
    if (move is DropMove) {
      PieceDefinition pieceDef = variant.pieces[move.piece];
      san = move.algebraic(size);
      if (!pieceDef.type.noSanSymbol) {
        san = '${pieceDef.symbol.toUpperCase()}$san';
      }
    } else {
      move = move as StandardMove;
      int piece = board[move.from].type;
      PieceDefinition pieceDef = variant.pieces[piece];
      String disambiguator = getDisambiguator(move, moves);
      if (pieceDef.type.noSanSymbol) {
        if (move.capture) san = size.squareName(move.from)[0];
      } else {
        san = pieceDef.symbol;
      }
      if (disambiguator.isNotEmpty) {
        san =
            pieceDef.type.noSanSymbol ? disambiguator : '$san$disambiguator';
      }
      if (move.capture) san = '${san}x';
      san = '$san${size.squareName(move.to)}';

      if (move.promotion) {
        san = '$san=${variant.pieces[move.promoPiece!].symbol}';
      }
    }
  }
  if (checks) {
    bool ok = makeMove(move, false);
    if (!ok) return 'invalid';
    if (inCheck || won) {
      san = '$san${won ? '#' : '+'}';
    }
    undo();
  }
  return san;
}