fix(SCOPONE-0012): complete iteration 3 - pass ai quality gate

This commit is contained in:
Giancarmine Salucci
2026-04-11 21:02:58 +02:00
parent 3d76fb544f
commit 7a64e923f1
2 changed files with 206 additions and 17 deletions

View File

@@ -11,6 +11,10 @@ export type AIBenchmarkCriticalConcept =
| 'partner-scopa-setup'
| 'settebello-capture'
| 'anti-scopa-defense'
| 'cards-majority-conversion'
| 'denari-denial'
| 'primiera-denial'
| 'partner-preserving-quiet-release'
| 'dealer-rank-residue-preservation'
| 'exact-endgame-resolution';
@@ -90,7 +94,7 @@ const RAW_FIXTURES: RawFixture[] = [
{
id: 'anti-scopa-safe-dump',
name: 'Anti-Scopa Safe Dump',
description: 'The root player should prefer the safe high dump instead of taking a flashy but dangerous capture.',
description: 'The root player should take the clean five capture that removes the only cheap concession, rather than floating a tenth card into the same anti-scopa race.',
tags: ['critical-anti-scopa', 'table-control'],
criticalConcept: 'anti-scopa-defense',
dealer: 0,
@@ -107,27 +111,34 @@ const RAW_FIXTURES: RawFixture[] = [
piles: PILES_TEMPLATE_A,
totalPoints: [8, 8],
expectedMove: {
cardId: 'spade_10',
cardId: 'denara_5',
captureIds: ['coppe_5'],
},
},
{
id: 'dealer-rank-residue-preserve-pair',
name: 'Dealer Rank Residue Preserve Pair',
description: 'The dealer should keep the double-nine structure intact and release the harmless low card.',
description: 'The dealer should keep the double-nine structure intact and release the harmless three into the heavy 6+8+8 table instead of breaking higher-rank control.',
tags: ['critical-dealer-rank-residue', 'dealer-side-control'],
criticalConcept: 'dealer-rank-residue-preservation',
dealer: 3,
currentPlayer: 3,
handSizes: [5, 5, 5, 5],
hands: [undefined, undefined, undefined, [
hands: [[
'bastoni_1',
'bastoni_2',
'bastoni_4',
'bastoni_5',
'spade_5',
], undefined, undefined, [
'spade_3',
'denara_9',
'coppe_9',
'bastoni_10',
'denara_5',
]],
table: ['denara_2', 'coppe_5', 'bastoni_4', 'spade_8'],
piles: PILES_TEMPLATE_A,
table: ['bastoni_6', 'spade_8', 'coppe_8'],
pileCardCounts: [5, 4, 4, 4],
scopes: [0, 1, 0, 1],
totalPoints: [9, 7],
expectedMove: {
@@ -180,8 +191,8 @@ const RAW_FIXTURES: RawFixture[] = [
},
{
id: 'partner-scopa-setup',
name: 'Partner Scopa Setup',
description: 'When there is no safe immediate sweep, the root player should prefer the quiet partner invitation that preserves table pressure for the partner line instead of cashing a smaller material capture.',
name: 'Partner Pressure Setup',
description: 'Instead of floating a sterile ten, the root player should take the low five capture that strips the loose 1+4 total and leaves a heavy 7+8 table the next opponent cannot immediately cash, preserving team pressure into the partner rotation.',
tags: ['critical-partner-setup', 'partner-window', 'table-control'],
criticalConcept: 'partner-scopa-setup',
dealer: 0,
@@ -190,7 +201,7 @@ const RAW_FIXTURES: RawFixture[] = [
hands: [
undefined,
['coppe_10', 'spade_6', 'bastoni_3', 'denara_5', 'coppe_2'],
['spade_10', 'denara_6', 'bastoni_4', 'coppe_9', 'denara_8'],
['spade_10', 'denara_6', 'bastoni_4', 'coppe_9', 'spade_3'],
undefined,
],
table: ['denara_1', 'coppe_4', 'bastoni_7', 'spade_8'],
@@ -198,7 +209,8 @@ const RAW_FIXTURES: RawFixture[] = [
scopes: [0, 1, 0, 1],
totalPoints: [8, 9],
expectedMove: {
cardId: 'coppe_10',
cardId: 'denara_5',
captureIds: ['denara_1', 'coppe_4'],
},
},
{
@@ -360,6 +372,115 @@ const RAW_FIXTURES: RawFixture[] = [
cardId: 'coppe_8',
},
},
{
id: 'cards-majority-conversion',
name: 'Cards Majority Conversion',
description: 'With the cards race on a knife edge, the search should take the two-card conversion that secures team majority instead of the single-card direct grab.',
tags: ['critical-cards-majority-conversion', 'team-race', 'material-margin'],
criticalConcept: 'cards-majority-conversion',
dealer: 1,
currentPlayer: 0,
handSizes: [5, 5, 5, 5],
hands: [[
'bastoni_9',
'spade_8',
'denara_2',
'coppe_3',
'spade_6',
], undefined, undefined, undefined],
table: ['coppe_4', 'spade_5', 'denara_8', 'bastoni_10'],
pileCardCounts: [5, 3, 5, 3],
scopes: [1, 0, 1, 0],
totalPoints: [9, 9],
expectedMove: {
cardId: 'bastoni_9',
captureIds: ['coppe_4', 'spade_5'],
},
},
{
id: 'denari-denial-window',
name: 'Denari Denial Window',
description: 'The root player should strip the exposed denari immediately, before the heavier table turns into a generic control race, because the team denari count is still live.',
tags: ['critical-denari-denial', 'denari-race', 'team-defense'],
criticalConcept: 'denari-denial',
dealer: 2,
currentPlayer: 1,
handSizes: [5, 5, 5, 5],
hands: [undefined, [
'spade_6',
'bastoni_5',
'coppe_3',
'spade_10',
'spade_2',
], undefined, undefined],
table: ['denara_6', 'coppe_7', 'bastoni_8', 'spade_9'],
pileCardCounts: [4, 4, 4, 4],
totalPoints: [9, 9],
expectedMove: {
cardId: 'spade_6',
captureIds: ['denara_6'],
},
},
{
id: 'primiera-denial-window',
name: 'Primiera Denial Window',
description: 'The benchmark should prefer removing the exposed seven that swings primiera control instead of banking the larger but strategically softer material capture.',
tags: ['critical-primiera-denial', 'primiera-pressure', 'team-defense'],
criticalConcept: 'primiera-denial',
dealer: 3,
currentPlayer: 2,
handSizes: [5, 5, 5, 5],
hands: [undefined, undefined, [
'spade_7',
'coppe_8',
'denara_2',
'bastoni_6',
'spade_9',
], undefined],
table: ['coppe_7', 'denara_4', 'spade_1', 'bastoni_3'],
pileCardCounts: [4, 4, 4, 4],
totalPoints: [8, 8],
expectedMove: {
cardId: 'spade_7',
captureIds: ['coppe_7'],
},
},
{
id: 'partner-preserving-quiet-release',
name: 'Partner Preserving Quiet Release',
description: 'Rather than breaking the paired sevens or the denari structure, the root player should release the quiet two that keeps the 2+8 ten line alive for the partner while the intervening opponent still has no immediate capture.',
tags: ['critical-partner-preserving-quiet-release', 'partner-window', 'quiet-release', 'table-control'],
criticalConcept: 'partner-preserving-quiet-release',
dealer: 0,
currentPlayer: 0,
handSizes: [5, 5, 5, 5],
hands: [[
'coppe_2',
'denara_7',
'coppe_7',
'denara_5',
'bastoni_4',
], [
'bastoni_1',
'bastoni_3',
'bastoni_5',
'bastoni_7',
'coppe_1',
], [
'spade_10',
'denara_8',
'coppe_9',
'bastoni_2',
'spade_6',
], undefined],
table: ['bastoni_8', 'spade_9', 'coppe_6'],
pileCardCounts: [5, 4, 4, 4],
scopes: [0, 1, 0, 1],
totalPoints: [8, 9],
expectedMove: {
cardId: 'coppe_2',
},
},
];
function cloneCard(card: Card): Card {