Time sequential Richese auctions
Verify and publish checkpoint container / container (push) Canceled after 0s
Verify and publish checkpoint container / container (push) Canceled after 0s
This commit is contained in:
1 parent
e7520e8679
commit
7fc6f1cd43
15 files changed
+692
-38
No files matched your search
+50
-18
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user