import { TacticalBoard } from "../themes/tacticalBoard.js";
import { Chess } from "chess.js";
import { TempoCalculator } from "../themes/temoCalculator.js";
export class PositionPrompter {
state;
sections;
tactical;
tempoCalculator;
constructor(state) {
this.state = state;
this.tactical = new TacticalBoard(state.fen);
this.tempoCalculator = new TempoCalculator(this.state, this.tactical, new Chess(state.fen).turn());
this.sections = [];
}
generatePrompt() {
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");
}
addGameStatus() {
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>");
}
addTempoAnalysis() {
const tempoDetails = this.tempoCalculator.getTempoExplanation();
for (const detail of tempoDetails) {
this.sections.push(detail);
}
}
addMaterialAnalysis() {
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>");
}
addPlayerMaterial(color, materialScore) {
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
}
addMaterialAdvantageDescription(advantage) {
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`);
}
}
addPiecePositions() {
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>");
}
addPlayerPiecePositions(color, placement) {
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
}
addKingSafetyAnalysis() {
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>");
}
addPlayerKingSafety(color, kingSafety) {
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
}
addCastlingRights() {
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>");
}
addPieceMobility() {
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>");
}
addPlayerMobility(color, mobility) {
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("");
}
addSpaceControl() {
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>");
}
addPlayerSpaceControl(color, spaceScore) {
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
}
addSquareColorControl() {
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>");
}
addPlayerSquareControl(color, squareControl) {
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
}
addPawnStructureAnalysis() {
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>");
}
addPlayerPawnStructure(color, positional) {
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
}
addTacticalAnalysis() {
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>");
}
addAttackDefenseDetails() {
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>");
}
addPlayerAttackDefense(color, attackDefenseInfo) {
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("");
}
}