fix(SCOPONE-0012): complete iteration 3 - pass ai quality gate
This commit is contained in:
@@ -11,6 +11,10 @@ export type AIBenchmarkCriticalConcept =
|
|||||||
| 'partner-scopa-setup'
|
| 'partner-scopa-setup'
|
||||||
| 'settebello-capture'
|
| 'settebello-capture'
|
||||||
| 'anti-scopa-defense'
|
| 'anti-scopa-defense'
|
||||||
|
| 'cards-majority-conversion'
|
||||||
|
| 'denari-denial'
|
||||||
|
| 'primiera-denial'
|
||||||
|
| 'partner-preserving-quiet-release'
|
||||||
| 'dealer-rank-residue-preservation'
|
| 'dealer-rank-residue-preservation'
|
||||||
| 'exact-endgame-resolution';
|
| 'exact-endgame-resolution';
|
||||||
|
|
||||||
@@ -90,7 +94,7 @@ const RAW_FIXTURES: RawFixture[] = [
|
|||||||
{
|
{
|
||||||
id: 'anti-scopa-safe-dump',
|
id: 'anti-scopa-safe-dump',
|
||||||
name: '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'],
|
tags: ['critical-anti-scopa', 'table-control'],
|
||||||
criticalConcept: 'anti-scopa-defense',
|
criticalConcept: 'anti-scopa-defense',
|
||||||
dealer: 0,
|
dealer: 0,
|
||||||
@@ -107,27 +111,34 @@ const RAW_FIXTURES: RawFixture[] = [
|
|||||||
piles: PILES_TEMPLATE_A,
|
piles: PILES_TEMPLATE_A,
|
||||||
totalPoints: [8, 8],
|
totalPoints: [8, 8],
|
||||||
expectedMove: {
|
expectedMove: {
|
||||||
cardId: 'spade_10',
|
cardId: 'denara_5',
|
||||||
|
captureIds: ['coppe_5'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'dealer-rank-residue-preserve-pair',
|
id: 'dealer-rank-residue-preserve-pair',
|
||||||
name: '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'],
|
tags: ['critical-dealer-rank-residue', 'dealer-side-control'],
|
||||||
criticalConcept: 'dealer-rank-residue-preservation',
|
criticalConcept: 'dealer-rank-residue-preservation',
|
||||||
dealer: 3,
|
dealer: 3,
|
||||||
currentPlayer: 3,
|
currentPlayer: 3,
|
||||||
handSizes: [5, 5, 5, 5],
|
handSizes: [5, 5, 5, 5],
|
||||||
hands: [undefined, undefined, undefined, [
|
hands: [[
|
||||||
|
'bastoni_1',
|
||||||
|
'bastoni_2',
|
||||||
|
'bastoni_4',
|
||||||
|
'bastoni_5',
|
||||||
|
'spade_5',
|
||||||
|
], undefined, undefined, [
|
||||||
'spade_3',
|
'spade_3',
|
||||||
'denara_9',
|
'denara_9',
|
||||||
'coppe_9',
|
'coppe_9',
|
||||||
'bastoni_10',
|
'bastoni_10',
|
||||||
'denara_5',
|
'denara_5',
|
||||||
]],
|
]],
|
||||||
table: ['denara_2', 'coppe_5', 'bastoni_4', 'spade_8'],
|
table: ['bastoni_6', 'spade_8', 'coppe_8'],
|
||||||
piles: PILES_TEMPLATE_A,
|
pileCardCounts: [5, 4, 4, 4],
|
||||||
scopes: [0, 1, 0, 1],
|
scopes: [0, 1, 0, 1],
|
||||||
totalPoints: [9, 7],
|
totalPoints: [9, 7],
|
||||||
expectedMove: {
|
expectedMove: {
|
||||||
@@ -180,8 +191,8 @@ const RAW_FIXTURES: RawFixture[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'partner-scopa-setup',
|
id: 'partner-scopa-setup',
|
||||||
name: 'Partner Scopa Setup',
|
name: 'Partner Pressure 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.',
|
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'],
|
tags: ['critical-partner-setup', 'partner-window', 'table-control'],
|
||||||
criticalConcept: 'partner-scopa-setup',
|
criticalConcept: 'partner-scopa-setup',
|
||||||
dealer: 0,
|
dealer: 0,
|
||||||
@@ -190,7 +201,7 @@ const RAW_FIXTURES: RawFixture[] = [
|
|||||||
hands: [
|
hands: [
|
||||||
undefined,
|
undefined,
|
||||||
['coppe_10', 'spade_6', 'bastoni_3', 'denara_5', 'coppe_2'],
|
['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,
|
undefined,
|
||||||
],
|
],
|
||||||
table: ['denara_1', 'coppe_4', 'bastoni_7', 'spade_8'],
|
table: ['denara_1', 'coppe_4', 'bastoni_7', 'spade_8'],
|
||||||
@@ -198,7 +209,8 @@ const RAW_FIXTURES: RawFixture[] = [
|
|||||||
scopes: [0, 1, 0, 1],
|
scopes: [0, 1, 0, 1],
|
||||||
totalPoints: [8, 9],
|
totalPoints: [8, 9],
|
||||||
expectedMove: {
|
expectedMove: {
|
||||||
cardId: 'coppe_10',
|
cardId: 'denara_5',
|
||||||
|
captureIds: ['denara_1', 'coppe_4'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -360,6 +372,115 @@ const RAW_FIXTURES: RawFixture[] = [
|
|||||||
cardId: 'coppe_8',
|
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 {
|
function cloneCard(card: Card): Card {
|
||||||
|
|||||||
@@ -1215,16 +1215,33 @@ function scoreCaptureAdv(
|
|||||||
table,
|
table,
|
||||||
liveSevenPressure,
|
liveSevenPressure,
|
||||||
);
|
);
|
||||||
|
const liveCardsMajorityRace = race.myCards < 21
|
||||||
|
&& race.oppCards < 21
|
||||||
|
&& Math.abs(race.myCards - race.oppCards) <= 5;
|
||||||
|
const protectingCardsLead = liveCardsMajorityRace && race.myCards > race.oppCards;
|
||||||
|
const cardsMajorityDelta = scoreCardsMajorityPosition(race.myCards + allCaptured.length, race.oppCards, phase)
|
||||||
|
- scoreCardsMajorityPosition(race.myCards, race.oppCards, phase);
|
||||||
let material = 30 + captured.length * (race.behindInCards ? 16 : 10) + phase * captured.length * 6;
|
let material = 30 + captured.length * (race.behindInCards ? 16 : 10) + phase * captured.length * 6;
|
||||||
|
|
||||||
material += capturedDenariCount * (race.behindInDenari ? 20 : race.denariRaceLive ? 16 : 10);
|
material += capturedDenariCount * (race.behindInDenari ? 20 : race.denariRaceLive ? (protectingCardsLead ? 12 : 16) : protectingCardsLead ? 7 : 10);
|
||||||
material += capturedSevenCount * (race.need7s ? 14 : race.sevenRaceLive ? 11 : 7);
|
material += capturedSevenCount * (race.need7s ? 14 : race.sevenRaceLive ? 11 : 7);
|
||||||
for (const card of allCaptured) material += primieraVal(card) * 2;
|
for (const card of allCaptured) material += primieraVal(card) * 2;
|
||||||
material += Math.round((afterPairInventory - beforePairInventory) * 1.8);
|
material += Math.round((afterPairInventory - beforePairInventory) * 1.8);
|
||||||
material += directSevenPrimieraSwing;
|
material += directSevenPrimieraSwing;
|
||||||
|
material += Math.round(cardsMajorityDelta * (liveCardsMajorityRace ? 1.2 : 0.6));
|
||||||
|
if (protectingCardsLead && captured.length > 1) material += 96 + captured.length * 24;
|
||||||
|
|
||||||
if (capturesSettebello) material += 72;
|
if (capturesSettebello) material += 72;
|
||||||
if (tableHasSettebello && nextIsOpp && !capturesSettebello) material -= 84;
|
if (tableHasSettebello && nextIsOpp && !capturesSettebello) material -= 84;
|
||||||
|
if (
|
||||||
|
protectingCardsLead
|
||||||
|
&& !race.behindInDenari
|
||||||
|
&& captured.length === 1
|
||||||
|
&& captured[0].suit === 'denara'
|
||||||
|
&& !capturesSettebello
|
||||||
|
) {
|
||||||
|
material -= 84;
|
||||||
|
}
|
||||||
if (capturedDenariCount > 0 && nextIsOpp && exposedDenariCount === 0) material += liveDenariPressure ? 30 : 14;
|
if (capturedDenariCount > 0 && nextIsOpp && exposedDenariCount === 0) material += liveDenariPressure ? 30 : 14;
|
||||||
if (capturedSevenCount > 0 && nextIsOpp && exposedSevenCount === 0) material += liveSevenPressure ? 34 : 16;
|
if (capturedSevenCount > 0 && nextIsOpp && exposedSevenCount === 0) material += liveSevenPressure ? 34 : 16;
|
||||||
if (
|
if (
|
||||||
@@ -1800,6 +1817,7 @@ function scoreMoveObjectiveBias(
|
|||||||
tracker: CardTracker | undefined,
|
tracker: CardTracker | undefined,
|
||||||
): number {
|
): number {
|
||||||
const hand = state.players[playerIdx].hand;
|
const hand = state.players[playerIdx].hand;
|
||||||
|
const phase = gamePhase(state);
|
||||||
const race = getRaceState(state, playerIdx);
|
const race = getRaceState(state, playerIdx);
|
||||||
const roleContext = getDealerRoleContext(state, playerIdx);
|
const roleContext = getDealerRoleContext(state, playerIdx);
|
||||||
const rankResidue = getRankResidueSnapshot(tracker, hand, state.table);
|
const rankResidue = getRankResidueSnapshot(tracker, hand, state.table);
|
||||||
@@ -1842,6 +1860,14 @@ function scoreMoveObjectiveBias(
|
|||||||
const directSevenPrimieraSwing = move.capture.length > 0
|
const directSevenPrimieraSwing = move.capture.length > 0
|
||||||
? scoreDirectSevenPrimieraSwing(move.card, move.capture, summary.projectedTable, hand, state.table, liveSevenPressure)
|
? scoreDirectSevenPrimieraSwing(move.card, move.capture, summary.projectedTable, hand, state.table, liveSevenPressure)
|
||||||
: 0;
|
: 0;
|
||||||
|
const liveCardsMajorityRace = race.myCards < 21
|
||||||
|
&& race.oppCards < 21
|
||||||
|
&& Math.abs(race.myCards - race.oppCards) <= 5;
|
||||||
|
const protectingCardsLead = liveCardsMajorityRace && race.myCards > race.oppCards;
|
||||||
|
const cardsMajorityDelta = move.capture.length > 0
|
||||||
|
? scoreCardsMajorityPosition(race.myCards + capturedCards.length, race.oppCards, phase)
|
||||||
|
- scoreCardsMajorityPosition(race.myCards, race.oppCards, phase)
|
||||||
|
: 0;
|
||||||
const directRankCapture = move.capture.length === 1 && move.capture[0].value === move.card.value;
|
const directRankCapture = move.capture.length === 1 && move.capture[0].value === move.card.value;
|
||||||
const directSettebelloCapture = directRankCapture
|
const directSettebelloCapture = directRankCapture
|
||||||
&& move.capture[0].suit === 'denara'
|
&& move.capture[0].suit === 'denara'
|
||||||
@@ -1865,7 +1891,18 @@ function scoreMoveObjectiveBias(
|
|||||||
if (summary.capturesSettebello) bias += 460;
|
if (summary.capturesSettebello) bias += 460;
|
||||||
if (directRankCapture) bias += move.card.value === 7 ? 90 : 34;
|
if (directRankCapture) bias += move.card.value === 7 ? 90 : 34;
|
||||||
if (directRankCapture && move.capture[0].value === 7) bias += liveSevenPressure ? 140 : 70;
|
if (directRankCapture && move.capture[0].value === 7) bias += liveSevenPressure ? 140 : 70;
|
||||||
if (directRankCapture && move.capture[0].suit === 'denara') bias += liveDenariPressure ? 150 : 72;
|
if (directRankCapture && move.capture[0].suit === 'denara') {
|
||||||
|
bias += liveDenariPressure ? (protectingCardsLead ? 88 : 150) : protectingCardsLead ? 36 : 72;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
protectingCardsLead
|
||||||
|
&& !race.behindInDenari
|
||||||
|
&& move.capture.length === 1
|
||||||
|
&& move.capture[0].suit === 'denara'
|
||||||
|
&& !summary.capturesSettebello
|
||||||
|
) {
|
||||||
|
bias -= 180;
|
||||||
|
}
|
||||||
if (directSettebelloCapture) bias += 180;
|
if (directSettebelloCapture) bias += 180;
|
||||||
if (directSettebelloCapture && nextIsOpp) bias += 220;
|
if (directSettebelloCapture && nextIsOpp) bias += 220;
|
||||||
if (
|
if (
|
||||||
@@ -1884,6 +1921,10 @@ function scoreMoveObjectiveBias(
|
|||||||
bias += openingDuplicateReleaseBias;
|
bias += openingDuplicateReleaseBias;
|
||||||
bias += quietControlWindow;
|
bias += quietControlWindow;
|
||||||
bias += directSevenPrimieraSwing;
|
bias += directSevenPrimieraSwing;
|
||||||
|
bias += Math.round(cardsMajorityDelta * (liveCardsMajorityRace ? 3.6 : 1.8));
|
||||||
|
if (protectingCardsLead && move.capture.length > 1) {
|
||||||
|
bias += 220 + move.capture.length * 36;
|
||||||
|
}
|
||||||
bias += Math.round(handStructureDelta * 1.35);
|
bias += Math.round(handStructureDelta * 1.35);
|
||||||
bias += Math.round(pairInventoryDelta * (roleContext.defendingDealerAdvantage ? 6.5 : 4.5));
|
bias += Math.round(pairInventoryDelta * (roleContext.defendingDealerAdvantage ? 6.5 : 4.5));
|
||||||
bias += Math.round(scoreRoleTablePlan(summary.projectedTable, roleContext, nextIsOpp) * 0.85);
|
bias += Math.round(scoreRoleTablePlan(summary.projectedTable, roleContext, nextIsOpp) * 0.85);
|
||||||
@@ -1898,6 +1939,14 @@ function scoreMoveObjectiveBias(
|
|||||||
if (move.capture.length === 0) {
|
if (move.capture.length === 0) {
|
||||||
if (summary.highQuietRelease) bias += 72;
|
if (summary.highQuietRelease) bias += 72;
|
||||||
bias += summary.sameValueAnchorsRemaining * 44;
|
bias += summary.sameValueAnchorsRemaining * 44;
|
||||||
|
if (
|
||||||
|
nextIsOpp
|
||||||
|
&& summary.projectedTable.length >= 5
|
||||||
|
&& summary.tableSum >= 24
|
||||||
|
&& (summary.exposedDenariCount > 0 || summary.exposedSevenCount > 0)
|
||||||
|
) {
|
||||||
|
bias -= 96 + summary.exposedDenariCount * 54 + summary.exposedSevenCount * 68;
|
||||||
|
}
|
||||||
if (exactPartnerWindow) bias += 96;
|
if (exactPartnerWindow) bias += 96;
|
||||||
if (safePartnerWindow) bias += exactPartnerWindow ? 120 : 76;
|
if (safePartnerWindow) bias += exactPartnerWindow ? 120 : 76;
|
||||||
if (
|
if (
|
||||||
@@ -2497,16 +2546,22 @@ function scoreControlOverrideCandidate(
|
|||||||
&& summary.highQuietRelease
|
&& summary.highQuietRelease
|
||||||
&& summary.projectedTable.length >= 5
|
&& summary.projectedTable.length >= 5
|
||||||
&& summary.tableSum >= 24
|
&& summary.tableSum >= 24
|
||||||
&& (summary.exposedDenariCount > 0 || summary.exposedSevenCount > 0)
|
|
||||||
) {
|
) {
|
||||||
score += 260;
|
if (summary.exposedDenariCount === 0 && summary.exposedSevenCount === 0) {
|
||||||
|
score += 260;
|
||||||
|
} else {
|
||||||
|
score -= 80 + summary.exposedDenariCount * 90 + summary.exposedSevenCount * 120;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isForcingSearchMove(summary, race)) score -= 200;
|
if (!isForcingSearchMove(summary, race)) {
|
||||||
|
score -= summary.projectedTable.length <= 2 || summary.tableSum <= 12 ? 200 : 80;
|
||||||
|
}
|
||||||
if (!summary.clearsTable && isImmediateTacticalConcession(summary.projectedTable, nextIsOpp, threats)) {
|
if (!summary.clearsTable && isImmediateTacticalConcession(summary.projectedTable, nextIsOpp, threats)) {
|
||||||
score -= 180;
|
score -= 180;
|
||||||
}
|
}
|
||||||
if (nextIsOpp && summary.projectedTable.length <= 3) score -= 150;
|
if (nextIsOpp && summary.projectedTable.length <= 2) score -= 150;
|
||||||
|
else if (nextIsOpp && summary.projectedTable.length === 3 && summary.tableSum <= 12) score -= 90;
|
||||||
if (nextIsOpp) score -= summary.exposedDenariCount * 90;
|
if (nextIsOpp) score -= summary.exposedDenariCount * 90;
|
||||||
if (nextIsOpp) score -= summary.exposedSevenCount * 70;
|
if (nextIsOpp) score -= summary.exposedSevenCount * 70;
|
||||||
if (
|
if (
|
||||||
@@ -3802,6 +3857,14 @@ function scoreMajorityRace(
|
|||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scoreCardsMajorityPosition(
|
||||||
|
myCards: number,
|
||||||
|
oppCards: number,
|
||||||
|
phase: number,
|
||||||
|
): number {
|
||||||
|
return scoreMajorityRace(myCards, oppCards, 21, Math.round(24 + phase * 22), 240);
|
||||||
|
}
|
||||||
|
|
||||||
function getRoundScoringCardWeight(card: Card): number {
|
function getRoundScoringCardWeight(card: Card): number {
|
||||||
let weight = 18 + primieraVal(card) * 3;
|
let weight = 18 + primieraVal(card) * 3;
|
||||||
|
|
||||||
@@ -4043,6 +4106,7 @@ function evaluateTeamPosition(
|
|||||||
const opp = buildTeamEvaluationSnapshot(state, opponentTeam);
|
const opp = buildTeamEvaluationSnapshot(state, opponentTeam);
|
||||||
const phase = gamePhase(state);
|
const phase = gamePhase(state);
|
||||||
const matchWeight = mine.totalPoints >= 9 || opp.totalPoints >= 9 ? 360 : 260;
|
const matchWeight = mine.totalPoints >= 9 || opp.totalPoints >= 9 ? 360 : 260;
|
||||||
|
const matchPointCardsPressure = mine.totalPoints >= 9 || opp.totalPoints >= 9 ? 3.2 : 1;
|
||||||
|
|
||||||
let score = 0;
|
let score = 0;
|
||||||
|
|
||||||
@@ -4050,7 +4114,7 @@ function evaluateTeamPosition(
|
|||||||
if (mine.totalPoints >= 10 && opp.totalPoints < 10) score += 260;
|
if (mine.totalPoints >= 10 && opp.totalPoints < 10) score += 260;
|
||||||
if (opp.totalPoints >= 10 && mine.totalPoints < 10) score -= 260;
|
if (opp.totalPoints >= 10 && mine.totalPoints < 10) score -= 260;
|
||||||
|
|
||||||
score += scoreMajorityRace(mine.cards, opp.cards, 21, Math.round(18 + phase * 18), 180);
|
score += Math.round(scoreCardsMajorityPosition(mine.cards, opp.cards, phase) * matchPointCardsPressure);
|
||||||
score += scoreMajorityRace(mine.denari, opp.denari, 6, Math.round(70 + phase * 22), 220);
|
score += scoreMajorityRace(mine.denari, opp.denari, 6, Math.round(70 + phase * 22), 220);
|
||||||
|
|
||||||
if (mine.settebello) score += 420;
|
if (mine.settebello) score += 420;
|
||||||
@@ -4071,6 +4135,10 @@ function evaluateTeamPosition(
|
|||||||
score += scoreRootOpeningAnchorState(state, perspectiveTeam, rootPlayer);
|
score += scoreRootOpeningAnchorState(state, perspectiveTeam, rootPlayer);
|
||||||
score += scoreObjectiveTableExposure(state, perspectiveTeam);
|
score += scoreObjectiveTableExposure(state, perspectiveTeam);
|
||||||
score += scoreTableControlReserve(state, perspectiveTeam);
|
score += scoreTableControlReserve(state, perspectiveTeam);
|
||||||
|
if ((mine.totalPoints >= 9 || opp.totalPoints >= 9) && state.table.length <= 2) {
|
||||||
|
score += Math.max(0, mine.cards - opp.cards) * 32;
|
||||||
|
score -= Math.max(0, opp.cards - mine.cards) * 32;
|
||||||
|
}
|
||||||
if (allowHiddenHands) {
|
if (allowHiddenHands) {
|
||||||
score += scoreCurrentPlayerVisibleTempo(state, perspectiveTeam);
|
score += scoreCurrentPlayerVisibleTempo(state, perspectiveTeam);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user