Hello All,
I am attempting to create a simple program that analyzes the poker hands. Given n hands/players and the community cards (Texas hold 'em) I would like to determine the winner(s). However, my test is failing when I have two exact results - it only returns one winner. i.e The hand result contains J J 9 9 K, for both players, but my winners list contains one.
There are a couple reasons why I am posting here. Obviously, the first being is there anything apparent that is wrong here? Is this a good approach to have the sorting implemented (I couldn't see a reason to separate the sorting), is there a better approach and why?
I have a DetermineWinners method that is performing a order on the players' HandResult:
var ordered = _players.OrderByDescending(player => player.Result);
var bestHand = ordered.First();
var winners = ordered.Where(s => s.Result == bestHand.Result).ToList();
Here is my hand result class:
public class HandResult : IComparable<HandResult>
{
public Hand WholeCards { get; set; }
public HandRanking HandRank { get; set; }
public IEnumerable<Card> CommunityCards { get; set; }
public IEnumerable<Card> UsedCards { get; set; }
public static bool operator !=(HandResult a, HandResult b)
{
if (a == null)
return b != null;
if (b == null)
return true;
if (a.HandRank != b.HandRank)
return true;
//Compare Used Cards
var aCards = a.UsedCards.Select(s => s.GetCardValue()).ToList();
var bCards = b.UsedCards.Select(s => s.GetCardValue()).ToList();
var cardGroup = a.HandRank.GetGrouping();
for (int i = 0; i < 5; i += cardGroup[i])
{
if (aCards[i] != bCards[i])
return true;
}
return false;
}
public static bool operator ==(HandResult a, HandResult b)
{
if ((object)a == null)
return (object)b == null;
if ((object)b == null)
return false;
if (a.HandRank != b.HandRank)
return false;
var aCards = a.UsedCards.Select(s => s.GetCardValue()).ToList();
var bCards = b.UsedCards.Select(s => s.GetCardValue()).ToList();
var cardGroup = a.HandRank.GetGrouping();
for (int i = 0; i < 5; i += cardGroup[i])
{
if (aCards[i] != bCards[i])
{
return false;
}
}
return true;
}
public static bool operator >(HandResult a, HandResult b)
{
if ((object)a == null)
return (object)b != null;
if ((object)b == null)
return false;
if ((object)a == (object)b)
return false;
if (a.HandRank != b.HandRank)
return a.HandRank > b.HandRank;
if (a == b)
return false;
var aCards = a.UsedCards.Select(s => s.GetCardValue()).ToList();
var bCards = b.UsedCards.Select(s => s.GetCardValue()).ToList();
var cardGroup = a.HandRank.GetGrouping();
for (int i = 0; i < 5; i += cardGroup[i])
{
if (aCards[i] != bCards[i])
{
return aCards[i] > bCards[i];
}
}
return false;
}
public static bool operator <(HandResult a, HandResult b)
{
if ((object)a == null)
return (object)b == null;
if ((object)b == null)
return true;
if ((object)a == (object)b)
return false;
if (a.HandRank != b.HandRank)
return a.HandRank < b.HandRank;
var aCards = a.UsedCards.Select(s => s.GetCardValue()).ToList();
var bCards = b.UsedCards.Select(s => s.GetCardValue()).ToList();
var cardGroup = a.HandRank.GetGrouping();
for (int i = 0; i < 5; i += cardGroup[i])
{
if (aCards[i] != bCards[i])
return aCards[i] < bCards[i];
}
return false;
}
#region IComparable<HandResult> Members
public int CompareTo(HandResult other)
{
if (this == null)
return other == null ? 0 : -1;
if (other == null)
return 1;
if (this == other)
return 0;
if (this > other)
return 1;
return -1;
}
#endregion
public override bool Equals(object obj)
{
return this == (HandResult)obj;
}
public override int GetHashCode()
{
var result = 0;
result = (result * 397) ^ (int)this.HandRank;
foreach (var card in this.UsedCards)
{
result = (result * 397) ^ card.GetHashCode();
}
return result;
}
}
The GetCardResult method simply returns an integer representation of the card, i.e 1 through 14. Here is the HandRanking enum:
public enum HandRanking
{
HighCard,
Pair,
TwoPair,
ThreeOfAKind,
Straight,
Flush,
FullHouse,
FourOfAKind,
StraightFlush,
RoyalFlush
}
This is the GetGrouping extension on the HandRanking enum. It is used to help iterate through the cards when comparing values:
internal static int[] GetGrouping(this HandRanking rank)
{
switch (rank)
{
case HandRanking.Pair:
return new int[] { 2, 0, 1, 1, 1 };
case HandRanking.TwoPair:
return new int[] { 2, 0, 2, 0, 1 };
case HandRanking.ThreeOfAKind:
return new int[] { 3, 0, 0, 1, 1 };
case HandRanking.FullHouse:
return new int[] { 3, 0, 0, 2, 0 };
case HandRanking.FourOfAKind:
return new int[] { 4, 0, 0, 0, 1 };
default:
return new int[] { 1, 1, 1, 1, 1 };
}
}
Your help is greatly appreciated.
EDIT: My tests for CompareTo_Equal, CompareTo_LessThan and CompareTo_GreaterThan (which uses my operator overloads) succeed with results: 0, -1 and 1, respectively. I am lead to believe it's a problem with my Linq.OrderByDescending implementation. I would of thought this just uses the CompareTo implemenation, am I wrong?