我这辈子都做不到,除了在 20 级提供粉碎性打击之外,我似乎无法让鱼做任何事情。下面提供了用于配置鱼的技能水平和深度以及 UCI 顺序的代码代码执行的命令。
这段代码是用 javascript 编写的,但就像最初的开源实现一样使用 UCI。我按照以下示例进行操作:https ://github.com/nmrugg/stockfish.js/blob/2b87d5d16a613f3ce05b1dd0bbb58465501ed30a/example/enginegame.js#L38 。
Stockfish.js
import _ from 'lodash';
import ChessBoardState from './ChessBoardState';
import Move from './Move';
class Stockfish {
constructor() {
this.bestMove = null;
this.skill = null;
this.depth = null;
this.isThinking = false;
this.engineStatus = {};
this.stockfish = new Worker('stockfish.js');
this.stockfish.onmessage = (event) => {
const line = event && typeof event === 'object' ? event.data : event;
console.log('Stockfish: ', line);
if (line === 'uciok') {
this.engineStatus.engineLoaded = true;
} else if (line === 'readyok') {
this.engineStatus.engineReady = true;
} else {
const match = line.match('^bestmove ([a-h][1-8])([a-h][1-8])([qrbn])?');
if (match) {
this.bestMove = new Move(match[1], match[2], match[3] ? match[3] : null);
this.isThinking = false;
}
}
}
this.stockfish.postMessage('uci');
this.stockfish.postMessage('isready');
this.stockfish.postMessage('ucinewgame');
}
isEngineLoaded() {
return this.engineStatus.engineLoaded;
}
/**
* Update engine state to fenCode.
* @param {string} fenCode
*/
setFEN(fenCode) {
if (!this.engineStatus.engineLoaded) {
throw new Error('Engine not loaded');
}
this.stockfish.postMessage(`position fen ${fenCode}`);
}
/**
* Update engine depth
* @param {number} depth
*/
setDepth(depth) {
console.log(depth);
this.depth = _.clamp(depth, 1, 20);
}
/**
* Update engine skill level
* @param {number} skill
*/
setSkillLevel(skillLevel) {
if (!this.engineStatus.engineLoaded) {
throw new Error('Engine not loaded');
}
skillLevel = _.clamp(skillLevel, 0, 20);
console.log(skillLevel);
this.stockfish.postMessage(`setoption name Skill Level value ${skillLevel}`);
// Stockfish level 20 does not make errors (intentially), so these numbers have no effect on level 20.
// Level 0 starts at 1
const err_prob = Math.round((skillLevel * 6.35) + 1);
// Level 0 starts at 10
const max_err = Math.round((skillLevel * -0.5) + 10);
this.stockfish.postMessage(`setoption name Skill Level Maximum Error value ${max_err}`);
this.stockfish.postMessage(`setoption name Skill Level Probability value ${err_prob}`);
this.skillLevel = skillLevel;
}
/**
* Start churning for best move.
* @param {ChessBoardState} chessBoardState
* @param {number} depth
*/
searchBestMove(chessBoardState) {
this.bestMove = null;
this.isThinking = true;
this.setFEN(chessBoardState.toFEN());
this.stockfish.postMessage(`go depth ${this.depth}`);
}
/**
* Returns best move if one has been found.
*/
getBestMove() {
return this.isThinking ? null : this.bestMove;
}
}
export default Stockfish
ChessGameUI.js(...
用于表示不相关的代码)
...
const stockfish = new Stockfish()
this.state = {
...
stockfish: stockfish,
stockfishSkillLevel: 0,
stockfishDepth: 1,
}
...
this.intID = setInterval(() => {
this.state.stockfish.setSkillLevel(this.state.stockfishSkillLevel);
this.state.stockfish.setDepth(this.state.stockfishDepth);
...
const bestMove = this.state.stockfish.getBestMove();
if (!bestMove && !this.state.stockfish.isThinking) {
this.state.stockfish.searchBestMove(this.state.chessBoardState, this.state.stockfishDepth);
} else if (bestMove) {
this.state.chessBoardState.move(bestMove)
this.state.stockfish.bestMove = null;
this.setState(prev => ({
...prev,
chessBoardState: ChessBoardState.fromFEN(this.state.chessBoardState.toFEN()),
}));
}
}, 500);
理论上,这段代码应该尝试每半秒积极地设置技能级别和深度,同时bestMove
从 Stockfish 轮询新的。但设置似乎对引擎的难度没有任何影响。
调用的 UCI 命令(按顺序):
uci
isready
ucinewgame
setoption name Skill Level value 0
setoption name Skill Level Maximum Error value 1
setoption name Skill Level Probability Value 10
position fen rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1
go depth 1