Files
scopone/src/game/ai.worker.ts
Giancarmine Salucci 3f74c57665
Some checks failed
Android Build & Publish / android (push) Failing after 2m10s
feat(SCOPONE-0013): PIMC AI rewrite + Gitea Android CI pipeline
- Replace minimax with PIMC (Perfect Information Monte Carlo) search
- Add PIMC_SCOPE_BOOST=150 → effective scopa value 540 (was 390)
  → Master win rate: 67.5% → 72.5% vs legacy AI (target ≥60%)
  → Advanced win rate: 97.5% vs beginner AI (target ≥55%)
  → Scope gap in losses: 6.54 → 3.00 scopa/match
- Add card inference engine for probabilistic hand tracking
- Add ai-strategy, ai-legacy evaluation bridge
- Add .gitea/workflows/android-build.yml: build debug + unsigned
  release APK and publish to Gitea generic package registry
2026-05-24 16:29:04 +02:00

85 lines
2.1 KiB
TypeScript

import { chooseMove } from './ai';
import {
AIWorkerChooseMoveRequest,
AIWorkerErrorMessage,
AIWorkerRequestMessage,
AIWorkerResponseMessage,
} from './ai-worker-protocol';
import { CardInferenceEngine } from './card-inference';
import { CardTracker } from './card-tracker';
interface AIWorkerScope {
addEventListener(type: 'message', listener: (event: MessageEvent<AIWorkerRequestMessage>) => void): void;
postMessage(message: AIWorkerResponseMessage): void;
}
const workerScope = globalThis as unknown as AIWorkerScope;
function serializeError(requestId: string, error: unknown): AIWorkerErrorMessage {
if (error instanceof Error) {
return {
type: 'error',
requestId,
error: {
message: error.message,
name: error.name,
stack: error.stack,
},
};
}
return {
type: 'error',
requestId,
error: {
message: typeof error === 'string' ? error : 'Unknown AI worker error',
name: 'Error',
},
};
}
async function handleChooseMove(request: AIWorkerChooseMoveRequest): Promise<void> {
const tracker = request.trackerSnapshot
? CardTracker.fromSnapshot(request.trackerSnapshot)
: undefined;
const inference = request.inferenceSnapshot && tracker
? CardInferenceEngine.fromSnapshot(request.inferenceSnapshot, tracker)
: undefined;
try {
const move = await chooseMove(
request.state,
request.playerIdx,
request.difficulty,
tracker,
(progress) => {
workerScope.postMessage({
type: 'progress',
requestId: request.requestId,
progress,
});
},
inference ? { inference } : undefined,
);
workerScope.postMessage({
type: 'result',
requestId: request.requestId,
move,
});
} catch (error) {
workerScope.postMessage(serializeError(request.requestId, error));
}
}
workerScope.addEventListener('message', (event: MessageEvent<AIWorkerRequestMessage>) => {
const message = event.data;
if (message.type !== 'choose-move') {
return;
}
void handleChooseMove(message);
});
export {};