1

我这辈子都做不到,除了在 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
4

1 回答 1

1

尝试使用它,它对我有用:

const stockfish = new Worker('/stockfish.js');
stockfish.postMessage('uci');
stockfish.postMessage('ucinewgame');
stockfish.postMessage('setoption name Skill Level value 3');
stockfish.postMessage('setoption name Skill Level Maximum Error value 600');
stockfish.postMessage('setoption name Skill Level Probability value 128');
stockfish.postMessage('position fen ' + chess.fen());
stockfish.postMessage('go depth 10');
于 2021-12-17T16:42:01.437 回答