feat(SCOPONE-0005): complete iteration 0 — AI mastery levels, score bar fix, difficulty selection

This commit is contained in:
Giancarmine Salucci
2026-03-31 22:22:24 +02:00
parent 6c01044c71
commit 0a030d0f01
5 changed files with 580 additions and 136 deletions

68
src/game/card-tracker.ts Normal file
View File

@@ -0,0 +1,68 @@
import { Card, Suit, SUITS } from './types';
/**
* Tracks which cards have been played/captured during a round.
* Used by AI to infer opponent hands WITHOUT cheating.
*/
export class CardTracker {
private played: Set<string> = new Set(); // card IDs that have been seen
/** Record a card being played to the table */
trackPlay(card: Card): void {
this.played.add(card.id);
}
/** Record cards captured from the table */
trackCapture(cards: Card[]): void {
for (const c of cards) {
this.played.add(c.id);
}
}
/** Reset for a new round */
reset(): void {
this.played.clear();
}
/** Has a specific card been seen played? */
hasBeenPlayed(cardId: string): boolean {
return this.played.has(cardId);
}
/** Is the settebello (7 of denara) still unseen (not played/captured)? */
isSettebelloUnseen(): boolean {
return !this.played.has('denara_7');
}
/**
* Get cards that could be in opponent hands.
* = full 40-card deck minus: already played, my hand, currently on table
*/
getUnseenCards(myHand: Card[], table: Card[]): Card[] {
const known = new Set<string>();
for (const id of this.played) known.add(id);
for (const c of myHand) known.add(c.id);
for (const c of table) known.add(c.id);
const unseen: Card[] = [];
for (const suit of SUITS) {
for (let v = 1; v <= 10; v++) {
const id = `${suit}_${v}`;
if (!known.has(id)) {
unseen.push({ suit, value: v, id });
}
}
}
return unseen;
}
/** Count how many cards of a suit are still unseen */
countRemainingSuit(suit: Suit, myHand: Card[], table: Card[]): number {
return this.getUnseenCards(myHand, table).filter(c => c.suit === suit).length;
}
/** Get count of all played/seen cards */
get playedCount(): number {
return this.played.size;
}
}