| import { Engine } from './Engine.js'; |
| import { MoveEvaluator } from './MoveEvaluator.js'; |
| import { MoveClassifier } from '../classification/MoveClassifier.js'; |
|
|
| |
| |
| |
| export class EvaluationQueue { |
| constructor(settingsMenu) { |
| this.queue = []; |
| this.currentEvaluation = null; |
| this.isProcessing = false; |
| this.processedMoves = new Map(); |
| this.displayProgressBar = true; |
| this.settingsMenu = settingsMenu; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| addToQueue(node, fen, previousFen, callback, moveTree) { |
| const nodeId = node.id; |
| |
| |
| if (this.isNodeQueued(nodeId) || this.processedMoves.has(nodeId)) return; |
|
|
| |
| const priority = moveTree && nodeId === moveTree.currentNode.id ? 0 : 1; |
| const queueItem = { node, fen, previousFen, callback, priority, timeAdded: Date.now(), moveTree }; |
|
|
| |
| const insertIndex = this.queue.findIndex(item => item.priority > priority); |
| insertIndex === -1 ? this.queue.push(queueItem) : this.queue.splice(insertIndex, 0, queueItem); |
|
|
| |
| if (!this.isProcessing) this.processQueue(); |
| } |
|
|
| |
| |
| |
| async processQueue() { |
| if (this.queue.length === 0 || this.isProcessing) return; |
|
|
| this.updateMiniEvaluationProgress(0); |
|
|
| this.isProcessing = true; |
| const item = this.queue.shift(); |
| this.currentEvaluation = item; |
|
|
| try { |
| |
| let prevLines = this.findPreviousLines(item); |
| |
| |
| if (!prevLines) { |
| prevLines = await MoveEvaluator.tryCloudEvaluation(item.previousFen) || |
| await this.evaluateWithEngine(item.previousFen, 12, 0, 100); |
| } |
| |
| |
| let lines = await MoveEvaluator.tryCloudEvaluation(item.fen); |
| let engine = null; |
| |
| if (!lines || lines.length < 2) { |
| const engineType = this.settingsMenu?.getSettingValue('engineType') || 'stockfish-17-lite'; |
| engine = new Engine({ engineType: engineType }); |
| const depth = this.settingsMenu?.getSettingValue('variationEngineDepth') || 16; |
| lines = await this.evaluateWithEngine(item.fen, depth, 0, 100, engine); |
| } |
| |
| |
| const result = { |
| move: { |
| fen: item.fen, |
| lines: lines, |
| uciMove: item.node.move ? `${item.node.move.from}${item.node.move.to}` : "", |
| engine: engine ? engine.engine.name : 'Cloud' |
| }, |
| previous: { fen: item.previousFen, lines: prevLines } |
| }; |
| |
| this.processedMoves.set(item.node.id, result); |
| |
| |
| if (item.callback) { |
| const movesUpToCurrent = this.getMovesUpToCurrent(item.node, item.moveTree); |
| const classification = MoveClassifier.classifyMove(result.move, result.previous, movesUpToCurrent); |
|
|
| item.callback({ |
| classification, |
| uciMove: item.node.move, |
| fen: item.fen, |
| lines, |
| engine: engine ? engine.engine.name : 'Cloud' |
| }); |
| } |
|
|
| engine.abort(); |
| engine.terminate(); |
| |
| this.updateMiniEvaluationProgress(100); |
| } catch (error) { |
| console.error("Error during evaluation:", error); |
| } |
|
|
| |
| this.currentEvaluation = null; |
| this.isProcessing = false; |
| |
| if (this.queue.length > 0) this.processQueue(); |
| } |
| |
| |
| |
| |
| |
| async evaluateWithEngine(fen, depth, startProgress, endProgress, engine = null) { |
| if (!engine) { |
| |
| const engineType = this.settingsMenu?.getSettingValue('engineType') || 'stockfish-17-lite'; |
| engine = new Engine({ engineType: engineType }); |
| } |
|
|
| return await engine.evaluate(fen, depth, false, (progress) => { |
| const scaledProgress = startProgress + (progress.percent * (endProgress - startProgress) / 100); |
| this.updateMiniEvaluationProgress(Math.round(scaledProgress)); |
| }); |
| } |
| |
| |
| |
| |
| |
| findPreviousLines(item) { |
| if (!item.moveTree || !item.node) return null; |
| |
| let parentNode = null; |
| let parentNodeId = null; |
| |
| |
| if (item.node.isMainline && typeof item.node.parentIndex === 'number') { |
| parentNode = item.moveTree.mainline[item.node.parentIndex]; |
| parentNodeId = parentNode?.id; |
| } else if (item.node.parentId) { |
| parentNodeId = item.node.parentId; |
| parentNode = item.moveTree.nodeMap.get(parentNodeId); |
| } |
| |
| if (!parentNodeId) return null; |
| |
| |
| const parentResult = this.processedMoves.get(parentNodeId); |
| if (parentResult?.move?.lines) return parentResult.move.lines; |
| |
| |
| return parentNode?.evaluatedMove?.lines || null; |
| } |
| |
| |
| |
| |
| |
| getMovesUpToCurrent(node, moveTree) { |
| if (!moveTree) return []; |
| |
| const moves = []; |
| let currentNode = node; |
|
|
| while (currentNode) { |
| if (currentNode.move) moves.unshift(currentNode.san); |
| |
| if (currentNode.isMainline && typeof currentNode.parentIndex === 'number') { |
| currentNode = moveTree.mainline[currentNode.parentIndex]; |
| } else if (currentNode.parentId) { |
| currentNode = moveTree.nodeMap.get(currentNode.parentId); |
| } else { |
| currentNode = null; |
| } |
| } |
| |
| return moves; |
| } |
|
|
| |
| |
| |
| |
| |
| isNodeQueued(nodeId) { |
| return this.queue.some(item => item.node.id === nodeId) || |
| (this.currentEvaluation?.node.id === nodeId); |
| } |
|
|
| |
| |
| |
| |
| |
| getResult(nodeId) { |
| return this.processedMoves.get(nodeId) || null; |
| } |
|
|
| |
| |
| |
| |
| updateMiniEvaluationProgress(progress) { |
| if (!this.displayProgressBar) return; |
|
|
| |
| let percentage = typeof progress === 'object' && progress !== null ? |
| (progress.percent || 0) : progress; |
|
|
| |
| const progressBar = $(".evaluation-progress-bar"); |
| progressBar.addClass("visible"); |
| progressBar.css("opacity", "1"); |
|
|
| progressBar.css("width", percentage + "%"); |
|
|
| |
| if (percentage >= 100) { |
| progressBar.css("opacity", "0"); |
| progressBar.css("width", "0%"); |
| } |
| } |
| } |