I'm looking for an efficient solution to determine if a hand is a winning hand in Indian rummy. Indian rummy is similar to gin rummy in terms of melding. One could either meld a sequence (straight) of the same suit or meld a set of same value. Both sequences and sets should at least contain 3 cards. Unlike gin rummy, in Indian rummy a hand consists of 13 cards. A winning hand should contain at least two sequences and at least one of those sequences have to be a pure sequence. By pure, I mean the sequence should not be made with the help of a joker (wild card). The rest of the hand can consist of sequences and sets made with or without jokers. Note: Apart from the 2 jokers in a deck (52 + 2), there is also a random card from the deck used as a joker. For example, if 5 of spades was randomly picked as a joker, then the remaining three 5s of other suits in the deck can be used as a joker on top of the 2 regular jokers.
Here are a few examples of valid winning hands without the use of a joker:
Here are a few examples of winning hands with the use of a joker. Let's assume 6(spades) was the joker randomly picked from the deck; so all the remaining 6s can be used as a joker.
Here are a few examples of what is NOT a winning hand:
I hope that has explained what a winning hand is. The model below represents a card:
public class Card {
public final static int SPADES = 0,
HEARTS = 1,
DIAMONDS = 2,
CLUBS = 3;
public final static int ACE = 1,
JACK = 11,
QUEEN = 12,
KING = 13,
JOKER = 0;
private final int suit;
private final int value;
public Card(int theValue, int theSuit) {
value = theValue;
suit = theSuit;
}
public int getSuit() {
return suit;
}
public int getValue() {
return value;
}
public String getSuitAsString() {
switch ( suit ) {
case SPADES: return "Spades";
case HEARTS: return "Hearts";
case DIAMONDS: return "Diamonds";
case CLUBS: return "Clubs";
default: return "??";
}
}
public String getValueAsString() {
switch ( value ) {
case 1: return "Ace";
case 2: return "2";
case 3: return "3";
case 4: return "4";
case 5: return "5";
case 6: return "6";
case 7: return "7";
case 8: return "8";
case 9: return "9";
case 10: return "10";
case 11: return "Jack";
case 12: return "Queen";
case 13: return "King";
default: return "JOKER";
}
}
@Override
public String toString() {
return getValueAsString().equals("JOKER") ? "JOKER" : getValueAsString() + "(" + getSuitAsString() + ")";
}
@Override
public boolean equals(Object card) {
return suit == ((Card) card).getSuit() && value == ((Card) card).getValue();
}
}
I also wrote some functions to get the possible sequences and sets in my card. The argument (List) in the getSequences function is already sorted by suit and then by value. In case of the argument in the getSets function, cards are sorted by value alone.The value of second argument (min) in both the functions are 3.
private List<List<Card>> getSequences(List<Card> hand, int min) {
List<List<Card>> sequences = new ArrayList<>();
List<Card> sequence = new ArrayList<>();
for(int i=1; i<hand.size(); i++) {
if(hand.get(i).getSuit() == hand.get(i-1).getSuit() &&
(hand.get(i).getValue() - hand.get(i-1).getValue()) == 1) {
sequence.add(hand.get(i-1));
if(hand.get(i).getValue() == 13) {
int j = i;
while(hand.get(j).getSuit() == hand.get(i).getSuit()) {
j--;
if(hand.get(j).getValue() == 1) {
sequence.add(hand.get(j));
}
}
}
if(i == hand.size() -1) {
sequence.add(hand.get(i));
sequences.add(sequence);
}
} else {
sequence.add(hand.get(i-1));
if(sequence.size() >= min) {
sequences.add(sequence);
}
sequence = new ArrayList<>();
}
}
return sequences;
}
private List<List<Card>> getSets(List<Card> hand, int min) {
List<List<Card>> sets = new ArrayList<>();
List<Card> set = new ArrayList<>();
for(int i=1; i<hand.size(); i++) {
if(hand.get(i).getValue() != joker.getValue()) {
if(hand.get(i).getValue() == hand.get(i-1).getValue()) {
set.add(hand.get(i-1));
if(i == hand.size() -1) {
set.add(hand.get(i));
}
} else {
set.add(hand.get(i-1));
if(set.size() >= min) {
sets.add(set);
}
set = new ArrayList<>();
}
}
}
return sets;
}
I don't think it's the most elegant way of finding sequences and sets. Therefore, I welcome any suggestions on how I can improve it. But what I really need help with is what do I do next? There could be overlaps between the sets and sequences. For example, in case of the following cards:
Please advise on efficiently determining a winning hand.
P.S: At the time of determining a winning hand, the player will have 14 cards in the hand. After melding 13 cards, the fourteenth card will be discarded as finishing card.
I have implemented a java version of Rummikub (a game with similar constraints).
My approach there was to give each card a hidden integer attribute (a prime number).
Then each valid meld can be uniquely represented as an integer number.
The precise integer numbers that make valid melds can be calculated beforehand and put in a Set<Long>
of course.
Checking whether a hand contains only valid melds is then reduced to checking whether a given long can be written as a product of a set of given numbers. (for which recursion and dynamic programming can be used)
Concrete example (1):
Set<Long> validMelds = {30, .., ..}
If hand (value = 60) then we know it contains 2 valid melds.
concrete example (2)
known valid melds = {30, 210, 226793, ..}
value of hand = 6803790
easy (recursive) algorithm:
recursive algorithm concludes this is a valid branch
alternative
6803790 is divisible by 210
recursive algorithm concludes the branch stops here
If you need to be able to handle the situation in which parts of the hand-cards are not always part of a valid meld, you may wish to look into linear algebra.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With