Is this StockfishWorker implementation for evaluating chess moves functional and efficient? [closed]

I’m working on a chess analysis tool that uses a StockfishWorker class to evaluate moves and provide engine suggestions. The code is designed to interface with a web worker running Stockfish, handle communication, and process multi-PV lines.

Here’s the complete code for the class:

import type { EngineLine, Evaluation, Lan } from '../types/Game';
import type GameReviewManager from './_GameReviewManager';
import { getCloudEvaluation } from '../api/lichessApiAccess';

class StockfishWorker {
  private workerUrl: URL;
  private stockfishWorker: Worker;
  private verbose: boolean;
  private multipv: number;
  private targetDepth: number;
  readonly game_review_manager: GameReviewManager;

  private waitFor(response: string, errormsg = 'error') {
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (this.verbose) console.debug(e);
        if (e instanceof MessageEvent && e.data.includes(response)) {
          this.stockfishWorker.removeEventListener('message', listener);
          resolve(true);
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      // Add a timeout for error handling (optional)
      setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(`delay time exceeded: ${errormsg}`);
      }, 5000);
    });
  }

  _init(restart = false) {
    return new Promise(async (resolve) => {
      this.stockfishWorker.onmessageerror = (e) => console.debug(e);
      if (!restart) {
        this.stockfishWorker.postMessage('uci');
        await this.waitFor('uciok', 'uci setup error');
      } else console.debug('Restarting engine...');
      this.stockfishWorker.postMessage(`ucinewgame`);
      this.stockfishWorker.postMessage('isready');
      this.stockfishWorker.postMessage(
        `setoption name MultiPV value ${this.multipv}`,
      );
      this.waitFor('readyok', 'this.stockfishWorker not ready after timeout')
        .then(() => {
          resolve(true);
        })
        .catch((err) => {
          throw new Error(err);
        });
    });
  }

  evaluateMove(fen: string = 'startpos'): Promise<EngineLine[]> {
    console.log('evaluate move');
    !fen || fen == 'startpos'
      ? this.stockfishWorker.postMessage(`position startpos`)
      : this.stockfishWorker.postMessage(`position fen ${fen}`);
    this.stockfishWorker.postMessage(`go depth ${this.targetDepth}`);

    let messages = [];
    let lines: EngineLine[] = [];
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (e instanceof MessageEvent) {
          messages.unshift(e.data);
          if (e.data.includes('depth 0')) {
            if (this.verbose) console.warn(`${e}`);
          }
          if (e.data.startsWith('bestmove') || e.data.includes('depth 0')) {
            this.stockfishWorker.removeEventListener('message', listener);
            let searchMessages = messages.filter((msg) =>
              msg.startsWith('info depth'),
            );
            for (let searchMessage of searchMessages) {
              // Extract depth, MultiPV line ID and evaluation from search message
              let idString = searchMessage.match(/(?:multipv )(d+)/)?.[1];
              let depthString = searchMessage.match(/(?:depth )(d+)/)?.[1];

              let bestMove: Lan =
                searchMessage.match(/(?: pv )(.+?)(?= |$)/)?.[1];

              let evaluation: Evaluation = {
                type: searchMessage.includes(' cp ') ? 'cp' : 'mate',
                value: parseInt(
                  searchMessage.match(/(?:(?:cp )|(?:mate ))([d-]+)/)?.[1] ||
                    '0',
                ),
              };
              // Invert evaluation if black to play since scores are from black perspective
              // and we want them always from the perspective of white
              if (fen && fen.includes(' b ')) {
                evaluation.value *= -1;
              }
              // If any piece of data from message is missing, discard message
              if (!idString || !depthString || !bestMove) {
                lines.push(
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                );
                resolve(lines);
                return;
              }

              let id = parseInt(idString);
              let depth = parseInt(depthString);

              // Discard if target depth not reached or lineID already present
              if (
                depth != this.targetDepth ||
                lines.some((line) => line.id == id)
              )
                continue;

              lines.push({
                id,
                depth,
                evaluation,
                bestMove,
              });
            }
            clearTimeout(timeoutId); // Clear the timeout if message is received
            console.debug(lines);
            console.debug('cleared time out id');
            resolve(lines);
          }
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      const timeoutId = setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(new Error('takes alot of time'));
      }, 20000); // Adjust timeout as needed
    });
  }

  private restartWorker(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    console.log('restarting worker....');
    this.stockfishWorker.terminate();
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this._init().then(() => {
      this.evaluateStuckMove(ui_update_callback, move).then((res) => {
        if (res.success) {
          console.warn('continue with work');
          this.evaluatePosition(ui_update_callback);
        } else {
          console.error('restarting worker again');
          this.restartWorker(ui_update_callback, move);
        }
      });
    });
  }

  private async evaluateStuckMove(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    try {
      console.debug('evaluating halt move', move);
      const engineLines = await this.evaluateMove(move.current_fen);
      ui_update_callback();
      this.game_review_manager.add_enginelines(engineLines, move.move_num);
      return { success: true };
    } catch (error) {
      return { success: false };
    }
  }

  async evaluatePosition(ui_update_callback: () => void) {
    console.log('evaluating position');
    while (!this.game_review_manager.done_evaluating()) {
      const currentMove = this.game_review_manager.get_next_move();
      const { current_fen, move_num } = currentMove;
      console.debug({ currentMove });
      try {
        const engineLines = await this.evaluateMove(current_fen);
        const cloud_eval = await getCloudEvaluation(current_fen);
        console.warn({ cloud_eval });
        console.debug({ engineLines });
        ui_update_callback();
        this.game_review_manager.add_enginelines(engineLines, move_num);
      } catch (error) {
        console.error('error');
        console.error('restarting');
        this.restartWorker(ui_update_callback, currentMove);
      }
    }
    console.log('terminate_worker');
    this.stockfishWorker.terminate();
    return { success: true };
  }

  /**
   * @param startingpos : fen format
   * @param multipv : number of lines to return
   * @param verbose : testing
   */
  constructor(
    game_review_manager: GameReviewManager,
    multipv = 2,
    targetDepth = 15,
    verbose = false,
  ) {
    var wasmSupported =
      typeof WebAssembly === 'object' &&
      WebAssembly.validate(
        Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
      );

    this.workerUrl = wasmSupported
      ? new URL('./stockfish/stockfish.js', import.meta.url)
      : new URL('./stockfish/stockfish.wasm.js', import.meta.url);
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this.multipv = multipv;
    //this.evaluateMove = this.evaluateMove.bind(this);
    this.targetDepth = targetDepth;
    this.verbose = verbose;
    //this.waitFor = this.waitFor.bind(this);
    this.game_review_manager = game_review_manager;
  }
}
export default StockfishWorker;

Features:
Initialization: The _init method sets up the Stockfish engine and waits for it to be ready.
Move Evaluation: evaluateMove sends FEN positions to the engine and collects depth-based evaluations.
Timeout Handling: There are multiple timeout mechanisms to ensure the process doesn’t hang.
Worker Restarts: restartWorker attempts to recover the worker if it becomes unresponsive.
Cloud Integration: It also integrates with a cloud evaluation API for additional analysis.
problems:
the engine keeps halting and restarting, and the engine sometimes returns an empty result as the eval even if I know it exists one
when I increase the multiple the failing rises a lot to the point of it not working

My Concerns:
Timeouts and Asynchronous Handling: Are the timeout and retry mechanisms implemented correctly to ensure robust communication with the worker?
Message Filtering: Is the filtering and processing of engine output (info depth, bestmove, etc.) reliable for real-time chess analysis?
Redundancy: Certain parts, like pushing fallback lines with placeholder values (a1a1 moves), seem redundant. Should this be optimized?
Worker Performance: Does terminating and restarting the worker frequently affect performance, and is there a better way to handle “stuck” evaluations?

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Is this StockfishWorker implementation for evaluating chess moves functional and efficient? [closed]

I’m working on a chess analysis tool that uses a StockfishWorker class to evaluate moves and provide engine suggestions. The code is designed to interface with a web worker running Stockfish, handle communication, and process multi-PV lines.

Here’s the complete code for the class:

import type { EngineLine, Evaluation, Lan } from '../types/Game';
import type GameReviewManager from './_GameReviewManager';
import { getCloudEvaluation } from '../api/lichessApiAccess';

class StockfishWorker {
  private workerUrl: URL;
  private stockfishWorker: Worker;
  private verbose: boolean;
  private multipv: number;
  private targetDepth: number;
  readonly game_review_manager: GameReviewManager;

  private waitFor(response: string, errormsg = 'error') {
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (this.verbose) console.debug(e);
        if (e instanceof MessageEvent && e.data.includes(response)) {
          this.stockfishWorker.removeEventListener('message', listener);
          resolve(true);
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      // Add a timeout for error handling (optional)
      setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(`delay time exceeded: ${errormsg}`);
      }, 5000);
    });
  }

  _init(restart = false) {
    return new Promise(async (resolve) => {
      this.stockfishWorker.onmessageerror = (e) => console.debug(e);
      if (!restart) {
        this.stockfishWorker.postMessage('uci');
        await this.waitFor('uciok', 'uci setup error');
      } else console.debug('Restarting engine...');
      this.stockfishWorker.postMessage(`ucinewgame`);
      this.stockfishWorker.postMessage('isready');
      this.stockfishWorker.postMessage(
        `setoption name MultiPV value ${this.multipv}`,
      );
      this.waitFor('readyok', 'this.stockfishWorker not ready after timeout')
        .then(() => {
          resolve(true);
        })
        .catch((err) => {
          throw new Error(err);
        });
    });
  }

  evaluateMove(fen: string = 'startpos'): Promise<EngineLine[]> {
    console.log('evaluate move');
    !fen || fen == 'startpos'
      ? this.stockfishWorker.postMessage(`position startpos`)
      : this.stockfishWorker.postMessage(`position fen ${fen}`);
    this.stockfishWorker.postMessage(`go depth ${this.targetDepth}`);

    let messages = [];
    let lines: EngineLine[] = [];
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (e instanceof MessageEvent) {
          messages.unshift(e.data);
          if (e.data.includes('depth 0')) {
            if (this.verbose) console.warn(`${e}`);
          }
          if (e.data.startsWith('bestmove') || e.data.includes('depth 0')) {
            this.stockfishWorker.removeEventListener('message', listener);
            let searchMessages = messages.filter((msg) =>
              msg.startsWith('info depth'),
            );
            for (let searchMessage of searchMessages) {
              // Extract depth, MultiPV line ID and evaluation from search message
              let idString = searchMessage.match(/(?:multipv )(d+)/)?.[1];
              let depthString = searchMessage.match(/(?:depth )(d+)/)?.[1];

              let bestMove: Lan =
                searchMessage.match(/(?: pv )(.+?)(?= |$)/)?.[1];

              let evaluation: Evaluation = {
                type: searchMessage.includes(' cp ') ? 'cp' : 'mate',
                value: parseInt(
                  searchMessage.match(/(?:(?:cp )|(?:mate ))([d-]+)/)?.[1] ||
                    '0',
                ),
              };
              // Invert evaluation if black to play since scores are from black perspective
              // and we want them always from the perspective of white
              if (fen && fen.includes(' b ')) {
                evaluation.value *= -1;
              }
              // If any piece of data from message is missing, discard message
              if (!idString || !depthString || !bestMove) {
                lines.push(
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                );
                resolve(lines);
                return;
              }

              let id = parseInt(idString);
              let depth = parseInt(depthString);

              // Discard if target depth not reached or lineID already present
              if (
                depth != this.targetDepth ||
                lines.some((line) => line.id == id)
              )
                continue;

              lines.push({
                id,
                depth,
                evaluation,
                bestMove,
              });
            }
            clearTimeout(timeoutId); // Clear the timeout if message is received
            console.debug(lines);
            console.debug('cleared time out id');
            resolve(lines);
          }
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      const timeoutId = setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(new Error('takes alot of time'));
      }, 20000); // Adjust timeout as needed
    });
  }

  private restartWorker(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    console.log('restarting worker....');
    this.stockfishWorker.terminate();
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this._init().then(() => {
      this.evaluateStuckMove(ui_update_callback, move).then((res) => {
        if (res.success) {
          console.warn('continue with work');
          this.evaluatePosition(ui_update_callback);
        } else {
          console.error('restarting worker again');
          this.restartWorker(ui_update_callback, move);
        }
      });
    });
  }

  private async evaluateStuckMove(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    try {
      console.debug('evaluating halt move', move);
      const engineLines = await this.evaluateMove(move.current_fen);
      ui_update_callback();
      this.game_review_manager.add_enginelines(engineLines, move.move_num);
      return { success: true };
    } catch (error) {
      return { success: false };
    }
  }

  async evaluatePosition(ui_update_callback: () => void) {
    console.log('evaluating position');
    while (!this.game_review_manager.done_evaluating()) {
      const currentMove = this.game_review_manager.get_next_move();
      const { current_fen, move_num } = currentMove;
      console.debug({ currentMove });
      try {
        const engineLines = await this.evaluateMove(current_fen);
        const cloud_eval = await getCloudEvaluation(current_fen);
        console.warn({ cloud_eval });
        console.debug({ engineLines });
        ui_update_callback();
        this.game_review_manager.add_enginelines(engineLines, move_num);
      } catch (error) {
        console.error('error');
        console.error('restarting');
        this.restartWorker(ui_update_callback, currentMove);
      }
    }
    console.log('terminate_worker');
    this.stockfishWorker.terminate();
    return { success: true };
  }

  /**
   * @param startingpos : fen format
   * @param multipv : number of lines to return
   * @param verbose : testing
   */
  constructor(
    game_review_manager: GameReviewManager,
    multipv = 2,
    targetDepth = 15,
    verbose = false,
  ) {
    var wasmSupported =
      typeof WebAssembly === 'object' &&
      WebAssembly.validate(
        Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
      );

    this.workerUrl = wasmSupported
      ? new URL('./stockfish/stockfish.js', import.meta.url)
      : new URL('./stockfish/stockfish.wasm.js', import.meta.url);
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this.multipv = multipv;
    //this.evaluateMove = this.evaluateMove.bind(this);
    this.targetDepth = targetDepth;
    this.verbose = verbose;
    //this.waitFor = this.waitFor.bind(this);
    this.game_review_manager = game_review_manager;
  }
}
export default StockfishWorker;

Features:
Initialization: The _init method sets up the Stockfish engine and waits for it to be ready.
Move Evaluation: evaluateMove sends FEN positions to the engine and collects depth-based evaluations.
Timeout Handling: There are multiple timeout mechanisms to ensure the process doesn’t hang.
Worker Restarts: restartWorker attempts to recover the worker if it becomes unresponsive.
Cloud Integration: It also integrates with a cloud evaluation API for additional analysis.
problems:
the engine keeps halting and restarting, and the engine sometimes returns an empty result as the eval even if I know it exists one
when I increase the multiple the failing rises a lot to the point of it not working

My Concerns:
Timeouts and Asynchronous Handling: Are the timeout and retry mechanisms implemented correctly to ensure robust communication with the worker?
Message Filtering: Is the filtering and processing of engine output (info depth, bestmove, etc.) reliable for real-time chess analysis?
Redundancy: Certain parts, like pushing fallback lines with placeholder values (a1a1 moves), seem redundant. Should this be optimized?
Worker Performance: Does terminating and restarting the worker frequently affect performance, and is there a better way to handle “stuck” evaluations?

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Is this StockfishWorker implementation for evaluating chess moves functional and efficient? [closed]

I’m working on a chess analysis tool that uses a StockfishWorker class to evaluate moves and provide engine suggestions. The code is designed to interface with a web worker running Stockfish, handle communication, and process multi-PV lines.

Here’s the complete code for the class:

import type { EngineLine, Evaluation, Lan } from '../types/Game';
import type GameReviewManager from './_GameReviewManager';
import { getCloudEvaluation } from '../api/lichessApiAccess';

class StockfishWorker {
  private workerUrl: URL;
  private stockfishWorker: Worker;
  private verbose: boolean;
  private multipv: number;
  private targetDepth: number;
  readonly game_review_manager: GameReviewManager;

  private waitFor(response: string, errormsg = 'error') {
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (this.verbose) console.debug(e);
        if (e instanceof MessageEvent && e.data.includes(response)) {
          this.stockfishWorker.removeEventListener('message', listener);
          resolve(true);
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      // Add a timeout for error handling (optional)
      setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(`delay time exceeded: ${errormsg}`);
      }, 5000);
    });
  }

  _init(restart = false) {
    return new Promise(async (resolve) => {
      this.stockfishWorker.onmessageerror = (e) => console.debug(e);
      if (!restart) {
        this.stockfishWorker.postMessage('uci');
        await this.waitFor('uciok', 'uci setup error');
      } else console.debug('Restarting engine...');
      this.stockfishWorker.postMessage(`ucinewgame`);
      this.stockfishWorker.postMessage('isready');
      this.stockfishWorker.postMessage(
        `setoption name MultiPV value ${this.multipv}`,
      );
      this.waitFor('readyok', 'this.stockfishWorker not ready after timeout')
        .then(() => {
          resolve(true);
        })
        .catch((err) => {
          throw new Error(err);
        });
    });
  }

  evaluateMove(fen: string = 'startpos'): Promise<EngineLine[]> {
    console.log('evaluate move');
    !fen || fen == 'startpos'
      ? this.stockfishWorker.postMessage(`position startpos`)
      : this.stockfishWorker.postMessage(`position fen ${fen}`);
    this.stockfishWorker.postMessage(`go depth ${this.targetDepth}`);

    let messages = [];
    let lines: EngineLine[] = [];
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (e instanceof MessageEvent) {
          messages.unshift(e.data);
          if (e.data.includes('depth 0')) {
            if (this.verbose) console.warn(`${e}`);
          }
          if (e.data.startsWith('bestmove') || e.data.includes('depth 0')) {
            this.stockfishWorker.removeEventListener('message', listener);
            let searchMessages = messages.filter((msg) =>
              msg.startsWith('info depth'),
            );
            for (let searchMessage of searchMessages) {
              // Extract depth, MultiPV line ID and evaluation from search message
              let idString = searchMessage.match(/(?:multipv )(d+)/)?.[1];
              let depthString = searchMessage.match(/(?:depth )(d+)/)?.[1];

              let bestMove: Lan =
                searchMessage.match(/(?: pv )(.+?)(?= |$)/)?.[1];

              let evaluation: Evaluation = {
                type: searchMessage.includes(' cp ') ? 'cp' : 'mate',
                value: parseInt(
                  searchMessage.match(/(?:(?:cp )|(?:mate ))([d-]+)/)?.[1] ||
                    '0',
                ),
              };
              // Invert evaluation if black to play since scores are from black perspective
              // and we want them always from the perspective of white
              if (fen && fen.includes(' b ')) {
                evaluation.value *= -1;
              }
              // If any piece of data from message is missing, discard message
              if (!idString || !depthString || !bestMove) {
                lines.push(
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                );
                resolve(lines);
                return;
              }

              let id = parseInt(idString);
              let depth = parseInt(depthString);

              // Discard if target depth not reached or lineID already present
              if (
                depth != this.targetDepth ||
                lines.some((line) => line.id == id)
              )
                continue;

              lines.push({
                id,
                depth,
                evaluation,
                bestMove,
              });
            }
            clearTimeout(timeoutId); // Clear the timeout if message is received
            console.debug(lines);
            console.debug('cleared time out id');
            resolve(lines);
          }
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      const timeoutId = setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(new Error('takes alot of time'));
      }, 20000); // Adjust timeout as needed
    });
  }

  private restartWorker(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    console.log('restarting worker....');
    this.stockfishWorker.terminate();
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this._init().then(() => {
      this.evaluateStuckMove(ui_update_callback, move).then((res) => {
        if (res.success) {
          console.warn('continue with work');
          this.evaluatePosition(ui_update_callback);
        } else {
          console.error('restarting worker again');
          this.restartWorker(ui_update_callback, move);
        }
      });
    });
  }

  private async evaluateStuckMove(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    try {
      console.debug('evaluating halt move', move);
      const engineLines = await this.evaluateMove(move.current_fen);
      ui_update_callback();
      this.game_review_manager.add_enginelines(engineLines, move.move_num);
      return { success: true };
    } catch (error) {
      return { success: false };
    }
  }

  async evaluatePosition(ui_update_callback: () => void) {
    console.log('evaluating position');
    while (!this.game_review_manager.done_evaluating()) {
      const currentMove = this.game_review_manager.get_next_move();
      const { current_fen, move_num } = currentMove;
      console.debug({ currentMove });
      try {
        const engineLines = await this.evaluateMove(current_fen);
        const cloud_eval = await getCloudEvaluation(current_fen);
        console.warn({ cloud_eval });
        console.debug({ engineLines });
        ui_update_callback();
        this.game_review_manager.add_enginelines(engineLines, move_num);
      } catch (error) {
        console.error('error');
        console.error('restarting');
        this.restartWorker(ui_update_callback, currentMove);
      }
    }
    console.log('terminate_worker');
    this.stockfishWorker.terminate();
    return { success: true };
  }

  /**
   * @param startingpos : fen format
   * @param multipv : number of lines to return
   * @param verbose : testing
   */
  constructor(
    game_review_manager: GameReviewManager,
    multipv = 2,
    targetDepth = 15,
    verbose = false,
  ) {
    var wasmSupported =
      typeof WebAssembly === 'object' &&
      WebAssembly.validate(
        Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
      );

    this.workerUrl = wasmSupported
      ? new URL('./stockfish/stockfish.js', import.meta.url)
      : new URL('./stockfish/stockfish.wasm.js', import.meta.url);
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this.multipv = multipv;
    //this.evaluateMove = this.evaluateMove.bind(this);
    this.targetDepth = targetDepth;
    this.verbose = verbose;
    //this.waitFor = this.waitFor.bind(this);
    this.game_review_manager = game_review_manager;
  }
}
export default StockfishWorker;

Features:
Initialization: The _init method sets up the Stockfish engine and waits for it to be ready.
Move Evaluation: evaluateMove sends FEN positions to the engine and collects depth-based evaluations.
Timeout Handling: There are multiple timeout mechanisms to ensure the process doesn’t hang.
Worker Restarts: restartWorker attempts to recover the worker if it becomes unresponsive.
Cloud Integration: It also integrates with a cloud evaluation API for additional analysis.
problems:
the engine keeps halting and restarting, and the engine sometimes returns an empty result as the eval even if I know it exists one
when I increase the multiple the failing rises a lot to the point of it not working

My Concerns:
Timeouts and Asynchronous Handling: Are the timeout and retry mechanisms implemented correctly to ensure robust communication with the worker?
Message Filtering: Is the filtering and processing of engine output (info depth, bestmove, etc.) reliable for real-time chess analysis?
Redundancy: Certain parts, like pushing fallback lines with placeholder values (a1a1 moves), seem redundant. Should this be optimized?
Worker Performance: Does terminating and restarting the worker frequently affect performance, and is there a better way to handle “stuck” evaluations?

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Is this StockfishWorker implementation for evaluating chess moves functional and efficient? [closed]

I’m working on a chess analysis tool that uses a StockfishWorker class to evaluate moves and provide engine suggestions. The code is designed to interface with a web worker running Stockfish, handle communication, and process multi-PV lines.

Here’s the complete code for the class:

import type { EngineLine, Evaluation, Lan } from '../types/Game';
import type GameReviewManager from './_GameReviewManager';
import { getCloudEvaluation } from '../api/lichessApiAccess';

class StockfishWorker {
  private workerUrl: URL;
  private stockfishWorker: Worker;
  private verbose: boolean;
  private multipv: number;
  private targetDepth: number;
  readonly game_review_manager: GameReviewManager;

  private waitFor(response: string, errormsg = 'error') {
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (this.verbose) console.debug(e);
        if (e instanceof MessageEvent && e.data.includes(response)) {
          this.stockfishWorker.removeEventListener('message', listener);
          resolve(true);
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      // Add a timeout for error handling (optional)
      setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(`delay time exceeded: ${errormsg}`);
      }, 5000);
    });
  }

  _init(restart = false) {
    return new Promise(async (resolve) => {
      this.stockfishWorker.onmessageerror = (e) => console.debug(e);
      if (!restart) {
        this.stockfishWorker.postMessage('uci');
        await this.waitFor('uciok', 'uci setup error');
      } else console.debug('Restarting engine...');
      this.stockfishWorker.postMessage(`ucinewgame`);
      this.stockfishWorker.postMessage('isready');
      this.stockfishWorker.postMessage(
        `setoption name MultiPV value ${this.multipv}`,
      );
      this.waitFor('readyok', 'this.stockfishWorker not ready after timeout')
        .then(() => {
          resolve(true);
        })
        .catch((err) => {
          throw new Error(err);
        });
    });
  }

  evaluateMove(fen: string = 'startpos'): Promise<EngineLine[]> {
    console.log('evaluate move');
    !fen || fen == 'startpos'
      ? this.stockfishWorker.postMessage(`position startpos`)
      : this.stockfishWorker.postMessage(`position fen ${fen}`);
    this.stockfishWorker.postMessage(`go depth ${this.targetDepth}`);

    let messages = [];
    let lines: EngineLine[] = [];
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (e instanceof MessageEvent) {
          messages.unshift(e.data);
          if (e.data.includes('depth 0')) {
            if (this.verbose) console.warn(`${e}`);
          }
          if (e.data.startsWith('bestmove') || e.data.includes('depth 0')) {
            this.stockfishWorker.removeEventListener('message', listener);
            let searchMessages = messages.filter((msg) =>
              msg.startsWith('info depth'),
            );
            for (let searchMessage of searchMessages) {
              // Extract depth, MultiPV line ID and evaluation from search message
              let idString = searchMessage.match(/(?:multipv )(d+)/)?.[1];
              let depthString = searchMessage.match(/(?:depth )(d+)/)?.[1];

              let bestMove: Lan =
                searchMessage.match(/(?: pv )(.+?)(?= |$)/)?.[1];

              let evaluation: Evaluation = {
                type: searchMessage.includes(' cp ') ? 'cp' : 'mate',
                value: parseInt(
                  searchMessage.match(/(?:(?:cp )|(?:mate ))([d-]+)/)?.[1] ||
                    '0',
                ),
              };
              // Invert evaluation if black to play since scores are from black perspective
              // and we want them always from the perspective of white
              if (fen && fen.includes(' b ')) {
                evaluation.value *= -1;
              }
              // If any piece of data from message is missing, discard message
              if (!idString || !depthString || !bestMove) {
                lines.push(
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                );
                resolve(lines);
                return;
              }

              let id = parseInt(idString);
              let depth = parseInt(depthString);

              // Discard if target depth not reached or lineID already present
              if (
                depth != this.targetDepth ||
                lines.some((line) => line.id == id)
              )
                continue;

              lines.push({
                id,
                depth,
                evaluation,
                bestMove,
              });
            }
            clearTimeout(timeoutId); // Clear the timeout if message is received
            console.debug(lines);
            console.debug('cleared time out id');
            resolve(lines);
          }
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      const timeoutId = setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(new Error('takes alot of time'));
      }, 20000); // Adjust timeout as needed
    });
  }

  private restartWorker(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    console.log('restarting worker....');
    this.stockfishWorker.terminate();
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this._init().then(() => {
      this.evaluateStuckMove(ui_update_callback, move).then((res) => {
        if (res.success) {
          console.warn('continue with work');
          this.evaluatePosition(ui_update_callback);
        } else {
          console.error('restarting worker again');
          this.restartWorker(ui_update_callback, move);
        }
      });
    });
  }

  private async evaluateStuckMove(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    try {
      console.debug('evaluating halt move', move);
      const engineLines = await this.evaluateMove(move.current_fen);
      ui_update_callback();
      this.game_review_manager.add_enginelines(engineLines, move.move_num);
      return { success: true };
    } catch (error) {
      return { success: false };
    }
  }

  async evaluatePosition(ui_update_callback: () => void) {
    console.log('evaluating position');
    while (!this.game_review_manager.done_evaluating()) {
      const currentMove = this.game_review_manager.get_next_move();
      const { current_fen, move_num } = currentMove;
      console.debug({ currentMove });
      try {
        const engineLines = await this.evaluateMove(current_fen);
        const cloud_eval = await getCloudEvaluation(current_fen);
        console.warn({ cloud_eval });
        console.debug({ engineLines });
        ui_update_callback();
        this.game_review_manager.add_enginelines(engineLines, move_num);
      } catch (error) {
        console.error('error');
        console.error('restarting');
        this.restartWorker(ui_update_callback, currentMove);
      }
    }
    console.log('terminate_worker');
    this.stockfishWorker.terminate();
    return { success: true };
  }

  /**
   * @param startingpos : fen format
   * @param multipv : number of lines to return
   * @param verbose : testing
   */
  constructor(
    game_review_manager: GameReviewManager,
    multipv = 2,
    targetDepth = 15,
    verbose = false,
  ) {
    var wasmSupported =
      typeof WebAssembly === 'object' &&
      WebAssembly.validate(
        Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
      );

    this.workerUrl = wasmSupported
      ? new URL('./stockfish/stockfish.js', import.meta.url)
      : new URL('./stockfish/stockfish.wasm.js', import.meta.url);
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this.multipv = multipv;
    //this.evaluateMove = this.evaluateMove.bind(this);
    this.targetDepth = targetDepth;
    this.verbose = verbose;
    //this.waitFor = this.waitFor.bind(this);
    this.game_review_manager = game_review_manager;
  }
}
export default StockfishWorker;

Features:
Initialization: The _init method sets up the Stockfish engine and waits for it to be ready.
Move Evaluation: evaluateMove sends FEN positions to the engine and collects depth-based evaluations.
Timeout Handling: There are multiple timeout mechanisms to ensure the process doesn’t hang.
Worker Restarts: restartWorker attempts to recover the worker if it becomes unresponsive.
Cloud Integration: It also integrates with a cloud evaluation API for additional analysis.
problems:
the engine keeps halting and restarting, and the engine sometimes returns an empty result as the eval even if I know it exists one
when I increase the multiple the failing rises a lot to the point of it not working

My Concerns:
Timeouts and Asynchronous Handling: Are the timeout and retry mechanisms implemented correctly to ensure robust communication with the worker?
Message Filtering: Is the filtering and processing of engine output (info depth, bestmove, etc.) reliable for real-time chess analysis?
Redundancy: Certain parts, like pushing fallback lines with placeholder values (a1a1 moves), seem redundant. Should this be optimized?
Worker Performance: Does terminating and restarting the worker frequently affect performance, and is there a better way to handle “stuck” evaluations?

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Is this StockfishWorker implementation for evaluating chess moves functional and efficient? [closed]

I’m working on a chess analysis tool that uses a StockfishWorker class to evaluate moves and provide engine suggestions. The code is designed to interface with a web worker running Stockfish, handle communication, and process multi-PV lines.

Here’s the complete code for the class:

import type { EngineLine, Evaluation, Lan } from '../types/Game';
import type GameReviewManager from './_GameReviewManager';
import { getCloudEvaluation } from '../api/lichessApiAccess';

class StockfishWorker {
  private workerUrl: URL;
  private stockfishWorker: Worker;
  private verbose: boolean;
  private multipv: number;
  private targetDepth: number;
  readonly game_review_manager: GameReviewManager;

  private waitFor(response: string, errormsg = 'error') {
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (this.verbose) console.debug(e);
        if (e instanceof MessageEvent && e.data.includes(response)) {
          this.stockfishWorker.removeEventListener('message', listener);
          resolve(true);
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      // Add a timeout for error handling (optional)
      setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(`delay time exceeded: ${errormsg}`);
      }, 5000);
    });
  }

  _init(restart = false) {
    return new Promise(async (resolve) => {
      this.stockfishWorker.onmessageerror = (e) => console.debug(e);
      if (!restart) {
        this.stockfishWorker.postMessage('uci');
        await this.waitFor('uciok', 'uci setup error');
      } else console.debug('Restarting engine...');
      this.stockfishWorker.postMessage(`ucinewgame`);
      this.stockfishWorker.postMessage('isready');
      this.stockfishWorker.postMessage(
        `setoption name MultiPV value ${this.multipv}`,
      );
      this.waitFor('readyok', 'this.stockfishWorker not ready after timeout')
        .then(() => {
          resolve(true);
        })
        .catch((err) => {
          throw new Error(err);
        });
    });
  }

  evaluateMove(fen: string = 'startpos'): Promise<EngineLine[]> {
    console.log('evaluate move');
    !fen || fen == 'startpos'
      ? this.stockfishWorker.postMessage(`position startpos`)
      : this.stockfishWorker.postMessage(`position fen ${fen}`);
    this.stockfishWorker.postMessage(`go depth ${this.targetDepth}`);

    let messages = [];
    let lines: EngineLine[] = [];
    return new Promise((resolve, reject) => {
      const listener = <K extends keyof WorkerEventMap>(
        e: WorkerEventMap[K],
      ) => {
        if (e instanceof MessageEvent) {
          messages.unshift(e.data);
          if (e.data.includes('depth 0')) {
            if (this.verbose) console.warn(`${e}`);
          }
          if (e.data.startsWith('bestmove') || e.data.includes('depth 0')) {
            this.stockfishWorker.removeEventListener('message', listener);
            let searchMessages = messages.filter((msg) =>
              msg.startsWith('info depth'),
            );
            for (let searchMessage of searchMessages) {
              // Extract depth, MultiPV line ID and evaluation from search message
              let idString = searchMessage.match(/(?:multipv )(d+)/)?.[1];
              let depthString = searchMessage.match(/(?:depth )(d+)/)?.[1];

              let bestMove: Lan =
                searchMessage.match(/(?: pv )(.+?)(?= |$)/)?.[1];

              let evaluation: Evaluation = {
                type: searchMessage.includes(' cp ') ? 'cp' : 'mate',
                value: parseInt(
                  searchMessage.match(/(?:(?:cp )|(?:mate ))([d-]+)/)?.[1] ||
                    '0',
                ),
              };
              // Invert evaluation if black to play since scores are from black perspective
              // and we want them always from the perspective of white
              if (fen && fen.includes(' b ')) {
                evaluation.value *= -1;
              }
              // If any piece of data from message is missing, discard message
              if (!idString || !depthString || !bestMove) {
                lines.push(
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                  {
                    id: parseInt(idString),
                    depth: parseInt(depthString),
                    evaluation: { type: 'mate', value: 0 },
                    bestMove: 'a1a1',
                  },
                );
                resolve(lines);
                return;
              }

              let id = parseInt(idString);
              let depth = parseInt(depthString);

              // Discard if target depth not reached or lineID already present
              if (
                depth != this.targetDepth ||
                lines.some((line) => line.id == id)
              )
                continue;

              lines.push({
                id,
                depth,
                evaluation,
                bestMove,
              });
            }
            clearTimeout(timeoutId); // Clear the timeout if message is received
            console.debug(lines);
            console.debug('cleared time out id');
            resolve(lines);
          }
        }
      };
      this.stockfishWorker.addEventListener('message', listener);
      const timeoutId = setTimeout(() => {
        this.stockfishWorker.removeEventListener('message', listener);
        reject(new Error('takes alot of time'));
      }, 20000); // Adjust timeout as needed
    });
  }

  private restartWorker(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    console.log('restarting worker....');
    this.stockfishWorker.terminate();
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this._init().then(() => {
      this.evaluateStuckMove(ui_update_callback, move).then((res) => {
        if (res.success) {
          console.warn('continue with work');
          this.evaluatePosition(ui_update_callback);
        } else {
          console.error('restarting worker again');
          this.restartWorker(ui_update_callback, move);
        }
      });
    });
  }

  private async evaluateStuckMove(
    ui_update_callback: () => void,
    move: {
      current_fen: string;
      move_num: number;
      sanmove: string;
    },
  ) {
    try {
      console.debug('evaluating halt move', move);
      const engineLines = await this.evaluateMove(move.current_fen);
      ui_update_callback();
      this.game_review_manager.add_enginelines(engineLines, move.move_num);
      return { success: true };
    } catch (error) {
      return { success: false };
    }
  }

  async evaluatePosition(ui_update_callback: () => void) {
    console.log('evaluating position');
    while (!this.game_review_manager.done_evaluating()) {
      const currentMove = this.game_review_manager.get_next_move();
      const { current_fen, move_num } = currentMove;
      console.debug({ currentMove });
      try {
        const engineLines = await this.evaluateMove(current_fen);
        const cloud_eval = await getCloudEvaluation(current_fen);
        console.warn({ cloud_eval });
        console.debug({ engineLines });
        ui_update_callback();
        this.game_review_manager.add_enginelines(engineLines, move_num);
      } catch (error) {
        console.error('error');
        console.error('restarting');
        this.restartWorker(ui_update_callback, currentMove);
      }
    }
    console.log('terminate_worker');
    this.stockfishWorker.terminate();
    return { success: true };
  }

  /**
   * @param startingpos : fen format
   * @param multipv : number of lines to return
   * @param verbose : testing
   */
  constructor(
    game_review_manager: GameReviewManager,
    multipv = 2,
    targetDepth = 15,
    verbose = false,
  ) {
    var wasmSupported =
      typeof WebAssembly === 'object' &&
      WebAssembly.validate(
        Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
      );

    this.workerUrl = wasmSupported
      ? new URL('./stockfish/stockfish.js', import.meta.url)
      : new URL('./stockfish/stockfish.wasm.js', import.meta.url);
    this.stockfishWorker = new Worker(this.workerUrl, { type: 'classic' });
    this.multipv = multipv;
    //this.evaluateMove = this.evaluateMove.bind(this);
    this.targetDepth = targetDepth;
    this.verbose = verbose;
    //this.waitFor = this.waitFor.bind(this);
    this.game_review_manager = game_review_manager;
  }
}
export default StockfishWorker;

Features:
Initialization: The _init method sets up the Stockfish engine and waits for it to be ready.
Move Evaluation: evaluateMove sends FEN positions to the engine and collects depth-based evaluations.
Timeout Handling: There are multiple timeout mechanisms to ensure the process doesn’t hang.
Worker Restarts: restartWorker attempts to recover the worker if it becomes unresponsive.
Cloud Integration: It also integrates with a cloud evaluation API for additional analysis.
problems:
the engine keeps halting and restarting, and the engine sometimes returns an empty result as the eval even if I know it exists one
when I increase the multiple the failing rises a lot to the point of it not working

My Concerns:
Timeouts and Asynchronous Handling: Are the timeout and retry mechanisms implemented correctly to ensure robust communication with the worker?
Message Filtering: Is the filtering and processing of engine output (info depth, bestmove, etc.) reliable for real-time chess analysis?
Redundancy: Certain parts, like pushing fallback lines with placeholder values (a1a1 moves), seem redundant. Should this be optimized?
Worker Performance: Does terminating and restarting the worker frequently affect performance, and is there a better way to handle “stuck” evaluations?

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật