Time sequential Richese auctions
Verify and publish checkpoint container / container (push) Canceled after 0s

This commit is contained in:
Quintine committed 2026-10-29 02:11:18 +11:00
1 parent e7520e8679
commit 7fc6f1cd43
15 files changed
+692 -38

No files matched your search

+50 -18
View File
@@ -15501,6 +15501,7 @@ function beginRicheseLot(
order,
tieOrder: g.order,
});
if (lot.method !== 'silent') lot.clockVersion = 1;
g.richeseAuction = lot;
if (source === 'cache' && g.earlyRicheseBidPromises?.length) {
const attached = earlyRicheseBidRule(() => attachEarlyRicheseBids(g.earlyRicheseBidPromises!, g.turn, lot.event));
@@ -17906,11 +17907,28 @@ function ordinaryAuctionClockOpportunity(g: Game) {
bid: auction.bid, bidder: auction.bidder, passed: auction.passed,
opener: auction.opener, order: auction.saphoOrder ?? g.order }) };
}
export function hasAuctionClockOpportunity(g: Game): boolean {
return ordinaryAuctionClockOpportunity(g) !== null;
/** A source is public auction progress, never the concealed Black Market card.
* A saved sequential lot without this fresh version remains untimed. */
function richeseAuctionClockOpportunity(g: Game) {
const lot = g.richeseAuction;
if (g.status !== 'playing' || g.phase !== 3 || lot?.clockVersion !== 1 ||
lot.method === 'silent' || lot.outcome || !lot.active ||
!lot.eligible.includes(lot.active) || !lot.order.includes(lot.active) ||
!g.players.some(player => player.id === lot.active)) return null;
return { player: lot.active, source: JSON.stringify({kind:'richese', turn:g.turn,
event:lot.event, method:lot.method, source:lot.source, active:lot.active,
bid:lot.bid, bidder:lot.bidder, passed:lot.passed, acted:lot.acted}) };
}
function ordinaryAuctionClockBlocked(g: Game): boolean {
return !!(g.active !== g.auction?.active || g.richeseAuction || g.truthtrance || g.response ||
function currentAuctionClockOpportunity(g: Game) {
return richeseAuctionClockOpportunity(g) ?? ordinaryAuctionClockOpportunity(g);
}
export function hasAuctionClockOpportunity(g: Game): boolean {
return currentAuctionClockOpportunity(g) !== null;
}
function auctionClockBlocked(g: Game): boolean {
const richese = richeseAuctionClockOpportunity(g);
return !!(g.active !== (richese ? g.richeseAuction?.active : g.auction?.active) ||
!richese && g.richeseAuction || g.truthtrance || g.response ||
g.decision || g.phaseOpening || g.pendingTreacheryDiscard ||
g.pendingNullentropy || g.pendingKull || g.pendingHomeworldTupileCleanup ||
g.pendingRicheseBetrayal || g.pendingGuildBetrayal ||
@@ -17921,10 +17939,10 @@ function ordinaryAuctionClockBlocked(g: Game): boolean {
pendingNexusTraitors(g));
}
function auctionClockQuote(g: Game, now: number, controlEpoch: string, blocked = false) {
const opportunity = ordinaryAuctionClockOpportunity(g);
const opportunity = currentAuctionClockOpportunity(g);
try {
return quoteAuctionClock({ clock: g.auctionClock, opportunity, now, controlEpoch,
blocked: blocked || ordinaryAuctionClockBlocked(g),
blocked: blocked || auctionClockBlocked(g),
event: opportunity && (!g.auctionClock || g.auctionClock.source !== opportunity.source ||
g.auctionClock.player !== opportunity.player || g.auctionClock.controlEpoch !== controlEpoch)
? crypto.randomUUID() : g.auctionClock?.event ?? 'no-auction' });
@@ -17980,19 +17998,33 @@ export function advanceAuctionClock(source: Game, now: number, controlEpoch: str
if (!quote.expired || !game.auctionClock) return game;
const actor = game.auctionClock.player;
let next: Game, compelled = false;
try { next = applyAction(game, actor, { type: 'passBid' }); }
const lot = richeseAuctionClockOpportunity(game) && game.richeseAuction;
try { next = applyAction(game, actor, lot
? {type:'richeseBid',event:lot.event,amount:null,allyPayment:0}
: { type: 'passBid' }); }
catch (error) {
if (!(error instanceof RuleError)) throw error;
const whole = liveTurnActionPromises(game.turnActionPromises ?? [], actor, game.turn).length
? turnActionCompletion(game, actor)?.bid : undefined;
const opportunity = bidOpportunity(game);
const required = opportunity && bidRequired(game.bidPromises ?? [], actor, opportunity);
const phaseGuide = projectedPhaseBidGuide(game, actor)?.action;
if (!whole && !required && phaseGuide?.type !== 'bid') throw error;
const completion = phaseGuide?.type === 'bid' ? phaseGuide : minimumCompelledAuctionBid(game, actor);
requireRule(completion, 'The expired ordinary bid needs a legal completion of its binding Truthtrance answer.');
next = applyAction(game, actor, completion);
compelled = true;
if (lot) {
const context = {turn:game.turn,event:lot.event,player:actor};
const required = richeseBidRequired(game.richeseBidPromises ?? [], context);
if (!required) throw error;
const completion = projectedRicheseBidGuide(game, actor)?.action;
requireRule(completion?.type === 'richeseBid',
'The expired Richese bid needs a legal completion of its binding Truthtrance answer.');
next = applyAction(game, actor, completion);
compelled = true;
} else {
const whole = liveTurnActionPromises(game.turnActionPromises ?? [], actor, game.turn).length
? turnActionCompletion(game, actor)?.bid : undefined;
const opportunity = bidOpportunity(game);
const required = opportunity && bidRequired(game.bidPromises ?? [], actor, opportunity);
const phaseGuide = projectedPhaseBidGuide(game, actor)?.action;
if (!whole && !required && phaseGuide?.type !== 'bid') throw error;
const completion = phaseGuide?.type === 'bid' ? phaseGuide : minimumCompelledAuctionBid(game, actor);
requireRule(completion, 'The expired ordinary bid needs a legal completion of its binding Truthtrance answer.');
next = applyAction(game, actor, completion);
compelled = true;
}
}
log(next, compelled
? `${getPlayer(game, actor).name}'s ten-second bidding time expired. The legal bid required by their binding Truthtrance answer was submitted.`
@@ -18000,7 +18032,7 @@ export function advanceAuctionClock(source: Game, now: number, controlEpoch: str
return synchronizeAuctionClock(next, now, controlEpoch, blocked);
}
function projectedAuctionClock(g: Game, now?: number): AuctionClockView | null {
const opportunity = ordinaryAuctionClockOpportunity(g);
const opportunity = currentAuctionClockOpportunity(g);
if (!opportunity) return null;
const clock = g.auctionClock;
if (!clock || clock.source !== opportunity.source || clock.player !== opportunity.player)