Skip to content

Commit

Permalink
Fix stochastic ponder issue
Browse files Browse the repository at this point in the history
  • Loading branch information
sunfish-shogi committed Feb 1, 2025
1 parent fba458b commit 2adc376
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 115 deletions.
6 changes: 6 additions & 0 deletions src/common/settings/usi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const USIMultiPV = "USI_MultiPV";
export const Threads = "Threads";
export const NumberOfThreads = "NumberOfThreads";
export const MultiPV = "MultiPV";
export const StochasticPonder = "Stochastic_Ponder";

export type USIEngineOptionType = "check" | "spin" | "combo" | "button" | "string" | "filename";

Expand Down Expand Up @@ -90,6 +91,11 @@ export function getUSIEngineMultiPV(engine: USIEngine): number | undefined {
return value as number | undefined;
}

export function getUSIEngineStochasticPonder(engine: USIEngine): boolean {
const value = getUSIEngineOptionCurrentValue(engine.options[StochasticPonder]);
return value === "true";
}

export type USIEngineOptions = { [name: string]: USIEngineOption };

export enum USIEngineLabel {
Expand Down
56 changes: 26 additions & 30 deletions src/renderer/players/usi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { parseUSIPV, USIInfoCommand } from "@/common/game/usi";
import {
getUSIEngineMultiPV,
getUSIEnginePonder,
getUSIEngineStochasticPonder,
MultiPV,
USIEngine,
USIMultiPV,
Expand All @@ -12,18 +13,22 @@ import { Player, SearchInfo, SearchHandler, MateHandler } from "./player";
import { GameResult } from "@/common/game/result";
import { TimeStates } from "@/common/game/time";

type onReceiveUSIBestMoveHandler = (sessionID: number, usi: string, usiMove: string) => void;
type onReceiveUSIBestMoveHandler = (
sessionID: number,
position: ImmutablePosition,
usiMove: string,
) => void;

type onUpdateUSIInfoHandler = (
sessionID: number,
usi: string,
position: ImmutablePosition,
name: string,
info: USIInfoCommand,
ponderMove?: Move,
) => void;

let onReceiveUSIBestMove: onReceiveUSIBestMoveHandler = () => {};
let onUpdateUSIInfo: onUpdateUSIInfoHandler = () => {};
let onUpdateUSIPonderInfo: onUpdateUSIInfoHandler = () => {};

export function setOnReceiveUSIBestMoveHandler(handler: onReceiveUSIBestMoveHandler) {
onReceiveUSIBestMove = handler;
Expand All @@ -33,18 +38,14 @@ export function setOnUpdateUSIInfoHandler(handler: onUpdateUSIInfoHandler) {
onUpdateUSIInfo = handler;
}

export function setOnUpdateUSIPonderInfoHandler(handler: onUpdateUSIInfoHandler) {
onUpdateUSIPonderInfo = handler;
}

export class USIPlayer implements Player {
private _sessionID = 0;
private usi?: string;
private position?: Position;
private searchHandler?: SearchHandler;
private mateHandler?: MateHandler;
private ponder?: string;
private inPonder = false;
private ponderMove?: Move;
private info?: SearchInfo;
private usiInfoTimeout?: number;
private customMultiPV?: number;
Expand Down Expand Up @@ -86,13 +87,13 @@ export class USIPlayer implements Player {
this.searchHandler = handler;
this.usi = usi;
this.position = position.clone();
if (this.inPonder && this.ponder === this.usi) {
if (this.ponderMove && this.ponder === this.usi) {
api.usiPonderHit(this.sessionID, timeStates);
} else {
this.info = undefined;
await api.usiGo(this.sessionID, this.usi, timeStates);
}
this.inPonder = false;
this.ponderMove = undefined;
this.ponder = undefined;
}

Expand All @@ -107,7 +108,7 @@ export class USIPlayer implements Player {
}
// 連続して Ponder を開始しない。
// NOTE: 早期 Ponder 機能を有効にすると早期実行と通常実行の 2 回の呼び出しが来る。
if (this.inPonder) {
if (this.ponderMove) {
return;
}
// 現在局面までの USI が前方一致しているか確認する。
Expand All @@ -125,9 +126,11 @@ export class USIPlayer implements Player {
this.clearHandlers();
this.usi = this.ponder;
this.position = position.clone();
this.position.doMove(ponderMove);
if (!this.stochasticPonder) {
this.position.doMove(ponderMove);
}
this.info = undefined;
this.inPonder = true;
this.ponderMove = ponderMove;
await api.usiGoPonder(this.sessionID, this.ponder, timeStates);
}

Expand Down Expand Up @@ -180,6 +183,7 @@ export class USIPlayer implements Player {
if (usi !== this.usi) {
return;
}
onReceiveUSIBestMove(this.sessionID, this.position, usiMove);
if (usiMove === "resign") {
searchHandler.onResign();
return;
Expand Down Expand Up @@ -212,6 +216,7 @@ export class USIPlayer implements Player {
if (usi !== this.usi || !this.position) {
return;
}
onUpdateUSIInfo(this.sessionID, this.position, this.name, { pv: usiMoves });

Check warning on line 219 in src/renderer/players/usi.ts

View check run for this annotation

Codecov / codecov/patch

src/renderer/players/usi.ts#L219

Added line #L219 was not covered by tests
const mateHandler = this.mateHandler;
this.clearHandlers();
if (!mateHandler) {
Expand Down Expand Up @@ -262,6 +267,7 @@ export class USIPlayer implements Player {
if (usi !== this.usi || !this.position) {
return;
}
onUpdateUSIInfo(this.sessionID, this.position, this.name, infoCommand, this.ponderMove);
if (infoCommand.multipv && infoCommand.multipv !== 1) {
return;
}
Expand All @@ -282,7 +288,7 @@ export class USIPlayer implements Player {
pv: (pv && parseUSIPV(this.position, pv)) ?? this.info?.pv,
};
// Ponder 中はハンドラーを呼ばない。
if (this.inPonder) {
if (this.ponderMove) {
return;
}
// 高頻度でコマンドが送られてくると描画が追いつかないので、一定時間ごとに反映する。
Expand Down Expand Up @@ -319,23 +325,23 @@ export class USIPlayer implements Player {
await api.usiSetOption(this.sessionID, option.name, multiPV.toFixed(0));
this.customMultiPV = multiPV;
}

get stochasticPonder(): boolean {
return getUSIEngineStochasticPonder(this.engine);
}
}

const usiPlayers: { [sessionID: number]: USIPlayer } = {};

export function onUSIBestMove(sessionID: number, usi: string, usiMove: string, ponder?: string) {
usiPlayers[sessionID]?.onBestMove(usi, usiMove, ponder);
onReceiveUSIBestMove(sessionID, usi, usiMove);
}

export function onUSICheckmate(sessionID: number, usi: string, usiMoves: string[]) {
const player = usiPlayers[sessionID];
if (!player) {
return;
}
onUpdateUSIInfo(sessionID, usi, player.name, {
pv: usiMoves,
});
player.onCheckmate(usi, usiMoves);
}

Expand All @@ -352,21 +358,11 @@ export function onUSINoMate(sessionID: number, usi: string) {
}

export function onUSIInfo(sessionID: number, usi: string, info: USIInfoCommand) {
const player = usiPlayers[sessionID];
if (!player) {
return;
}
player.onUSIInfo(usi, info);
onUpdateUSIInfo(sessionID, usi, player.name, info);
usiPlayers[sessionID]?.onUSIInfo(usi, info);
}

export function onUSIPonderInfo(sessionID: number, usi: string, info: USIInfoCommand) {
const player = usiPlayers[sessionID];
if (!player) {
return;
}
player.onUSIInfo(usi, info);
onUpdateUSIPonderInfo(sessionID, usi, player.name, info);
usiPlayers[sessionID]?.onUSIInfo(usi, info);

Check warning on line 365 in src/renderer/players/usi.ts

View check run for this annotation

Codecov / codecov/patch

src/renderer/players/usi.ts#L365

Added line #L365 was not covered by tests
}

export function isActiveUSIPlayerSession(sessionID: number): boolean {
Expand Down
51 changes: 14 additions & 37 deletions src/renderer/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
ImmutableRecord,
Move,
PositionChange,
Record,
formatSpecialMove,
exportKIF,
RecordMetadataKey,
Expand Down Expand Up @@ -57,11 +56,7 @@ import { t } from "@/common/i18n";
import { MateSearchManager } from "./mate";
import { detectUnsupportedRecordProperties } from "@/renderer/helpers/record";
import { RecordFileFormat, detectRecordFileFormatByPath } from "@/common/file/record";
import {
setOnReceiveUSIBestMoveHandler,
setOnUpdateUSIInfoHandler,
setOnUpdateUSIPonderInfoHandler,
} from "@/renderer/players/usi";
import { setOnReceiveUSIBestMoveHandler, setOnUpdateUSIInfoHandler } from "@/renderer/players/usi";
import { useErrorStore } from "./error";
import { useBusyState } from "./busy";
import { Confirmation, useConfirmationStore } from "./confirm";
Expand Down Expand Up @@ -213,7 +208,6 @@ class Store {
.on("error", this.onCheckmateError.bind(refs));
setOnReceiveUSIBestMoveHandler(this.endUSIInfoIteration.bind(refs));
setOnUpdateUSIInfoHandler(this.updateUSIInfo.bind(refs));
setOnUpdateUSIPonderInfoHandler(this.updateUSIPonderInfo.bind(refs));
}

addEventListener(event: "changePosition", handler: ChangePositionHandler): void;
Expand Down Expand Up @@ -463,6 +457,9 @@ class Store {
const candidates: Move[] = [];
const usiSet = new Set<string>();
for (const session of this.usiMonitor.sessions) {
if (session.ponderMove) {
continue;
}

Check warning on line 462 in src/renderer/store/index.ts

View check run for this annotation

Codecov / codecov/patch

src/renderer/store/index.ts#L461-L462

Added lines #L461 - L462 were not covered by tests
let entryCount = 0;
let maxScore = -Infinity;
for (const iteration of session.latestIteration) {
Expand Down Expand Up @@ -533,41 +530,21 @@ class Store {
this.researchManager.setMultiPV(sessionID, multiPV);
}

endUSIInfoIteration(sessionID: number, usi: string): void {
if (this.recordManager.record.usi !== usi) {
return;
}
this.usiMonitor.endIteration(sessionID, this.recordManager.record.position);
endUSIInfoIteration(sessionID: number, position: ImmutablePosition): void {
this.usiMonitor.endIteration(sessionID, position);
}

updateUSIInfo(sessionID: number, usi: string, name: string, info: USIInfoCommand): void {
if (this.recordManager.record.usi !== usi) {
return;
}
const appSettings = useAppSettings();
this.usiMonitor.update(
sessionID,
this.recordManager.record.position,
name,
info,
appSettings.maxPVTextLength,
);
}

updateUSIPonderInfo(sessionID: number, usi: string, name: string, info: USIInfoCommand): void {
const record = Record.newByUSI(usi);
if (record instanceof Error) {
api.log(LogLevel.ERROR, `invalid USI: ${usi} (updateUSIPonderInfo)`);
return;
}
const ponderMove = record.current.move;
if (!(ponderMove instanceof Move)) {
return;
}
updateUSIInfo(
sessionID: number,
position: ImmutablePosition,
name: string,
info: USIInfoCommand,
ponderMove?: Move,
): void {
const appSettings = useAppSettings();
this.usiMonitor.update(
sessionID,
record.position,
position,
name,
info,
appSettings.maxPVTextLength,
Expand Down
15 changes: 10 additions & 5 deletions src/renderer/store/usi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,9 @@ export class USIPlayerMonitor {
}

endIteration(sfen: string) {
if (this.sfen !== sfen) {
return;
if (this.sfen === sfen) {
this.refreshOnNextUpdate = true;
}
this.refreshOnNextUpdate = true;
}
}

Expand Down Expand Up @@ -228,6 +227,8 @@ export class USIMonitor {
this._update(update);
}
this.updateQueue = [];

clearTimeout(this.timeoutHandle);
this.timeoutHandle = undefined;
}

Expand All @@ -251,8 +252,12 @@ export class USIMonitor {
endIteration(sessionID: number, position: ImmutablePosition) {
const monitor = this._sessions.find((session) => session.sessionID === sessionID);
if (monitor) {
this.dequeue(); // flush the queue
monitor.endIteration(position.sfen);
const sfen = position.sfen;
// Invoke asynchronously to prevent IPC message delay.
setTimeout(() => {
this.dequeue(); // flush the queue
monitor.endIteration(sfen);
});
}
}
}
Loading

0 comments on commit 2adc376

Please sign in to comment.