AI will play VP to win game
This commit is contained in:
parent
260139b7a3
commit
dd8b930d0b
@ -170,34 +170,60 @@ const tryProgress = (_received?: any): any => {
|
|||||||
if (!game.private || !game.private.development) {
|
if (!game.private || !game.private.development) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let vps = 0;
|
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++) {
|
for (let i = 0; i < game.private.development.length; i++) {
|
||||||
const card = game.private.development[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++;
|
if (card.type === 'vp') vps++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (card.type === 'vp') {
|
||||||
|
vps++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// remember the first playable non-VP card
|
||||||
|
if (!firstPlayableProgress) firstPlayableProgress = card;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`${name} - Playing -`, card);
|
// If we have a non-VP progress card to play, play it (existing behavior)
|
||||||
send({
|
if (firstPlayableProgress) {
|
||||||
type: 'play-card',
|
console.log(`${name} - Playing -`, firstPlayableProgress);
|
||||||
card
|
send({ type: 'play-card', card: firstPlayableProgress });
|
||||||
});
|
return { turn: { actions: anyValue } };
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
// If we have VP cards, consider playing them to win. Determine victory threshold.
|
||||||
turn: {
|
if (vps > 0) {
|
||||||
actions: anyValue
|
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;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -365,7 +391,7 @@ const processLobby = (received: any): any => {
|
|||||||
|
|
||||||
/* AI selected a Player. Wait for game-order.
|
/* AI selected a Player. Wait for game-order.
|
||||||
* Only treat the bot as already selected if its color is not the 'unassigned' sentinel.
|
* 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. */
|
* remains 'unassigned', so guard that case explicitly. */
|
||||||
if (received.unselected.indexOf(name) === -1 && game.color !== 'unassigned') {
|
if (received.unselected.indexOf(name) === -1 && game.color !== 'unassigned') {
|
||||||
send({
|
send({
|
||||||
@ -566,25 +592,6 @@ const processDiscard = async (_received?: any): Promise<any> => {
|
|||||||
|
|
||||||
let mustDiscard = game.players[game.color].mustDiscard;
|
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) {
|
if (!mustDiscard) {
|
||||||
return;
|
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
|
// If the robber is active on our turn, ensure we have player information
|
||||||
// only when a discard might be required. Some servers omit `mustDiscard`;
|
// only when a discard might be required.
|
||||||
// in that case request `players` only if the known resource total > 7.
|
|
||||||
if (game.turn && game.turn.robberInAction) {
|
if (game.turn && game.turn.robberInAction) {
|
||||||
if (!game.players) {
|
if (!game.players) {
|
||||||
console.log(`${name} - robber in action and players missing; requesting 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;
|
const totalResources = (typeof playerResources !== 'undefined') ? playerResources : privateTotal;
|
||||||
|
|
||||||
if (typeof game.players[game.color].mustDiscard === 'undefined') {
|
if (typeof game.players[game.color].mustDiscard === 'undefined') {
|
||||||
if (totalResources > 7) {
|
// No discard required (totalResources <= 7); proceed with turn actions.
|
||||||
console.log(`${name} - robber in action but mustDiscard unknown and totalResources=${totalResources} > 7; requesting players`);
|
console.log(`${name} - robber in action but no discard required (totalResources=${totalResources}); proceeding`);
|
||||||
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`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user