UNPKG

belote

Version:

A TypeScript library for playing Belote card game with bot support.

89 lines (88 loc) 4.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BotAI = void 0; const types_1 = require("./types"); const utils_1 = require("./utils"); class BotAI { static decideBid(player) { const colorCounts = { hearts: 0, diamonds: 0, clubs: 0, spades: 0 }; const colorValues = { hearts: 0, diamonds: 0, clubs: 0, spades: 0 }; for (const card of player.cards) { colorCounts[card.color]++; colorValues[card.color] += (0, utils_1.getCardValue)(card, card.color); } let bestColor = null; let bestScore = 0; for (const color of types_1.cardColors) { const score = colorCounts[color] * 2 + colorValues[color] * 0.1; if (score > bestScore) { bestScore = score; bestColor = color; } } const minCardsForBid = 3; const minScoreForBid = 6; if (bestColor && colorCounts[bestColor] >= minCardsForBid && bestScore >= minScoreForBid) { return bestColor; } if (player.isDealer) { const rng = (0, utils_1.mulberry32)(Date.now()); return bestColor || types_1.cardColors[Math.floor(rng() * types_1.cardColors.length)]; } return 'pass'; } static decideCalling(player, adut) { const allPossibleCalls = this.findAllPossibleCalls(player.cards, adut); if (allPossibleCalls.length === 0) return []; allPossibleCalls.sort((a, b) => b.calling.type - a.calling.type); return allPossibleCalls[0].cards; } static decideCardToPlay(player, currentTrick, adut) { const allPlayerCards = [...player.cards, ...player.talon]; const legalCards = allPlayerCards.filter((card) => (0, utils_1.canPlayCard)(card, currentTrick, adut, allPlayerCards)); if (legalCards.length === 0) throw new Error('No legal cards to play?'); else if (legalCards.length === 1) return legalCards[0]; // Leading - if no cards played, choose best card. if (currentTrick.length === 0) return this.chooseBestLeadingCard(legalCards, adut); // Following - try to win or play strategically. else return this.chooseBestFollowingCard(legalCards, currentTrick, adut); } static findAllPossibleCalls(cards, adut) { const results = []; for (let i = 1; i < (1 << cards.length); i++) { const subset = []; for (let j = 0; j < cards.length; j++) { if (i & (1 << j)) { subset.push(cards[j]); } } const calling = (0, utils_1.validateCalling)(subset, adut); if (calling) results.push({ cards: subset, calling }); } return results; } static chooseBestLeadingCard(cards, adut) { // Prefer trump cards, then high-value cards. const trumpCards = cards.filter((c) => c.color === adut); if (trumpCards.length > 0) { return trumpCards.reduce((best, current) => (0, utils_1.getCardValue)(current, adut) > (0, utils_1.getCardValue)(best, adut) ? current : best); } // Otherwise play highest value card. return cards.reduce((best, current) => (0, utils_1.getCardValue)(current, adut) > (0, utils_1.getCardValue)(best, adut) ? current : best); } static chooseBestFollowingCard(cards, currentTrick, adut) { const winningCards = cards.filter((card) => { return currentTrick.every((playedCard) => (0, utils_1.canBeatCard)(card, playedCard, adut)); }); if (winningCards.length > 0) return winningCards.reduce((best, current) => (0, utils_1.getCardValue)(current, adut) < (0, utils_1.getCardValue)(best, adut) ? current : best); return cards.reduce((best, current) => (0, utils_1.getCardValue)(current, adut) < (0, utils_1.getCardValue)(best, adut) ? current : best); } } exports.BotAI = BotAI;