dawikk-stock
A React Native library that integrates the powerful Stockfish chess engine for both iOS and Android platforms.
Features
- Full UCI (Universal Chess Interface) support
- Native integration with Stockfish engine
- Cross-platform support (iOS and Android)
- Simple event-based API for communication with the engine
- Bundled with the latest Stockfish engine (version 17)
- Performance optimized for mobile devices
- Configurable event throttling to prevent UI thread blocking
- Selectable event emission to improve performance
- MultiPV support for analyzing multiple lines simultaneously
- Enhanced performance with latest analysis always available
Installation
# Using npm
npm install dawikk-stock --save
# Or using Yarn
yarn add dawikk-stock
iOS Setup
cd ios && pod install
Basic Usage
import Stockfish from 'dawikk-stock';
// Configure the engine (optional)
Stockfish.setConfig({
throttling: {
analysisInterval: 200, // Emit analysis events every 200ms
messageInterval: 300 // Emit raw messages every 300ms
},
events: {
emitMessage: true, // Enable/disable raw message events
emitAnalysis: true, // Enable/disable analysis events
emitBestMove: true // Enable/disable bestmove events
}
});
// Initialize the engine
await Stockfish.init();
// Set up a listener for engine output
const unsubscribeMessage = Stockfish.addMessageListener((message) => {
console.log('Engine message:', message);
});
// Send UCI commands
await Stockfish.sendCommand('position startpos');
await Stockfish.sendCommand('go depth 15');
// Clean up when done
unsubscribeMessage();
await Stockfish.shutdown();
API Reference
Methods
init()
Initializes the Stockfish engine.
const success = await Stockfish.init();
setConfig(config)
Configures the library's behavior regarding event throttling and emission. This method can be called at any time, even after initialization.
Stockfish.setConfig({
throttling: {
analysisInterval: 200, // Time in ms between analysis events
messageInterval: 300 // Time in ms between message events
},
events: {
emitMessage: true, // Whether to emit raw message events
emitAnalysis: true, // Whether to emit analysis events
emitBestMove: true // Whether to emit bestMove events
}
});
sendCommand(command)
Sends a UCI command to the engine.
await Stockfish.sendCommand('position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
await Stockfish.sendCommand('go depth 20');
shutdown()
Shuts down the engine and frees resources.
await Stockfish.shutdown();
addMessageListener(callback)
Adds a listener for raw output messages from the engine. Returns a function to remove the listener.
const unsubscribe = Stockfish.addMessageListener((message) => {
console.log('Engine says:', message);
});
// Later, to remove the listener
unsubscribe();
addAnalysisListener(callback)
Adds a listener for parsed analysis data (structured data). Returns a function to remove the listener.
const unsubscribe = Stockfish.addAnalysisListener((data) => {
console.log('Analysis data:', data);
// For MultiPV analysis, you can access all lines through these arrays
if (data.bestMoves && data.bestMoves.length > 1) {
console.log('All best moves:', data.bestMoves);
console.log('All evaluations:', data.evaluations);
console.log('All lines:', data.lines);
}
});
// Later, to remove the listener
unsubscribe();
addBestMoveListener(callback)
Adds a dedicated listener for "bestmove" events (computer's chosen moves). Perfect for implementing a game against the computer. Returns a function to remove the listener.
const unsubscribe = Stockfish.addBestMoveListener((data) => {
console.log('Computer chose move:', data.move);
// Make the move on your chess board
});
// Later, to remove the listener
unsubscribe();
analyzePosition(fen, options)
Helper method to set a position and start analysis.
await Stockfish.analyzePosition('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', {
depth: 20,
multiPv: 3,
movetime: 5000
});
stopAnalysis()
Stops the current analysis.
await Stockfish.stopAnalysis();
getComputerMove(fen, movetime, depth)
Helper method to get a computer move in a game. Will trigger a bestmove
event that can be captured with addBestMoveListener
.
// Computer has 1 second to choose a move
await Stockfish.getComputerMove('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 1000, 15);
Data Structures
StockfishConfig
interface StockfishConfig {
throttling: {
analysisInterval: number; // Time in ms between analysis event emissions
messageInterval: number; // Time in ms between message event emissions
};
events: {
emitMessage: boolean; // Whether to emit raw message events
emitAnalysis: boolean; // Whether to emit analysis events
emitBestMove: boolean; // Whether to emit bestMove events
};
}
AnalysisData
interface AnalysisData {
type: 'info' | 'bestmove';
depth?: number;
score?: number;
mate?: number;
bestMove?: string;
line?: string;
move?: string;
// For MultiPV analysis
bestMoves?: string[]; // Array of best moves for each line
evaluations?: string[]; // Array of evaluations for each line
lines?: string[]; // Array of move sequences for each line
depths?: number[]; // Array of depths for each line
}
BestMoveData
interface BestMoveData {
type: 'bestmove';
move: string;
ponder?: string;
}
AnalysisOptions
interface AnalysisOptions {
depth?: number;
multiPv?: number;
movetime?: number;
nodes?: number;
}
Events
The library emits three types of events:
Raw Messages - Plain text output from the Stockfish engine
- These include UCI protocol responses like
uciok
,readyok
, andbestmove e2e4
- Access these via
addMessageListener
- Can be disabled with
setConfig({events: {emitMessage: false}})
- These include UCI protocol responses like
Analysis Data - Structured data parsed from engine output
- This data may include evaluation scores, depth, best moves, etc.
- Support for MultiPV (multiple lines) with arrays for bestMoves, evaluations, and lines
- Access this via
addAnalysisListener
- Can be disabled with
setConfig({events: {emitAnalysis: false}})
Computer Moves - Dedicated events for computer moves
- Contains only the final move choice from the engine
- Access this via
addBestMoveListener
- Can be disabled with
setConfig({events: {emitBestMove: false}})
Throttling Configuration
To prevent UI thread blocking with rapid engine updates:
// Configure throttling
Stockfish.setConfig({
throttling: {
analysisInterval: 200, // Emit analysis updates every 200ms max
messageInterval: 300 // Emit raw message updates every 300ms max
}
});
The throttling system ensures:
- Events are buffered and emitted at regular intervals
- Only the most recent data is emitted, preventing outdated analysis
- For MultiPV analysis, all lines are collected and emitted together
- UI remains responsive even during intensive analysis
Usage Examples
Position Analysis with MultiPV
import Stockfish from 'dawikk-stock';
// Initialize the engine
await Stockfish.init();
// Configure for performance
Stockfish.setConfig({
throttling: {
analysisInterval: 200,
messageInterval: 300
},
events: {
emitMessage: false, // Disable raw messages (not needed for analysis)
emitAnalysis: true,
emitBestMove: false // Disable bestmove events (not needed for analysis)
}
});
// Set up analysis listener
const unsubscribe = Stockfish.addAnalysisListener((data) => {
// For MultiPV analysis, we'll receive arrays of data
if (data.bestMoves && data.bestMoves.length > 0) {
console.log('Analysis depth:', Math.max(...data.depths));
// Display each line
data.bestMoves.forEach((move, index) => {
console.log(`Line ${index + 1}:`);
console.log(` Best move: ${move}`);
console.log(` Evaluation: ${data.evaluations[index]}`);
console.log(` Full line: ${data.lines[index]}`);
});
}
});
// Analyze a position with 3 lines
await Stockfish.analyzePosition('r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3', {
depth: 18,
multiPv: 3
});
// Stop after 5 seconds
setTimeout(async () => {
await Stockfish.stopAnalysis();
unsubscribe();
await Stockfish.shutdown();
}, 5000);
Playing Against the Computer (Optimized)
import Stockfish from 'dawikk-stock';
import { Chess } from 'chess.js'; // Assuming you use chess.js
class ChessGame {
constructor() {
this.game = new Chess();
this.initialize();
}
async initialize() {
// Configure the engine for optimal performance in a game
Stockfish.setConfig({
throttling: {
analysisInterval: 200,
messageInterval: 300
},
events: {
emitMessage: false, // No need for raw messages
emitAnalysis: false, // No need for analysis data
emitBestMove: true // Only need the final move
}
});
// Initialize engine
await Stockfish.init();
// Listen for computer moves
this.unsubscribe = Stockfish.addBestMoveListener((data) => {
// Execute computer's move
this.game.move({
from: data.move.substring(0, 2),
to: data.move.substring(2, 4),
promotion: data.move.length > 4 ? data.move[4] : undefined
});
console.log('New position:', this.game.fen());
console.log('History:', this.game.history({ verbose: true }));
});
}
// Method to make player's move
makeMove(from, to, promotion) {
// Check if move is legal
const move = this.game.move({ from, to, promotion });
if (move) {
// Ask computer to respond
Stockfish.getComputerMove(this.game.fen(), 1000, 15);
return true;
}
return false;
}
// Clean up resources
cleanup() {
this.unsubscribe();
Stockfish.shutdown();
}
}
// Usage
const game = new ChessGame();
game.makeMove('e2', 'e4'); // Player makes first move
// ... after receiving bestmove event, computer responds automatically
Advanced: Creating Multiple Instances
For advanced use cases (like running multiple instances), you can create custom instances:
import { Stockfish } from 'dawikk-stock'; // Import the class instead of the default instance
// Create separate instances with different configurations
const analysisEngine = new Stockfish({
throttling: { analysisInterval: 100, messageInterval: 200 },
events: { emitMessage: false, emitAnalysis: true, emitBestMove: false }
});
const gameEngine = new Stockfish({
throttling: { analysisInterval: 300, messageInterval: 400 },
events: { emitMessage: false, emitAnalysis: false, emitBestMove: true }
});
// Initialize both engines
await analysisEngine.init();
await gameEngine.init();
// Use them independently
analysisEngine.addAnalysisListener(data => console.log('Analysis:', data));
gameEngine.addBestMoveListener(data => console.log('Game move:', data.move));
// Clean up when done
analysisEngine.destroy();
gameEngine.destroy();
Important Notes
Engine Initialization Sequence
For proper operation, follow this initialization sequence:
- Initialize the engine with
await Stockfish.init()
- Set up your listeners
- Send the UCI command:
await Stockfish.sendCommand('uci')
- Wait for the
uciok
response in your message listener - Send the
isready
command:await Stockfish.sendCommand('isready')
- Wait for the
readyok
response in your message listener - Now the engine is ready to accept further commands
Handling Engine Moves
The Stockfish engine communicates moves in the UCI format (e.g., e2e4
). To handle these:
Stockfish.addBestMoveListener((data) => {
const moveStr = data.move;
// Parse UCI format to your chess representation
const from = moveStr.substring(0, 2);
const to = moveStr.substring(2, 4);
const promotion = moveStr.length > 4 ? moveStr[4] : undefined;
// Now you can use these coordinates with a chess library like chess.js
chess.move({from, to, promotion});
});
Performance Optimization Tips
Disable Unnecessary Events: Use
setConfig()
to disable events you don't need// For computer game, disable analysis events Stockfish.setConfig({ events: { emitMessage: false, emitAnalysis: false, emitBestMove: true } }); // For position analysis, disable bestmove events Stockfish.setConfig({ events: { emitMessage: false, emitAnalysis: true, emitBestMove: false } });
Adjust Throttling: Increase intervals for smoother UI, decrease for more responsive analysis
// More responsive analysis (updates more frequently) Stockfish.setConfig({ throttling: { analysisInterval: 100, messageInterval: 150 } }); // Smoother UI (less frequent updates) Stockfish.setConfig({ throttling: { analysisInterval: 300, messageInterval: 400 } });
Minimize Listeners: Remove listeners when not needed
const unsubscribe = Stockfish.addAnalysisListener(data => {/*...*/}); // When done with analysis unsubscribe();
Common UCI Commands
Here are some common UCI commands you can send to the engine:
// Set up the starting position
await Stockfish.sendCommand('position startpos');
// Set up a position from FEN
await Stockfish.sendCommand('position fen r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3');
// Set up a position and apply moves
await Stockfish.sendCommand('position startpos moves e2e4 e7e5 g1f3');
// Start analysis with depth limit
await Stockfish.sendCommand('go depth 20');
// Start analysis with time limit (milliseconds)
await Stockfish.sendCommand('go movetime 3000');
// Start analysis with multiple lines (MultiPV)
await Stockfish.sendCommand('setoption name MultiPV value 3');
await Stockfish.sendCommand('go depth 20');
// Limit engine strength (0-20)
await Stockfish.sendCommand('setoption name Skill Level value 10');
// Stop the current calculation
await Stockfish.sendCommand('stop');
License
This project is licensed under the GPL-3.0 License, as it includes Stockfish code which is GPL-3.0 licensed.
For more information about Stockfish, visit stockfishchess.org