// The starter bot: plays the move the upstream AI recommends (obs.recommended, the MCP's recommendedAction), fills in // what that move leaves open (targets, cards to discard), and passes when it cannot. Change anything; keep the shape: // act(obs, legal) returns an index into legal, or a legal move's message with its params filled in. const CAMP = { 主公: 'lord', 忠臣: 'lord', 反贼: 'rebel', 内奸: 'renegade' }; const KEEP = { 桃: 5, 闪: 4, 无懈可击: 3, 酒: 2, 杀: 1 }; let turn = ''; let made = new Map(); // moves made this turn: an ask nobody answers (激将) must not be repeated forever function fill(obs, move) { const m = structuredClone(move.message); const me = obs.players[obs.viewer]; if (move.validTargets.length && !Array.isArray(m.params.targets)) { const ally = (i) => me.identity !== '内奸' && CAMP[obs.players[i].identity] === CAMP[me.identity]; const lord = obs.players.findIndex((p) => p.identity === '主公'); const foes = move.validTargets.filter((i) => !ally(i)).sort((a, b) => (me.identity === '反贼' ? (b === lord) - (a === lord) : 0)); if (!foes.length && move.category === 'play') return null; // only friends in reach: hold the card const targets = foes.length ? foes : move.validTargets; m.params.targets = targets.slice(0, Math.max(move.minTarget ?? 1, 1)); m.params.target = m.params.targets[0]; } if (Array.isArray(m.params.cardIds) && !m.params.cardIds.length) { const need = obs.pending?.discard?.min ?? 1; const hand = [...(me.hand ?? [])].sort((a, b) => (KEEP[a.name] ?? 0) - (KEEP[b.name] ?? 0)); if (hand.length < need) return null; m.params.cardIds = hand.slice(0, need).map((c) => c.id); } if (Array.isArray(m.params.allocation) && !m.params.allocation.length) return null; return m; } export default function act(obs, legal) { const now = `${obs.game}:${obs.turn.round}:${obs.currentPlayerIndex}`; if (now !== turn) (turn = now), (made = new Map()); const refused = new Set((obs.refused ?? []).map((m) => JSON.stringify(m))); const order = [obs.recommended, ...legal.keys()].filter((i) => i !== null && i !== undefined); for (const i of order) { const m = fill(obs, legal[i]); const key = JSON.stringify(m); if (m && !refused.has(key) && (made.get(key) ?? 0) < 2) { made.set(key, (made.get(key) ?? 0) + 1); return m; } } const pass = legal.findIndex((l) => l.category === 'skip' || l.message.actionType === 'end'); return pass >= 0 ? pass : 0; }