Skip to main content
Glama
positionPrompter.ts18.5 kB
import { TacticalBoard } from "../themes/tacticalBoard.js"; import { BoardState, KingSafety, MaterialInfo, PieceMobility, PositionalPawn, SideAttackerDefenders, SidePiecePlacement, SideSquareControl, SpaceControl, } from "../types/types.js"; import { Chess } from "chess.js"; import { TempoCalculator } from "../themes/temoCalculator.js"; export class PositionPrompter { private state: BoardState; private sections: string[]; private tactical: TacticalBoard; private tempoCalculator: TempoCalculator; constructor(state: BoardState) { this.state = state; this.tactical = new TacticalBoard(state.fen); this.tempoCalculator = new TempoCalculator(this.state, this.tactical, new Chess(state.fen).turn()); this.sections = []; } public generatePrompt(): string { if (!this.state) { return "<board_state>Invalid FEN provided</board_state>"; } this.sections = []; this.sections.push("<detailed_board_analysis>"); this.addGameStatus(); this.addMaterialAnalysis(); this.addPiecePositions(); this.addKingSafetyAnalysis(); this.addCastlingRights(); this.addPieceMobility(); this.addSpaceControl(); this.addSquareColorControl(); this.addPawnStructureAnalysis(); this.addTacticalAnalysis(); this.addTempoAnalysis(); this.addAttackDefenseDetails(); this.sections.push("</detailed_board_analysis>"); return this.sections.join("\n"); } private addGameStatus(): void { this.sections.push("<game_status>"); this.sections.push(`FEN: ${this.state.fen}`); this.sections.push(`Move Number: ${this.state.moveNumber}`); this.sections.push( `Active Player: ${ this.state.sidetomove === "white" ? "White to move" : "Black to move" }` ); this.sections.push(`Game Phase: ${this.state.gamePhase.toUpperCase()}`); if (this.state.isCheckmate) { this.sections.push("Game Status: CHECKMATE - Game is over"); } else if (this.state.isStalemate) { this.sections.push("Game Status: STALEMATE - Game is drawn"); } else if (this.state.isGameOver) { this.sections.push("Game Status: GAME OVER - No legal moves available"); } else { this.sections.push("Game Status: ACTIVE GAME - Normal play continues"); } this.sections.push( `Total Legal Moves Available: ${this.state.legalMoves.length}` ); if ( this.state.legalMoves.length > 0 && this.state.legalMoves.length <= 15 ) { this.sections.push( `All Legal Moves: ${this.state.legalMoves.join(", ")}` ); } else if (this.state.legalMoves.length > 15) { this.sections.push( `Sample Legal Moves: ${this.state.legalMoves .slice(0, 15) .join(", ")}... (${this.state.legalMoves.length - 15} more)` ); } this.sections.push("</game_status>"); } private addTempoAnalysis(): void { const tempoDetails = this.tempoCalculator.getTempoExplanation(); for (const detail of tempoDetails) { this.sections.push(detail); } } private addMaterialAnalysis(): void { this.sections.push("\n<material_analysis>"); const materialDiff = this.state.white.materialScore.materialvalue - this.state.black.materialScore.materialvalue; this.addPlayerMaterial("WHITE", this.state.white.materialScore); this.addPlayerMaterial("BLACK", this.state.black.materialScore); this.sections.push("\nMATERIAL BALANCE:"); if (materialDiff > 0) { this.sections.push( ` White has a material advantage of +${materialDiff} points` ); this.addMaterialAdvantageDescription(materialDiff); } else if (materialDiff < 0) { const absMatDiff = Math.abs(materialDiff); this.sections.push( ` Black has a material advantage of +${absMatDiff} points` ); this.addMaterialAdvantageDescription(absMatDiff); } else { this.sections.push( ` Material is EQUAL - both sides have ${this.state.white.materialScore.materialvalue} points` ); } this.sections.push("</material_analysis>"); } private addPlayerMaterial(color: string, materialScore: MaterialInfo): void { this.sections.push(`${color} PIECES:`); this.sections.push( ` Total Material Value: ${materialScore.materialvalue} points` ); this.sections.push( ` Queens: ${materialScore.piececount.queens} (${ materialScore.piececount.queens * 9 } points)` ); this.sections.push( ` Rooks: ${materialScore.piececount.rooks} (${ materialScore.piececount.rooks * 5 } points)` ); this.sections.push( ` Bishops: ${materialScore.piececount.bishops} (${ materialScore.piececount.bishops * 3 } points)` ); this.sections.push( ` Knights: ${materialScore.piececount.knights} (${ materialScore.piececount.knights * 3 } points)` ); this.sections.push( ` Pawns: ${materialScore.piececount.pawns} (${ materialScore.piececount.pawns * 1 } points)` ); this.sections.push( ` Bishop Pair Bonus: ${ materialScore.bishoppair ? "YES (+0.5 points strategic value)" : "NO" }` ); if (color === "WHITE") this.sections.push(""); // Add spacing between white and black } private addMaterialAdvantageDescription(advantage: number): void { if (advantage >= 9) { this.sections.push( ` This is equivalent to approximately a QUEEN advantage` ); } else if (advantage >= 5) { this.sections.push( ` This is equivalent to approximately a ROOK advantage` ); } else if (advantage >= 3) { this.sections.push( ` This is equivalent to approximately a MINOR PIECE advantage` ); } else if (advantage >= 1) { this.sections.push( ` This is equivalent to approximately a PAWN advantage` ); } } private addPiecePositions(): void { this.sections.push("\n<piece_positions>"); this.addPlayerPiecePositions("WHITE", this.state.white.pieceplacementScore); this.addPlayerPiecePositions("BLACK", this.state.black.pieceplacementScore); this.sections.push("</piece_positions>"); } private addPlayerPiecePositions(color: string, placement: SidePiecePlacement): void { this.sections.push(`${color} PIECE LOCATIONS:`); this.sections.push(` King: ${placement.kingplacement[0] || "MISSING"}`); this.sections.push( ` Queens: ${ placement.queenplacement.length > 0 ? placement.queenplacement.join(", ") : "None" }` ); this.sections.push( ` Rooks: ${ placement.rookplacement.length > 0 ? placement.rookplacement.join(", ") : "None" }` ); this.sections.push( ` Bishops: ${ placement.bishopplacement.length > 0 ? placement.bishopplacement.join(", ") : "None" }` ); this.sections.push( ` Knights: ${ placement.knightplacement.length > 0 ? placement.knightplacement.join(", ") : "None" }` ); this.sections.push( ` Pawns: ${ placement.pawnplacement.length > 0 ? placement.pawnplacement.join(", ") : "None" }` ); if (color === "WHITE") this.sections.push(""); // Add spacing } private addKingSafetyAnalysis(): void { this.sections.push("\n<king_safety_analysis>"); this.addPlayerKingSafety("WHITE", this.state.white.kingSafetyScore); this.addPlayerKingSafety("BLACK", this.state.black.kingSafetyScore); this.sections.push("</king_safety_analysis>"); } private addPlayerKingSafety(color: string, kingSafety: KingSafety): void { this.sections.push(`${color} KING SAFETY:`); this.sections.push(` King Position: ${kingSafety.kingsquare}`); this.sections.push( ` Enemy Attackers on King: ${kingSafety.attackerscount} pieces attacking the king` ); this.sections.push( ` Friendly Defenders of King: ${kingSafety.defenderscount} (higher = better) pieces defending the king` ); this.sections.push( ` Pawn Shield Strength: ${kingSafety.pawnshield} (higher is better protection)` ); if(color === "WHITE"){ this.sections.push( ` King Safety Advantage: ${kingSafety.kingsafetysadvantage} (higher is safer, negative means danger)` ); }else{ this.sections.push( ` King Safety Advantage: ${kingSafety.kingsafetysadvantage} (higher is danger, negative means safer)` ); } this.sections.push( ` Castling Status: ${ kingSafety.hascastled ? "King HAS castled (safer)" : "King has NOT castled" }` ); this.sections.push( ` Castling Rights: ${ kingSafety.cancastle ? "Can still castle" : "Cannot castle anymore" }` ); if (color === "WHITE") this.sections.push(""); // Add spacing } private addCastlingRights(): void { this.sections.push("\n<castling_rights>"); this.sections.push("WHITE CASTLING:"); this.sections.push( ` Kingside (O-O): ${ this.state.white.castlingScore.kingside ? "AVAILABLE" : "NOT AVAILABLE" }` ); this.sections.push( ` Queenside (O-O-O): ${ this.state.white.castlingScore.queenside ? "AVAILABLE" : "NOT AVAILABLE" }` ); this.sections.push("\nBLACK CASTLING:"); this.sections.push( ` Kingside (O-O): ${ this.state.black.castlingScore.kingside ? "AVAILABLE" : "NOT AVAILABLE" }` ); this.sections.push( ` Queenside (O-O-O): ${ this.state.black.castlingScore.queenside ? "AVAILABLE" : "NOT AVAILABLE" }` ); this.sections.push("</castling_rights>"); } private addPieceMobility(): void { this.sections.push("\n<piece_mobility>"); this.addPlayerMobility("WHITE", this.state.white.pieceMobilityScore); this.addPlayerMobility("BLACK", this.state.black.pieceMobilityScore); const mobilityDiff = this.state.white.pieceMobilityScore.totalmobility - this.state.black.pieceMobilityScore.totalmobility; if (mobilityDiff > 5) { this.sections.push( `\nMOBILITY ADVANTAGE: White has ${mobilityDiff} more squares of mobility (significantly more active pieces)` ); } else if (mobilityDiff < -5) { this.sections.push( `\nMOBILITY ADVANTAGE: Black has ${Math.abs( mobilityDiff )} more squares of mobility (significantly more active pieces)` ); } else if (mobilityDiff !== 0) { this.sections.push( `\nMOBILITY: ${mobilityDiff > 0 ? 'White' : 'Black'} has a slight mobility advantage (${Math.abs(mobilityDiff)} squares)` ); } else { this.sections.push( `\nMOBILITY: Both sides have equal piece mobility (${this.state.white.pieceMobilityScore.totalmobility} squares each)` ); } this.sections.push("</piece_mobility>"); } private addPlayerMobility(color: string, mobility: PieceMobility): void { this.sections.push( `${color} PIECE MOBILITY (number of squares each piece type can move to):` ); this.sections.push(` Queen Mobility: ${mobility.queenmobility} squares`); this.sections.push(` Rook Mobility: ${mobility.rookmobility} squares`); this.sections.push(` Bishop Mobility: ${mobility.bishopmobility} squares`); this.sections.push(` Knight Mobility: ${mobility.knightmobility} squares`); this.sections.push( ` Total Mobility Score: ${mobility.totalmobility} squares (higher = more active pieces)` ); if (color === "WHITE") this.sections.push(""); } private addSpaceControl(): void { this.sections.push("\n<space_control>"); this.addPlayerSpaceControl("WHITE", this.state.white.spaceScore); this.addPlayerSpaceControl("BLACK", this.state.black.spaceScore); const centerControlDiff = this.state.white.spaceScore.centerspacecontrolscore - this.state.black.spaceScore.centerspacecontrolscore; const totalSpaceDiff = this.state.white.spaceScore.totalspacecontrolscore - this.state.black.spaceScore.totalspacecontrolscore; if (centerControlDiff > 3) { this.sections.push( `\nCENTER CONTROL: White dominates the center (+${centerControlDiff} advantage)` ); } else if (centerControlDiff < -3) { this.sections.push( `\nCENTER CONTROL: Black dominates the center (+${Math.abs( centerControlDiff )} advantage)` ); } else if (centerControlDiff !== 0) { this.sections.push( `\nCENTER CONTROL: ${centerControlDiff > 0 ? 'White' : 'Black'} has slight center advantage (+${Math.abs(centerControlDiff)})` ); } else { this.sections.push( `\nCENTER CONTROL: Both sides have equal center control` ); } if (totalSpaceDiff > 5) { this.sections.push( `OVERALL SPACE: White has significantly more space (+${totalSpaceDiff} total advantage)` ); } else if (totalSpaceDiff < -5) { this.sections.push( `OVERALL SPACE: Black has significantly more space (+${Math.abs( totalSpaceDiff )} total advantage)` ); } else if (totalSpaceDiff !== 0) { this.sections.push( `OVERALL SPACE: ${totalSpaceDiff > 0 ? 'White' : 'Black'} has slight space advantage (+${Math.abs(totalSpaceDiff)})` ); } else { this.sections.push(`OVERALL SPACE: Both sides control equal space`); } this.sections.push("</space_control>"); } private addPlayerSpaceControl(color: string, spaceScore: SpaceControl): void { this.sections.push(`${color} SPACE CONTROL:`); this.sections.push( ` Center Control Score: ${spaceScore.centerspacecontrolscore} (attacks on central squares d4,d5,e4,e5,c4,c5,f4,f5)` ); this.sections.push( ` Flank Control Score: ${spaceScore.flankspacecontrolscore} (attacks on flank squares a4,a5,b4,b5,g4,g5,h4,h5)` ); this.sections.push( ` Total Space Control: ${spaceScore.totalspacecontrolscore} (higher is better)` ); if (color === "WHITE") this.sections.push(""); // Add spacing } private addSquareColorControl(): void { this.sections.push("\n<square_color_control>"); this.addPlayerSquareControl("WHITE", this.state.white.squareControlScore); this.addPlayerSquareControl("BLACK", this.state.black.squareControlScore); this.sections.push("</square_color_control>"); } private addPlayerSquareControl( color: string, squareControl: SideSquareControl ): void { this.sections.push(`${color} SQUARE COLOR INFLUENCE:`); this.sections.push( ` Light Squares Controlled: ${squareControl.lightSquareControl} pieces on light squares` ); this.sections.push( ` Dark Squares Controlled: ${squareControl.darkSqaureControl} pieces on dark squares` ); this.sections.push( ` Light Square Pieces: ${ squareControl.lightSquares.join(", ") || "None" }` ); this.sections.push( ` Dark Square Pieces: ${squareControl.darkSquares.join(", ") || "None"}` ); if (color === "WHITE") this.sections.push(""); // Add spacing } private addPawnStructureAnalysis(): void { this.sections.push("\n<pawn_structure_analysis>"); this.addPlayerPawnStructure("WHITE", this.state.white.positionalScore); this.addPlayerPawnStructure("BLACK", this.state.black.positionalScore); this.sections.push("</pawn_structure_analysis>"); } private addPlayerPawnStructure( color: string, positional: PositionalPawn ): void { this.sections.push(`${color} PAWN STRUCTURE:`); this.sections.push( ` Doubled Pawns: ${positional.doublepawncount} (weakness - pawns on same file)` ); this.sections.push( ` Isolated Pawns: ${positional.isolatedpawncount} (weakness - no friendly pawns on adjacent files)` ); this.sections.push( ` Backward Pawns: ${positional.backwardpawncount} (weakness - pawns that cannot advance safely)` ); this.sections.push( ` Passed Pawns: ${positional.passedpawncount} (strength - no enemy pawns blocking path to promotion)` ); if(color === "WHITE"){ this.sections.push( ` Pawn Structure Advantage Score: ${positional.positionalAdvatange} (higher is better - negative is worse pawn structure)` ); }else { this.sections.push( ` Pawn Structure Advantage Score: ${positional.positionalAdvatange} (negative is better - positive is worse pawn strucutre)` ); } if (color === "WHITE") this.sections.push(""); // Add spacing } private addTacticalAnalysis(): void { this.sections.push("\n<tactical_analysis>"); // Get the full tactical summary const tacticalSummary = this.tactical.toString(); // Split into lines and add each line to sections const lines = tacticalSummary.split('\n'); for (const line of lines) { this.sections.push(line); } this.sections.push("</tactical_analysis>"); } private addAttackDefenseDetails(): void { this.sections.push("\n<attack_defense_details>"); this.addPlayerAttackDefense( "WHITE", this.state.whitepieceattackerdefenderinfo ); this.addPlayerAttackDefense( "BLACK", this.state.blackpieceattackerdefenderinfo ); this.sections.push("</attack_defense_details>"); } private addPlayerAttackDefense( color: string, attackDefenseInfo: SideAttackerDefenders ): void { this.sections.push(`${color} PIECE ATTACK/DEFENSE STATUS:`); this.sections.push( ` Pawns: ${attackDefenseInfo.pawnInfo.attackerscount} attackers, ${attackDefenseInfo.pawnInfo.defenderscount} defenders` ); this.sections.push( ` Knights: ${attackDefenseInfo.knightInfo.attackerscount} attackers, ${attackDefenseInfo.knightInfo.defenderscount} defenders` ); this.sections.push( ` Bishops: ${attackDefenseInfo.bishopInfo.attackerscount} attackers, ${attackDefenseInfo.bishopInfo.defenderscount} defenders` ); this.sections.push( ` Rooks: ${attackDefenseInfo.rookInfo.attackerscount} attackers, ${attackDefenseInfo.rookInfo.defenderscount} defenders` ); this.sections.push( ` Queens: ${attackDefenseInfo.queenInfo.attackerscount} attackers, ${attackDefenseInfo.queenInfo.defenderscount} defenders` ); if (color === "WHITE") this.sections.push(""); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jalpp/chessagine-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server