public static ulong FindCards(HandEvaluation eval) { int numberOfCards = Bits.BitCount(eval.Cards); int remove = numberOfCards - 5; if (remove <= 0) { return(eval.Cards); } uint sc = (uint)((eval.Cards >> (0)) & 0x1fffUL); uint sd = (uint)((eval.Cards >> (13)) & 0x1fffUL); uint sh = (uint)((eval.Cards >> (26)) & 0x1fffUL); uint ss = (uint)((eval.Cards >> (39)) & 0x1fffUL); uint ranks = sc | sd | sh | ss; uint n_ranks = Bits.nBitsTable[ranks]; // Flush, Straight Flush, Straight if (n_ranks >= 5) { if (Bits.nBitsTable[ss] >= 5) { if (Bits.straightTable[ss] != 0) { return(0x1FUL << (Bits.straightTable[ss] - 4 + 39)); } else { return((ulong)Bits.LeftBits(ss, 5) << 39); } } if (Bits.nBitsTable[sh] >= 5) { if (Bits.straightTable[sh] != 0) { return(0x1FUL << (Bits.straightTable[sh] - 4 + 26)); } else { return((ulong)Bits.LeftBits(sh, 5) << 26); } } if (Bits.nBitsTable[sd] >= 5) { if (Bits.straightTable[sd] != 0) { return(0x1FUL << (Bits.straightTable[sd] - 4 + 13)); } else { return((ulong)Bits.LeftBits(sd, 5) << 13); } } if (Bits.nBitsTable[sc] >= 5) { if (Bits.straightTable[sc] != 0) { return(0x1FUL << (Bits.straightTable[sc] - 4 + 0)); } else { return((ulong)Bits.LeftBits(sc, 5) << 0); } } if (Bits.straightTable[ranks] != 0) { uint stBits = (uint)0x1F << (Bits.straightTable[ranks] - 4); ss &= stBits; stBits ^= ss; sh &= stBits; stBits ^= sh; sd &= stBits; stBits ^= sd; sc &= stBits; return(((ulong)sc << 0) | ((ulong)sd << 13) | ((ulong)sh << 26) | ((ulong)ss << 39)); } } uint n_dups = ((uint)(numberOfCards - n_ranks)); switch (n_dups) { case 0: // High Card uint hcBits = Bits.LeftBits(ranks, 5); ss &= hcBits; hcBits ^= ss; sh &= hcBits; hcBits ^= sh; sd &= hcBits; hcBits ^= sd; sc &= hcBits; return(((ulong)sc << 0) | ((ulong)sd << 13) | ((ulong)sh << 26) | ((ulong)ss << 39)); case 1: // One Pair uint keepBits = ranks ^ (sc ^ sd ^ sh ^ ss); ranks ^= keepBits; ranks = Bits.LeftBits(ranks, 3) | keepBits; ss &= ranks; ranks = (ranks ^ ss) | keepBits; sh &= ranks; ranks = (ranks ^ sh) | keepBits; sd &= ranks; ranks = (ranks ^ sd) | keepBits; sc &= ranks; return(((ulong)sc << 0) | ((ulong)sd << 13) | ((ulong)sh << 26) | ((ulong)ss << 39)); case 2: // Two Pair or Trips keepBits = ranks ^ (sc ^ sd ^ sh ^ ss); int otherCards = 1; if (keepBits == 0) { // Or Trips keepBits = ((sc & sd) | (sh & ss)) & ((sc & sh) | (sd & ss)); otherCards = 2; } ranks ^= keepBits; ranks = Bits.LeftBits(ranks, otherCards) | keepBits; ss &= ranks; ranks = (ranks ^ ss) | keepBits; sh &= ranks; ranks = (ranks ^ sh) | keepBits; sd &= ranks; ranks = (ranks ^ sd) | keepBits; sc &= ranks; return(((ulong)sc << 0) | ((ulong)sd << 13) | ((ulong)sh << 26) | ((ulong)ss << 39)); default: /* Possible quads, fullhouse, or three pair */ keepBits = sc & sd & sh & ss; otherCards = 1; if (keepBits == 0) { keepBits = ranks ^ (sc ^ sd ^ sh ^ ss); if (Bits.nBitsTable[keepBits] == n_dups) { // three pair, so keep the best 2 pair keepBits = Bits.LeftBits(keepBits, 2); } else { // Must be trips somewhere, so fullhouse keepBits |= ((sc & sd) | (sh & ss)) & ((sc & sh) | (sd & ss)); otherCards = 0; } } ranks ^= keepBits; ranks = Bits.LeftBits(ranks, otherCards) | keepBits; ss &= ranks; ranks = (ranks ^ ss) | keepBits; sh &= ranks; ranks = (ranks ^ sh) | keepBits; sd &= ranks; ranks = (ranks ^ sd) | keepBits; sc &= ranks; return(((ulong)sc << 0) | ((ulong)sd << 13) | ((ulong)sh << 26) | ((ulong)ss << 39)); } }
public static HandEvaluation EvaluateHand(ulong cards) { int numberOfCards = Bits.BitCount(cards); uint four_mask, three_mask, two_mask; HandEvaluation ret = new HandEvaluation(cards, 0, 0); #if DEBUG // This functions supports 1-7 cards if (numberOfCards < 1 || numberOfCards > 7) { throw new ArgumentOutOfRangeException("numberOfCards"); } #endif // Seperate out by suit uint sc = (uint)((cards >> (0)) & 0x1fffUL); uint sd = (uint)((cards >> (13)) & 0x1fffUL); uint sh = (uint)((cards >> (26)) & 0x1fffUL); uint ss = (uint)((cards >> (39)) & 0x1fffUL); uint ranks = sc | sd | sh | ss; uint n_ranks = Bits.nBitsTable[ranks]; uint n_dups = ((uint)(numberOfCards - n_ranks)); /* Check for straight, flush, or straight flush, and return if we can * determine immediately that this is the best possible hand */ if (n_ranks >= 5) { if (Bits.nBitsTable[ss] >= 5) { uint st = Bits.straightTable[ss]; if (st != 0) { return(new HandEvaluation(cards, HandTypes.StraightFlush, HANDTYPE_VALUE_STRAIGHTFLUSH + (st << TOP_CARD_SHIFT))); } else { ret = new HandEvaluation(cards, HandTypes.Flush, HANDTYPE_VALUE_FLUSH + Bits.topFiveCardsTable[ss]); } } else if (Bits.nBitsTable[sh] >= 5) { uint st = Bits.straightTable[sh]; if (st != 0) { return(new HandEvaluation(cards, HandTypes.StraightFlush, HANDTYPE_VALUE_STRAIGHTFLUSH + (st << TOP_CARD_SHIFT))); } else { ret = new HandEvaluation(cards, HandTypes.Flush, HANDTYPE_VALUE_FLUSH + Bits.topFiveCardsTable[sh]); } } else if (Bits.nBitsTable[sd] >= 5) { uint st = Bits.straightTable[sd]; if (st != 0) { return(new HandEvaluation(cards, HandTypes.StraightFlush, HANDTYPE_VALUE_STRAIGHTFLUSH + (st << TOP_CARD_SHIFT))); } else { ret = new HandEvaluation(cards, HandTypes.Flush, HANDTYPE_VALUE_FLUSH + Bits.topFiveCardsTable[sd]); } } else if (Bits.nBitsTable[sc] >= 5) { uint st = Bits.straightTable[sc]; if (st != 0) { return(new HandEvaluation(cards, HandTypes.StraightFlush, HANDTYPE_VALUE_STRAIGHTFLUSH + (st << TOP_CARD_SHIFT))); } else { ret = new HandEvaluation(cards, HandTypes.Flush, HANDTYPE_VALUE_FLUSH + Bits.topFiveCardsTable[sc]); } } else { uint st = Bits.straightTable[ranks]; if (st != 0) { ret = new HandEvaluation(cards, HandTypes.Straight, HANDTYPE_VALUE_STRAIGHT + (st << TOP_CARD_SHIFT)); } }; /* * Another win -- since there can't be a FH/Quads with 5 ranks present, * which is true most of the time when there is a made hand, then if we've * found a five card hand, just return. This skips the whole process of * computing two_mask/three_mask/etc. */ if (ret.Value != 0) { return(ret); } } /* * By the time we're here, either: * 1) there's no five-card hand possible (flush or straight), or * 2) there's a flush or straight, but we know that there are enough * duplicates to make a full house / quads possible. */ switch (n_dups) { case 0: /* It's a no-pair hand */ return(new HandEvaluation(cards, HandTypes.HighCard, HANDTYPE_VALUE_HIGHCARD + Bits.topFiveCardsTable[ranks])); case 1: { /* It's a one-pair hand */ ret.Type = HandTypes.Pair; uint t, kickers; two_mask = ranks ^ (sc ^ sd ^ sh ^ ss); ret.Value = (uint)(HANDTYPE_VALUE_PAIR + (Bits.topCardTable[two_mask] << TOP_CARD_SHIFT)); t = ranks ^ two_mask; /* Only one bit set in two_mask */ /* Get the top five cards in what is left, drop all but the top three * cards, and shift them by one to get the three desired kickers */ kickers = (Bits.topFiveCardsTable[t] >> CARD_WIDTH) & ~FIFTH_CARD_MASK; ret.Value += kickers; return(ret); } case 2: /* Either two pair or trips */ two_mask = ranks ^ (sc ^ sd ^ sh ^ ss); if (two_mask != 0) { uint t = ranks ^ two_mask; /* Exactly two bits set in two_mask */ ret.Type = HandTypes.TwoPair; ret.Value = (uint)(HANDTYPE_VALUE_TWOPAIR + (Bits.topFiveCardsTable[two_mask] & (TOP_CARD_MASK | SECOND_CARD_MASK)) + (Bits.topCardTable[t] << THIRD_CARD_SHIFT)); return(ret); } else { ret.Type = HandTypes.Trips; uint t, second; three_mask = ((sc & sd) | (sh & ss)) & ((sc & sh) | (sd & ss)); ret.Value = (uint)(HANDTYPE_VALUE_TRIPS + (Bits.topCardTable[three_mask] << TOP_CARD_SHIFT)); t = ranks ^ three_mask; /* Only one bit set in three_mask */ second = Bits.topCardTable[t]; ret.Value += (second << SECOND_CARD_SHIFT); t ^= (1U << (int)second); ret.Value += (uint)(Bits.topCardTable[t] << THIRD_CARD_SHIFT); return(ret); } default: /* Possible quads, fullhouse, straight or flush, or two pair */ four_mask = sh & sd & sc & ss; if (four_mask != 0) { ret.Type = HandTypes.FourOfAKind; uint tc = Bits.topCardTable[four_mask]; ret.Value = (uint)(HANDTYPE_VALUE_FOUR_OF_A_KIND + (tc << TOP_CARD_SHIFT) + ((Bits.topCardTable[ranks ^ (1U << (int)tc)]) << SECOND_CARD_SHIFT)); return(ret); } ; /* Technically, three_mask as defined below is really the set of * bits which are set in three or four of the suits, but since * we've already eliminated quads, this is OK */ /* Similarly, two_mask is really two_or_four_mask, but since we've * already eliminated quads, we can use this shortcut */ two_mask = ranks ^ (sc ^ sd ^ sh ^ ss); if (Bits.nBitsTable[two_mask] != n_dups) { /* Must be some trips then, which really means there is a * full house since n_dups >= 3 */ ret.Type = HandTypes.FullHouse; uint tc, t; three_mask = ((sc & sd) | (sh & ss)) & ((sc & sh) | (sd & ss)); ret.Value = HANDTYPE_VALUE_FULLHOUSE; tc = Bits.topCardTable[three_mask]; ret.Value += (tc << TOP_CARD_SHIFT); t = (two_mask | three_mask) ^ (1U << (int)tc); ret.Value += (uint)(Bits.topCardTable[t] << SECOND_CARD_SHIFT); return(ret); } ; if (ret.Value != 0) /* flush and straight */ { return(ret); } else { /* Must be two pair */ ret.Type = HandTypes.TwoPair; uint top, second; ret.Value = HANDTYPE_VALUE_TWOPAIR; top = Bits.topCardTable[two_mask]; ret.Value += (top << TOP_CARD_SHIFT); second = Bits.topCardTable[two_mask ^ (1 << (int)top)]; ret.Value += (second << SECOND_CARD_SHIFT); ret.Value += (uint)((Bits.topCardTable[ranks ^ (1U << (int)top) ^ (1 << (int)second)]) << THIRD_CARD_SHIFT); return(ret); } } }
public override void SetupPhases() { PhaseActions = new List <Func <TableDealer, DisplayStage[]> >(); var chipMovingPhases = new DisplayStage[] { DisplayStage.DealtCards, DisplayStage.BetsOut, DisplayStage.Scooping, DisplayStage.PotScooped, DisplayStage.Delivering, DisplayStage.DeliverPot, DisplayStage.PotDelivered }; //Reset table for new hand PhaseActions.Add((tableDealer) => { var displayStages = StartNewHand(tableDealer); if (displayStages != null) { tableDealer.SetButton(); } return(displayStages); }); PhaseActions.Add(HandsDealt); //Blinds and set first bet action PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "Preflop"; tableDealer.Table.PhaseMessage = ""; var seatsAroundTheTable = tableDealer.Table.SeatsWithHands().OrderBy(s => s.Position).ToArray(); var players = seatsAroundTheTable.Count(); var buttonSeat = Array.FindIndex <Seat>(seatsAroundTheTable, 0, seatsAroundTheTable.Count(), s => s.Button); var smallBlind = buttonSeat + 1; if (smallBlind == players) { smallBlind = 0; } seatsAroundTheTable[smallBlind].ChipsOut = 1; var bigBlind = smallBlind + 1; if (bigBlind == players) { bigBlind = 0; } seatsAroundTheTable[bigBlind].ChipsOut = 2; var betAction = bigBlind + 1; if (betAction == players) { betAction = 0; } seatsAroundTheTable[betAction].ActionOn = true; return(new DisplayStage[] { DisplayStage.BetsOut, DisplayStage.SeatAction }); }); //Scoop Pot PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "Preflop"; tableDealer.Table.PhaseMessage = ""; return(new DisplayStage[] { DisplayStage.Scooping, DisplayStage.PotScooped }); }); //Flop PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "Flop"; tableDealer.Table.PhaseMessage = ""; var board = GetBoard(); board.CardsMask = tableDealer.Deck.DealCards(3); tableDealer.Table.SetBoard(board); var seatsAroundTheTable = tableDealer.Table.SeatsWithHands().OrderBy(s => s.Position).ToArray(); var players = seatsAroundTheTable.Count(); var buttonSeat = Array.FindIndex <Seat>(seatsAroundTheTable, 0, seatsAroundTheTable.Count(), s => s.Button); var smallBlind = buttonSeat + 1; if (smallBlind == players) { smallBlind = 0; } seatsAroundTheTable[smallBlind].ActionOn = true; return(new DisplayStage[] { DisplayStage.DealtCards, DisplayStage.SeatAction }); }); //Scoop Pot PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "Flop"; tableDealer.Table.PhaseMessage = ""; return(new DisplayStage[] { DisplayStage.Scooping, DisplayStage.PotScooped }); }); //Turn PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "Turn"; tableDealer.Table.PhaseMessage = ""; tableDealer.Table.Board.CardsMask |= tableDealer.Deck.DealCards(1); var seatsAroundTheTable = tableDealer.Table.SeatsWithHands().OrderBy(s => s.Position).ToArray(); var players = seatsAroundTheTable.Count(); var buttonSeat = Array.FindIndex <Seat>(seatsAroundTheTable, 0, seatsAroundTheTable.Count(), s => s.Button); var smallBlind = buttonSeat + 1; if (smallBlind == players) { smallBlind = 0; } seatsAroundTheTable[smallBlind].ActionOn = true; return(new DisplayStage[] { DisplayStage.DealtCards, DisplayStage.SeatAction }); }); //Scoop Pot PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "Turn"; tableDealer.Table.PhaseMessage = ""; return(new DisplayStage[] { DisplayStage.Scooping, DisplayStage.PotScooped }); }); //River PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "River"; tableDealer.Table.PhaseMessage = ""; tableDealer.Table.Board.CardsMask |= tableDealer.Deck.DealCards(1); var seatsAroundTheTable = tableDealer.Table.SeatsWithHands().OrderBy(s => s.Position).ToArray(); var players = seatsAroundTheTable.Count(); var buttonSeat = Array.FindIndex <Seat>(seatsAroundTheTable, 0, seatsAroundTheTable.Count(), s => s.Button); var smallBlind = buttonSeat + 1; if (smallBlind == players) { smallBlind = 0; } seatsAroundTheTable[smallBlind].ActionOn = true; return(new DisplayStage[] { DisplayStage.DealtCards, DisplayStage.SeatAction }); }); //Scoop Pot PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "Showdown"; tableDealer.Table.PhaseMessage = ""; return(new DisplayStage[] { DisplayStage.Scooping, DisplayStage.PotScooped }); }); // Award Pot PhaseActions.Add((tableDealer) => { tableDealer.Table.PhaseTitle = "Showdown"; tableDealer.Table.PhaseMessage = ""; var table = tableDealer.Table; HandEvaluation best = new HandEvaluation(); // Find best hand foreach (var seat in table.SeatsWithHands()) { seat.Hand.LastEvaluation = TexasHoldem.EvaluateHand(seat.Hand.CardsMask | table.Board.CardsMask); if (seat.Hand.LastEvaluation.Value > best.Value) { best = seat.Hand.LastEvaluation; } } int pot = table.Pot; int winners = 0; int bestCard = 0; table.PhaseMessage = $"{TexasHoldem.HandTypeDescriptions[(int)best.Type]}"; // Determine winners and the best card from the winners foreach (var seat in table.SeatsWithHands()) { if (seat.Hand.LastEvaluation.Value == best.Value) { winners++; bestCard = Math.Max(bestCard, seat.Hand.BestCard()); table.HighlightCards |= TexasHoldem.FindCards(best); } } int cut = pot / winners; int remainder = pot % winners; // Determine chips in foreach (var seat in table.SeatsWithHands()) { if (seat.Hand.LastEvaluation.Value == best.Value) { seat.ChipsIn = cut + (seat.Hand.BestCard() == bestCard ? remainder : 0); } } return(new DisplayStage[] { DisplayStage.Delivering, DisplayStage.DeliverPot, DisplayStage.PotDelivered }); }); }