Skip to main content
Glama

Texas Holdem MCP Server

by freshlife001
HandEvaluator.ts12.3 kB
import { Card, Rank, Suit } from '../models/Card'; export enum HandRank { HIGH_CARD = 0, PAIR = 1, TWO_PAIR = 2, THREE_OF_A_KIND = 3, STRAIGHT = 4, FLUSH = 5, FULL_HOUSE = 6, FOUR_OF_A_KIND = 7, STRAIGHT_FLUSH = 8, ROYAL_FLUSH = 9 } export interface HandResult { rank: HandRank; cards: Card[]; description: string; } export class HandEvaluator { private static readonly RANK_VALUES: Record<Rank, number> = { [Rank.TWO]: 2, [Rank.THREE]: 3, [Rank.FOUR]: 4, [Rank.FIVE]: 5, [Rank.SIX]: 6, [Rank.SEVEN]: 7, [Rank.EIGHT]: 8, [Rank.NINE]: 9, [Rank.TEN]: 10, [Rank.JACK]: 11, [Rank.QUEEN]: 12, [Rank.KING]: 13, [Rank.ACE]: 14 }; static evaluateHand(playerCards: Card[], communityCards: Card[]): HandResult { const allCards = [...playerCards, ...communityCards]; // Check for royal flush const royalFlush = this.checkRoyalFlush(allCards); if (royalFlush) return royalFlush; // Check for straight flush const straightFlush = this.checkStraightFlush(allCards); if (straightFlush) return straightFlush; // Check for four of a kind const fourOfAKind = this.checkFourOfAKind(allCards); if (fourOfAKind) return fourOfAKind; // Check for full house const fullHouse = this.checkFullHouse(allCards); if (fullHouse) return fullHouse; // Check for flush const flush = this.checkFlush(allCards); if (flush) return flush; // Check for straight const straight = this.checkStraight(allCards); if (straight) return straight; // Check for three of a kind const threeOfAKind = this.checkThreeOfAKind(allCards); if (threeOfAKind) return threeOfAKind; // Check for two pair const twoPair = this.checkTwoPair(allCards); if (twoPair) return twoPair; // Check for pair const pair = this.checkPair(allCards); if (pair) return pair; // High card return this.getHighCard(allCards); } private static checkRoyalFlush(cards: Card[]): HandResult | null { // Group cards by suit const suitGroups = this.groupBySuit(cards); for (const suit in suitGroups) { const suitCards = suitGroups[suit]; if (suitCards.length >= 5) { const royalCards = suitCards.filter(card => card.rank === Rank.TEN || card.rank === Rank.JACK || card.rank === Rank.QUEEN || card.rank === Rank.KING || card.rank === Rank.ACE ); if (royalCards.length === 5) { return { rank: HandRank.ROYAL_FLUSH, cards: royalCards, description: `Royal Flush of ${suit}` }; } } } return null; } private static checkStraightFlush(cards: Card[]): HandResult | null { // Group cards by suit const suitGroups = this.groupBySuit(cards); for (const suit in suitGroups) { const suitCards = suitGroups[suit]; if (suitCards.length >= 5) { const sortedCards = [...suitCards].sort((a, b) => this.RANK_VALUES[b.rank] - this.RANK_VALUES[a.rank] ); const straight = this.findStraight(sortedCards); if (straight) { return { rank: HandRank.STRAIGHT_FLUSH, cards: straight, description: `Straight Flush, ${straight[0].rank} high` }; } } } return null; } private static checkFourOfAKind(cards: Card[]): HandResult | null { // Group cards by rank const rankGroups = this.groupByRank(cards); for (const rank in rankGroups) { const rankCards = rankGroups[rank]; if (rankCards.length === 4) { // Find the highest kicker const kickers = cards.filter(card => card.rank !== rank) .sort((a, b) => this.RANK_VALUES[b.rank] - this.RANK_VALUES[a.rank]); return { rank: HandRank.FOUR_OF_A_KIND, cards: [...rankCards, kickers[0]], description: `Four of a Kind, ${rank}s` }; } } return null; } private static checkFullHouse(cards: Card[]): HandResult | null { // Group cards by rank const rankGroups = this.groupByRank(cards); let threeOfAKindRank: string | null = null; let pairRank: string | null = null; // Find the highest three of a kind for (const rank in rankGroups) { if (rankGroups[rank].length >= 3) { if (!threeOfAKindRank || this.RANK_VALUES[rank as Rank] > this.RANK_VALUES[threeOfAKindRank as Rank]) { threeOfAKindRank = rank; } } } if (!threeOfAKindRank) return null; // Find the highest pair that's not the same rank as the three of a kind for (const rank in rankGroups) { if (rank !== threeOfAKindRank && rankGroups[rank].length >= 2) { if (!pairRank || this.RANK_VALUES[rank as Rank] > this.RANK_VALUES[pairRank as Rank]) { pairRank = rank; } } } if (!pairRank) return null; return { rank: HandRank.FULL_HOUSE, cards: [ ...rankGroups[threeOfAKindRank].slice(0, 3), ...rankGroups[pairRank].slice(0, 2) ], description: `Full House, ${threeOfAKindRank}s full of ${pairRank}s` }; } private static checkFlush(cards: Card[]): HandResult | null { // Group cards by suit const suitGroups = this.groupBySuit(cards); for (const suit in suitGroups) { const suitCards = suitGroups[suit]; if (suitCards.length >= 5) { const sortedCards = [...suitCards].sort((a, b) => this.RANK_VALUES[b.rank] - this.RANK_VALUES[a.rank] ); return { rank: HandRank.FLUSH, cards: sortedCards.slice(0, 5), description: `Flush, ${sortedCards[0].rank} high` }; } } return null; } private static checkStraight(cards: Card[]): HandResult | null { const uniqueRankCards = this.getUniqueRankCards(cards); const sortedCards = [...uniqueRankCards].sort((a, b) => this.RANK_VALUES[b.rank] - this.RANK_VALUES[a.rank] ); const straight = this.findStraight(sortedCards); if (straight) { return { rank: HandRank.STRAIGHT, cards: straight, description: `Straight, ${straight[0].rank} high` }; } return null; } private static checkThreeOfAKind(cards: Card[]): HandResult | null { // Group cards by rank const rankGroups = this.groupByRank(cards); for (const rank in rankGroups) { const rankCards = rankGroups[rank]; if (rankCards.length === 3) { // Find the two highest kickers const kickers = cards.filter(card => card.rank !== rank) .sort((a, b) => this.RANK_VALUES[b.rank] - this.RANK_VALUES[a.rank]); return { rank: HandRank.THREE_OF_A_KIND, cards: [...rankCards, kickers[0], kickers[1]], description: `Three of a Kind, ${rank}s` }; } } return null; } private static checkTwoPair(cards: Card[]): HandResult | null { // Group cards by rank const rankGroups = this.groupByRank(cards); const pairs: Card[][] = []; for (const rank in rankGroups) { const rankCards = rankGroups[rank]; if (rankCards.length >= 2) { pairs.push(rankCards.slice(0, 2)); } } if (pairs.length >= 2) { // Sort pairs by rank pairs.sort((a, b) => this.RANK_VALUES[b[0].rank] - this.RANK_VALUES[a[0].rank] ); // Take the two highest pairs const topTwoPairs = pairs.slice(0, 2).flat(); // Find the highest kicker const kickers = cards.filter(card => card.rank !== topTwoPairs[0].rank && card.rank !== topTwoPairs[2].rank ).sort((a, b) => this.RANK_VALUES[b.rank] - this.RANK_VALUES[a.rank]); return { rank: HandRank.TWO_PAIR, cards: [...topTwoPairs, kickers[0]], description: `Two Pair, ${topTwoPairs[0].rank}s and ${topTwoPairs[2].rank}s` }; } return null; } private static checkPair(cards: Card[]): HandResult | null { // Group cards by rank const rankGroups = this.groupByRank(cards); for (const rank in rankGroups) { const rankCards = rankGroups[rank]; if (rankCards.length === 2) { // Find the three highest kickers const kickers = cards.filter(card => card.rank !== rank) .sort((a, b) => this.RANK_VALUES[b.rank] - this.RANK_VALUES[a.rank]); return { rank: HandRank.PAIR, cards: [...rankCards, kickers[0], kickers[1], kickers[2]], description: `Pair of ${rank}s` }; } } return null; } private static getHighCard(cards: Card[]): HandResult { const sortedCards = [...cards].sort((a, b) => this.RANK_VALUES[b.rank] - this.RANK_VALUES[a.rank] ); return { rank: HandRank.HIGH_CARD, cards: sortedCards.slice(0, 5), description: `High Card, ${sortedCards[0].rank}` }; } private static groupBySuit(cards: Card[]): Record<string, Card[]> { const groups: Record<string, Card[]> = {}; for (const card of cards) { if (!groups[card.suit]) { groups[card.suit] = []; } groups[card.suit].push(card); } return groups; } private static groupByRank(cards: Card[]): Record<string, Card[]> { const groups: Record<string, Card[]> = {}; for (const card of cards) { if (!groups[card.rank]) { groups[card.rank] = []; } groups[card.rank].push(card); } return groups; } private static findStraight(sortedCards: Card[]): Card[] | null { if (sortedCards.length < 5) return null; // Check for A-5-4-3-2 straight if (sortedCards[0].rank === Rank.ACE) { const lowAceStraight = sortedCards.filter(card => card.rank === Rank.ACE || card.rank === Rank.TWO || card.rank === Rank.THREE || card.rank === Rank.FOUR || card.rank === Rank.FIVE ); if (lowAceStraight.length >= 5) { // Reorder for A-5-4-3-2 (with Ace at the end) return [ lowAceStraight.find(card => card.rank === Rank.FIVE)!, lowAceStraight.find(card => card.rank === Rank.FOUR)!, lowAceStraight.find(card => card.rank === Rank.THREE)!, lowAceStraight.find(card => card.rank === Rank.TWO)!, lowAceStraight.find(card => card.rank === Rank.ACE)! ]; } } // Check for regular straight const straight: Card[] = [sortedCards[0]]; for (let i = 1; i < sortedCards.length; i++) { const prevRankValue = this.RANK_VALUES[sortedCards[i-1].rank]; const currRankValue = this.RANK_VALUES[sortedCards[i].rank]; if (prevRankValue - currRankValue === 1) { straight.push(sortedCards[i]); if (straight.length === 5) { return straight; } } else if (prevRankValue !== currRankValue) { // Reset if not consecutive and not the same rank straight.length = 1; straight[0] = sortedCards[i]; } } return null; } private static getUniqueRankCards(cards: Card[]): Card[] { const uniqueRanks = new Set<string>(); const uniqueCards: Card[] = []; for (const card of cards) { if (!uniqueRanks.has(card.rank)) { uniqueRanks.add(card.rank); uniqueCards.push(card); } } return uniqueCards; } static compareHands(hand1: HandResult, hand2: HandResult): number { // Compare hand ranks first if (hand1.rank !== hand2.rank) { return hand1.rank - hand2.rank; } // If ranks are the same, compare cards in order for (let i = 0; i < Math.min(hand1.cards.length, hand2.cards.length); i++) { const card1Value = this.RANK_VALUES[hand1.cards[i].rank]; const card2Value = this.RANK_VALUES[hand2.cards[i].rank]; if (card1Value !== card2Value) { return card1Value - card2Value; } } // Hands are identical return 0; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/freshlife001/mcp_poker'

If you have feedback or need assistance with the MCP directory API, please join our Discord server