Reputation: 45096
In a card application I use 0-51 to represent the 5 card hand.
The suit is card / 13
The rank is card %
There are only 4 possible suit (spade, heart, club diamond)
If all five suit are the same it is a flush. All flush are of equal value. Spades is the same as diamonds.
I know you are going to say pre-optimization is evil but I am running some simulations doing this million of times and this is the most expensive step. The card could be byte but the calculation seem to be faster with int. I don't really check the range but I put that in so you would know there is a range.
Is there a more efficient way?
public bool IsFlush(int[] quick)
{
HashSet<int> suit = new HashSet<int>();
suit.Add(quick[0] / 13);
int thisQuick;
for (int i = 1; i < quick.Length; i ++)
{
thisQuick = quick[i];
if (thisQuick < 0 || thisQuick > 51)
throw new IndexOutOfRangeException();
if (suit.Add(thisQuick / 13))
return false;
}
return true;
}
Upvotes: 3
Views: 332
Reputation: 6813
Eliminating the HashSet
should speed things up a bit:
public static bool IsFlush(int[] hand)
{
int firstSuit = hand[0] / 13;
for (int i = 1; i < hand.Length; i++)
{
int card = hand[i];
if (card < 0 || card > 51)
throw new IndexOutOfRangeException();
if (firstSuit != (card / 13))
return false;
}
return true;
}
My (admittedly meager) testing shows about a 20% performance improvement.
Upvotes: 2
Reputation: 155085
I'd use bitfields to store information about cards, it might be faster, that way you can avoid the expense of integer division and modulo operations.
const Int32 SuitMask = 0x001100000;
const Int32 Spade = 0x000000000;
const Int32 Heart = 0x000100000;
const Int32 Diamond = 0x001000000;
const Int32 Club = 0x001100000;
public static Boolean AllCardsInHandOfSameSuit(Int32[] hand) {
Int32 countSpades = 0;
Int32 countHeart = 0;
Int32 countDiamonds = 0;
Int32 countClubs = 0;
foreach( Int32 card in hand ) {
Int32 suit = card & SuitMask;
switch( suit ) {
case Spade:
countSpades++;
break;
case Heart:
countHearts++;
break;
case Diamond:
countDiamonds++;
break;
case Club:
countClubs++;
break;
}
}
// Your question is not worded clearly about whether or not you care about unique ranks, only unique suits:
// I'm also unsure of your flush/hand rules, but just compare the counts as-required:
Boolean allSameSuit =
( countSpades == 0 || countSpades == hand.Length ) &&
( countHearts == 0 || countHearts == hand.Length ) &&
( countDiamonds == 0 || countDiamonds == hand.Length ) &&
( countClubs == 0 || countClubs == hand.Length );
Boolean allDifferentSuit =
countSpades <= 1 &&
countHearts <= 1 &&
countDiamonds <= 1 &&
countClubs <= 1;
}
To generate values for cards:
public static Int32 CreateCard(Int32 suit, Int32 rank) {
return suit | ( rank & 0x0000 );
}
For example:
Int32[] deck = new Int32[52];
for( Int32 i = 0; i < deck.Length; i++ ) {
Int32 suit =
i / 13 == 0 ? Spade :
i / 13 == 1 ? Heart :
i / 13 == 2 ? Diamond : Club;
deck[i] = CreateCard( suit, i % 13 );
}
Int32[] hand = new Int32[4];
for( Int32 i = 0; i < hand.Length; i++ ) {
hand[i] = deck[ GetRandomIndexPreviouslyUnused() ];
}
Upvotes: 2