Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 10x 10x 10x 10x 10x 10x 10x 10x 16x 9x 9x 9x 9x 16x 3x 3x 13x 13x 13x 13x 8x 8x 16x 5x 3x 2x 1x 2x 13x 6x 9x 9x 9x 4x 4x 4x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 2x 1x | import { IBaseSearchOptions,
IBaseSearchInfo,
IBaseSearchResult,
IEngine,
EngineErrorCode, } from "../types.js";
import { EngineError } from "../errors/EngineError.js";
/**
* 棋譜全体の非同期一括解析を管理するクラス。
* 優先度制御(割り込み優先)と制御(pause/resume/abort)が可能。
*/
export class EngineBatchAnalyzer<
T_OPTIONS extends IBaseSearchOptions,
T_INFO extends IBaseSearchInfo,
T_RESULT extends IBaseSearchResult,
> {
private queue: T_OPTIONS[] = [];
private results: T_RESULT[] = [];
private isPaused = false;
private isAborted = false;
private currentTaskPromise: Promise<T_RESULT> | null = null;
private runPromise: Promise<T_RESULT[]> | null = null;
private currentIndex = 0;
constructor(private engine: IEngine<T_OPTIONS, T_INFO, T_RESULT>) {}
/**
* 解析キューに局面を追加します。
*/
public add(options: T_OPTIONS): void {
this.queue.push(options);
}
/**
* キューにある全ての局面の解析を開始します。
*/
public async analyzeAll(
onProgress?: (index: number, total: number, result: T_RESULT) => void,
): Promise<T_RESULT[]> {
Iif (this.runPromise) return this.runPromise;
this.runPromise = (async () => {
this.isAborted = false;
while (this.currentIndex < this.queue.length && !this.isAborted) {
if (this.isPaused) {
await new Promise((resolve) => setTimeout(resolve, 500));
continue;
}
const options = this.queue[this.currentIndex]!;
try {
this.currentTaskPromise = this.engine.search(options);
const result = await this.currentTaskPromise;
this.results[this.currentIndex] = result;
onProgress?.(this.currentIndex + 1, this.queue.length, result);
this.currentIndex++;
} catch (err) {
if (
err instanceof EngineError &&
err.code === EngineErrorCode.CANCELLED
) {
// Pause or Abort during search
if (this.isAborted) break;
if (this.isPaused) continue;
throw err;
}
throw err;
} finally {
this.currentTaskPromise = null;
}
}
return [...this.results];
})();
try {
return await this.runPromise;
} finally {
this.runPromise = null;
}
}
/**
* 解析を一時停止します。
*/
public pause(): void {
this.isPaused = true;
this.engine.stop();
}
/**
* 解析を再開します。
*/
public resume(): void {
this.isPaused = false;
}
/**
* 解析を中止します。
*/
public abort(): void {
this.isAborted = true;
this.engine.stop();
}
/**
* 特定の局面を最優先(割り込み)で解析します。
* 現在進行中のバッチ処理は一時停止されます。
*/
public async analyzePriority(options: T_OPTIONS): Promise<T_RESULT> {
const wasPaused = this.isPaused;
this.pause();
try {
// 進行中のタスクが止まるのを待つ
Eif (this.currentTaskPromise) {
try {
await this.currentTaskPromise;
} catch (err) {
Eif (
!(
err instanceof EngineError &&
err.code === EngineErrorCode.CANCELLED
)
) {
throw err;
}
}
}
return await this.engine.search(options);
} finally {
Eif (!wasPaused) this.resume();
}
}
public get progress(): { current: number; total: number } {
return { current: this.currentIndex, total: this.queue.length };
}
}
|