Preserve Fremen revival grant opportunity for bot allies
Verify and publish checkpoint container / container (push) Successful in 10m54s

This commit is contained in:
Quintine committed 2026-10-29 07:18:48 +11:00
1 parent 825f903cce
commit ac7db3cb35
7 files changed
+201 -2

No files matched your search

+20
View File
@@ -2,6 +2,7 @@ import { liveMovementExpressionPromises } from './movement-expression-promises';
import { isAlliedShipmentVisit } from './allied-separation';
import { discoveryBotActions } from './discovery-options';
import { normalAuctionHandBlock } from './normal-auction';
import { fremenRevivalGrantBlock } from './fremen-revival-grant';
import { atreidesFullPlanAction } from './atreides-full-plan';
import { liveMovementPromises } from './movement-promises';
import { ecazOccupyDialChoices, ecazOccupyOwnProfile, ecazOccupyPolicyActions } from './ecaz-occupy-options';
@@ -60,6 +61,8 @@ import { strongholdBattleEffects } from './stronghold-battle';
import {
eliteRevivalRemaining,
forceRevivalPrice,
freeRevivalRemaining,
revivalPrevented,
paidForceRevivalCost,
} from './revival';
import { controlsLeader, nativeAvailable } from './leader-control';
@@ -4661,6 +4664,21 @@ export function botActions(g: GameView): Action[] {
: [{ type: 'endMovement' }];
}
/** In concurrent Basic Revival, let a human Fremen decide its existing grant
* before its automated ally spends or finishes. Ready is the normal way to
* finish that decision; responses and unrelated actions remain available. */
function waitsForHumanFremenGrant(g: Game, player: Game['players'][number]): boolean {
if (g.status !== 'playing' || g.advanced || g.phase !== 4 || g.revivalOrder ||
g.coalitionState || player.tanks <= 0 || revivalPrevented(g, player.id)) return false;
const fremen = g.players.find(seat => seat.faction === 'fremen');
if (!fremen || fremen.bot || fremen.autopilot || fremen.ally !== player.id ||
player.ally !== fremen.id || g.ready.includes(fremen.id) ||
fremenRevivalGrantBlock(g, fremen.id) !== null) return false;
const current = freeRevivalRemaining(g, player);
const granted = freeRevivalRemaining({ ...g, freeRevival: [...g.freeRevival, player.id] }, player);
return Math.min(player.tanks, granted) > Math.min(player.tanks, current);
}
export function runBots(state: Game, limit = 96): Game {
const supplier = state.players.find((p) => p.faction === 'richese');
const unopenedAllyWindow =
@@ -4700,6 +4718,8 @@ export function runBots(state: Game, limit = 96): Game {
actors.sort((a, b) => Number(b.faction === 'guild') - Number(a.faction === 'guild'));
for (const p of actors) {
for (const action of botActions(viewGame(g, p.id))) {
if ((action.type === 'revive' || action.type === 'ready') && waitsForHumanFremenGrant(g, p))
continue;
try {
next = applyAction(g, p.id, action);
break;
+4 -2
View File
@@ -6553,7 +6553,7 @@ export const RULE_TOPICS: RuleTopic[] = [
"During Revival, Fremen may offer the current ally its ordinary free-force rate of three. The grant replaces the native ordinary rate; current bonuses, total limits and separate special-force limits still apply. It never moves counters by itself.",
"The original owner, ally and turn remain bound while another player may prevent the grant with Karama. All-pass commits once. Under the provisional prevention policy, cancellation denies this grant for the current Revival, preserving the ally’s intrinsic free rate, Recruits and completed returns/payments. The next native Revival clears the denial.",
"Recruits doubles the current granted rate including applicable bonuses before caps. With no other bonus, three becomes six. Provisionally accept grants before or after the card with the same calculation: deduct recorded free usage, preserve all earlier total returns and paid classifications, and give no refund. Finish a pending transaction before declaring a grant.",
"The Fremen panel keeps status and unavailable reasons visible. Your ally’s normal revival panel supplies the actual remaining amount. Complete modes, broader combinations and final acceptance remain unfinished.",
"The Fremen panel keeps status and unavailable reasons visible. In concurrent Basic Revival an automated ally can wait for a useful grant until the Fremen grants it or finishes the phase; the existing Karama response still applies. Your ally’s normal revival panel supplies the actual remaining amount. Complete modes, broader combinations and final acceptance remain unfinished.",
"In the Basic group preview, Fremen can provisionally grant each ally the rate separately; each named declaration has its own Karama response. Group interactions remain incomplete."
],
"related": [
@@ -6588,9 +6588,10 @@ export const RULE_TOPICS: RuleTopic[] = [
{
"area": "AI",
"status": "Partial",
"detail": "Existing-profile native/Recruits paths and staged group grants use the same projected legal permissions. Strategy and strength calibration remain deferred.",
"detail": "Existing-profile native/Recruits paths and staged group grants use the same projected legal permissions. In concurrent Basic Revival a mutual automated ally waits for a useful human Fremen grant until it is resolved or Fremen is Ready. Strategy and strength calibration remain deferred.",
"evidence": [
"game/bots.ts",
"tests/fremen-revival-grant-bot-priority.test.ts",
"tests/recruits-controls.test.tsx",
"docs/FREMEN_REVIVAL_GRANT.md"
]
@@ -6615,6 +6616,7 @@ export const RULE_TOPICS: RuleTopic[] = [
"tests/fremen-revival-grant-engine.test.ts",
"tests/fremen-revival-grant-quote.test.ts",
"tests/fremen-revival-grant-controls.test.tsx",
"tests/fremen-revival-grant-bot-priority.test.ts",
"tests/recruits-engine.test.ts",
"tests/recruits-controls.test.tsx"
]