Skip to content

Commit

Permalink
Merge pull request #124 from dcSpark/feature/add-chess-evaluate-tool
Browse files Browse the repository at this point in the history
feat: add chess-evaluate tool
  • Loading branch information
guillevalin authored Feb 4, 2025
2 parents 941d0c3 + 6405435 commit c5a0fa8
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 0 deletions.
Binary file added tools/chess-evaluate/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tools/chess-evaluate/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions tools/chess-evaluate/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect } from '@jest/globals';
import { getToolTestClient } from '../../src/test/utils';
import * as path from 'path';

describe('Chess Evaluate Tool', () => {
const toolPath = path.join(__dirname, 'tool.py');
const client = getToolTestClient();

it('evaluates the starting position', async () => {
const response = await client.executeToolFromFile(toolPath, {
fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
depth: 10
});
console.log(response);

expect(response).toHaveProperty('message');
// Could be "Evaluation: X centipawns" or "Mate in ...", etc.
// Just check it doesn't throw
expect(typeof response.message).toBe('string');
expect(response.message.length).toBeGreaterThan(0);
}, 20000);

it('handles invalid FEN', async () => {
await expect(client.executeToolFromFile(toolPath, {
fen: "not-a-valid-FEN"
})).rejects.toThrow(/Invalid FEN/);
}, 10000);
});
49 changes: 49 additions & 0 deletions tools/chess-evaluate/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"id": "chess-evaluate",
"name": "Chess Evaluate",
"version": "1.0.0",
"description": "Evaluate a chess position using Stockfish at a given depth",
"author": "Example",
"keywords": [
"chess",
"stockfish",
"evaluation",
"analysis",
"engine",
"position"
],
"configurations": {
"type": "object",
"properties": {},
"required": []
},
"parameters": {
"type": "object",
"properties": {
"fen": {
"type": "string",
"description": "FEN describing the position to evaluate"
},
"depth": {
"type": "number",
"description": "Depth for the engine search",
"default": 15
},
"time_limit_ms": {
"type": "number",
"description": "Time limit in milliseconds if depth is small",
"default": 1000
}
},
"required": ["fen"]
},
"result": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
},
"required": ["message"]
}
}
3 changes: 3 additions & 0 deletions tools/chess-evaluate/store.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"categoryId": "f4906ba5-16bb-445d-9241-85422cf5a055"
}
79 changes: 79 additions & 0 deletions tools/chess-evaluate/tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# /// script
# dependencies = [
# "python-chess>=1.999",
# "requests"
# ]
# ///

import chess
from typing import Dict, Any, Optional, List

class CONFIG:
pass

class INPUTS:
fen: str
depth: int = 15
time_limit_ms: int = 1000 # fallback if depth is small

class OUTPUT:
message: str

async def run(c: CONFIG, p: INPUTS) -> OUTPUT:
if not p.fen:
raise ValueError("No FEN provided")
# Validate FEN
board = chess.Board()
try:
board.set_fen(p.fen)
except ValueError:
raise ValueError("Invalid FEN")

# Basic evaluation based on material count and position
def evaluate_position(board: chess.Board) -> float:
# Material values
piece_values = {
chess.PAWN: 1,
chess.KNIGHT: 3,
chess.BISHOP: 3,
chess.ROOK: 5,
chess.QUEEN: 9,
chess.KING: 0 # Not counted in material
}

score = 0

# Count material
for piece_type in piece_values:
score += len(board.pieces(piece_type, chess.WHITE)) * piece_values[piece_type]
score -= len(board.pieces(piece_type, chess.BLACK)) * piece_values[piece_type]

# Position evaluation bonuses
if board.is_checkmate():
if board.turn == chess.WHITE:
score = -1000 # Black wins
else:
score = 1000 # White wins
elif board.is_stalemate() or board.is_insufficient_material():
score = 0

# Convert to centipawns
score = score * 100

return score

# Get evaluation
score = evaluate_position(board)

# Format message
if abs(score) >= 1000:
if score > 0:
message = "Mate for White"
else:
message = "Mate for Black"
else:
message = f"Evaluation: {int(score)} centipawns (White-positive)"

out = OUTPUT()
out.message = message
return out

0 comments on commit c5a0fa8

Please sign in to comment.