1
0

AI will play VP to win game

This commit is contained in:
James Ketr 2025-10-13 14:17:36 -07:00
parent 260139b7a3
commit dd8b930d0b

View File

@ -170,34 +170,60 @@ const tryProgress = (_received?: any): any => {
if (!game.private || !game.private.development) {
return undefined;
}
let vps = 0;
let firstPlayableProgress: any = undefined;
// Count VP cards and remember the first non-VP progress card we can play
for (let i = 0; i < game.private.development.length; i++) {
const card = game.private.development[i];
if (card.turn >= game.turns || card.played || card.type === 'vp') {
// card.turn >= game.turns => newly drawn this turn and can't be played
if (card.turn >= game.turns || card.played) {
if (card.type === 'vp') vps++;
continue;
}
if (card.type === 'vp') {
vps++;
continue;
}
// remember the first playable non-VP card
if (!firstPlayableProgress) firstPlayableProgress = card;
}
console.log(`${name} - Playing -`, card);
send({
type: 'play-card',
card
});
// If we have a non-VP progress card to play, play it (existing behavior)
if (firstPlayableProgress) {
console.log(`${name} - Playing -`, firstPlayableProgress);
send({ type: 'play-card', card: firstPlayableProgress });
return { turn: { actions: anyValue } };
}
return {
turn: {
actions: anyValue
// If we have VP cards, consider playing them to win. Determine victory threshold.
if (vps > 0) {
const currentPoints = (game.players && game.players[game.color] && game.players[game.color].points) || 0;
let victoryThreshold = 10;
try {
if (game.rules && game.rules['victory-points'] && ('points' in game.rules['victory-points'])) {
victoryThreshold = Number(game.rules['victory-points'].points) || victoryThreshold;
}
} catch (e) {
// ignore and use default
}
// If playing our VP cards would reach or exceed the threshold, play one now
if (currentPoints + vps >= victoryThreshold) {
// find first unplayed VP card object
for (let i = 0; i < game.private.development.length; i++) {
const card = game.private.development[i];
if (card.type === 'vp' && !(card.played) && !(card.turn >= game.turns)) {
console.log(`${name} - Playing VP to attempt win -`, card);
send({ type: 'play-card', card });
return { turn: { actions: anyValue } };
}
}
}
// Otherwise just announce how many VP cards we have
send({ type: 'chat', message: `I have ${vps} VP cards!` });
}
if (vps) {
send({
type: 'chat',
message: `I have ${vps} VP cards!`
});
}
return undefined;
};
@ -365,7 +391,7 @@ const processLobby = (received: any): any => {
/* AI selected a Player. Wait for game-order.
* Only treat the bot as already selected if its color is not the 'unassigned' sentinel.
* Some servers may return an empty 'unselected' array even while the player's color
* Server may return an empty 'unselected' array even while the player's color
* remains 'unassigned', so guard that case explicitly. */
if (received.unselected.indexOf(name) === -1 && game.color !== 'unassigned') {
send({
@ -566,25 +592,6 @@ const processDiscard = async (_received?: any): Promise<any> => {
let mustDiscard = game.players[game.color].mustDiscard;
// Some servers may not explicitly set `mustDiscard` in the players object;
// derive it from the player's resource count when possible. According to
// rules: if a player has >7 resources they must discard floor(resources/2).
if (!mustDiscard) {
const playerResources = Number(game.players[game.color].resources || 0);
const privateTotal = types.reduce((s, t) => s + (Number(game.private && game.private[t]) || 0), 0);
const totalResources = playerResources || privateTotal;
if (totalResources > 7) {
mustDiscard = Math.floor(totalResources / 2);
console.log(`${name} - computed mustDiscard=${mustDiscard} from totalResources=${totalResources} (playerResources=${playerResources}, privateTotal=${privateTotal})`);
// Store it so subsequent logic can observe it if desired
try {
game.players[game.color].mustDiscard = mustDiscard;
} catch (e) {
// ignore if assignment fails on read-only snapshot
}
}
}
if (!mustDiscard) {
return;
}
@ -822,8 +829,7 @@ const processNormal = async (received?: any): Promise<any> => {
}
// If the robber is active on our turn, ensure we have player information
// only when a discard might be required. Some servers omit `mustDiscard`;
// in that case request `players` only if the known resource total > 7.
// only when a discard might be required.
if (game.turn && game.turn.robberInAction) {
if (!game.players) {
console.log(`${name} - robber in action and players missing; requesting players`);
@ -836,13 +842,8 @@ const processNormal = async (received?: any): Promise<any> => {
const totalResources = (typeof playerResources !== 'undefined') ? playerResources : privateTotal;
if (typeof game.players[game.color].mustDiscard === 'undefined') {
if (totalResources > 7) {
console.log(`${name} - robber in action but mustDiscard unknown and totalResources=${totalResources} > 7; requesting players`);
return { players: anyValue };
} else {
// No discard required (totalResources <= 7); proceed with turn actions.
console.log(`${name} - robber in action but no discard required (totalResources=${totalResources}); proceeding`);
}
// No discard required (totalResources <= 7); proceed with turn actions.
console.log(`${name} - robber in action but no discard required (totalResources=${totalResources}); proceeding`);
}
}