views:

137

answers:

4

I'm storing 4-card hands in a way to treat hands with different suits the same, e.g.:

9h 8h 7c 6c

is the same as

9d 8d 7h 6h

since you can replace one suit with another and have the same thing. It's easy to turn these into a unique representation using wildcards for suits. THe previous would become:

9A 8A 7B 6B

My question is - what's the most elegant way to turn the latter back into a list of the former? For example, when the input is 9A 8A 7B 6B, the output should be:

9c 8c 7d 6d
9c 8c 7h 6h
9c 8c 7s 6s
9h 8h 7d 6d
9h 8h 7c 6c
9h 8h 7s 6s
9d 8d 7c 6c
9d 8d 7h 6h
9d 8d 7s 6s
9s 8s 7d 6d
9s 8s 7h 6h
9s 8s 7c 6c

I have some ugly code that does this on a case-by-case basis depending on how many unique suits there are. It won't scale to hands with more cards. Also in a situation like:

7A 7B 8A 8B

it will have duplicates, since in this case A=c and B=d is the same as A=d and B=c.

What's an elegant way to solve this problem efficiently? I'm coding in C, but I can convert higher-level code down to C.

+4  A: 

There are only 4 suits so the space of possible substitutions is really small - 4! = 24 cases. In this case, I don't think it is worth it, to try to come up with something especially clever.

Just parse the string like "7A 7B 8A 8B", count the number of different letters in it, and based on that number, generate substitutions based on a precomputed set of substitutions.

1 letter -> 4 possible substitutions c, d, h, or s
2 letters -> 12 substitutions like in Your example.
3 or 4 letters -> 24 substitutions.

Then sort the set of substitutions and remove duplicates. You have do sort the tokens in every string like "7c 8d 9d 9s" and then sort an array of the strings to detect duplicates but that shouldn't be a problem. It's good to have the patterns like "7A 7B 8A 8B" sorted too (the tokens like: "7A", "8B" are in an ascending order).

EDIT:

An alternative for sorting might be, to detect identical sets if ranks associated with two or more suits and take it into account when generating substitutions, but it's more complicated I think. You would have to create a set of ranks for each letter appearing in the pattern string.

For example, for the string "7A 7B 8A 8B", with the letter A, associated is the set {7, 8} and the same set is associated with the letter B. Then You have to look for identical sets associated with different letters. In most cases those sets will have just one element, but they might have two as in the example above. Letters associated with the same set are interchangeable. You can have following situations

1 letter no duplicates -> 4 possible substitutions c, d, h, or s
2 letters no duplicates -> 12 substitutions.
2 letters, 2 letters interchangeable (identical sets for both letters) -> 6 substitutions.
3 letters no duplicates -> 24 substitutions.
3 letters, 2 letters interchangeable -> 12 substitutions.
4 letters no duplicates -> 24 substitutions.
4 letters, 2 letters interchangeable -> 12 substitutions.
4 letters, 3 letters interchangeable -> 4 substitutions.
4 letters, 2 pairs of interchangeable letters -> 6 substitutions.
4 letters, 4 letters interchangeable -> 1 substitution.
Maciej Hehl
I like the idea of just pre-computing the subs. I think sorting the subs to detect dupes would be too slow, though - any other ideas?
Claudiu
+2  A: 

I think a generic permutation function that takes an array arr and an integer n and returns all possible permutations of n elements in that array would be useful here.

Find how how many unique suits exist in the hand. Then generate all possible permutations with those many elements from the actual suits [c, d, h, s]. Finally go through each permutation of suits, and assign each unknown letter [A, B, C, D] in the hand to the permuted values.

The following code in Ruby takes a given hand and generates all suit permutations. The heaviest work is being done by the Array.permutation(n) method here which should simplify things a lot for a corresponding C program as well.

# all 4 suits needed for generating permutations
suits = ["c", "d", "h", "s"]

# current hand
hand = "9A 8A 7B 6B"

# find number of unique suits in the hand. In this case it's 2 => [A, B]
unique_suits_in_hand = hand.scan(/.(.)\s?/).uniq.length

# generate all possible permutations of 2 suits, and for each permutation
# do letter assignments in the original hand
# tr is a translation function which maps corresponding letters in both strings.
# it doesn't matter which unknowns are used (A, B, C, D) since they 
# will be replaced consistently.
# After suit assignments are done, we split the cards in hand, and sort them.
possible_hands = suits.permutation(unique_suits_in_hand).map do |perm|
   hand.tr("ABCD", perm.join ).split(' ').sort
end

# Remove all duplicates
p possible_hands.uniq

The above code outputs

9c 8c 7d 6d
9c 8c 7h 6h
9c 8c 7s 6s
9d 8d 7c 6c
9d 8d 7h 6h
9d 8d 7s 6s
9h 8h 7c 6c
9h 8h 7d 6d
9h 8h 7s 6s
9s 8s 7c 6c
9s 8s 7d 6d
9s 8s 7h 6h
Anurag
what about the duplication issue with 7A 7B 8A 8B ?
Claudiu
@Claudiu - You can split and sort the cards in hand to have a unique representation for each hand, and then remove duplicates. Use an associative array to map this unique representation as the key, so later lookups can be in O(1). For example, `7c 7d 8c 8d` and `7d 7c 8d 8c` both would have the key `7c 7d 8c 8d`, so you can avoid duplicates while generating the hands, or remove them later.
Anurag
A: 

Represent suits as sparse arrays or lists, numbers as indexes, hands as associative arrays

In your example

H [A[07080000] B[07080000] C[00000000] D[00000000] ] (place for four cards)

To get the "real" hands always apply the 24 permutations (fixed time), so you don't have to care about how many cards has your hand A,B,C,D -> c,d,h,s with the following "trick"> store always in alphabetical order>

H1 [c[xxxxxx] d[xxxxxx] s[xxxxxx] h[xxxxxx]]

Since Hands are associative arrays, duplicated permutations does not generate two different output hands.

belisarius
A: 
#include <stdio.h>
#include <stdlib.h>

const int RANK = 0;
const int SUIT = 1;

const int NUM_SUITS = 4;

const char STANDARD_SUITS[] = "dchs";
int usedSuits[] = {0, 0, 0, 0};

const char MOCK_SUITS[] = "ABCD";

const char BAD_SUIT = '*';

char pullSuit (int i) {
  if (usedSuits [i] > 0) {
    return BAD_SUIT;
  }
  ++usedSuits [i];
  return STANDARD_SUITS [i];
}

void unpullSuit (int i) {
  --usedSuits [i];
}

int indexOfSuit (char suit, const char suits[]) {
  int i;
  for (i = 0; i < NUM_SUITS; ++i) {
    if (suit == suits [i]) {
      return i;
    }
  }
  return -1;
}

int legitimateSuits (const char suits[]) {
  return indexOfSuit (BAD_SUIT, suits) == -1;
}

int distinctSuits (const char suits[]) {
  int i, j;
  for (i = 0; i < NUM_SUITS; ++i) {
    for (j = 0; j < NUM_SUITS; ++j) {
      if (i != j && suits [i] == suits [j]) {
        return 0;
      }
    }
  }
  return 1;
}

void printCards (char* mockCards[], int numMockCards, const char realizedSuits[]) {
  int i;
  for (i = 0; i < numMockCards; ++i) {
    char* mockCard = mockCards [i];
    char rank = mockCard [RANK];
    char mockSuit = mockCard [SUIT];
    int idx = indexOfSuit (mockSuit, MOCK_SUITS);
    char realizedSuit = realizedSuits [idx];
    printf ("%c%c ", rank, realizedSuit);
  }
  printf ("\n");
}

/*
 * Example usage:
 * char** mockCards = {"9A", "8A", "7B", "6B"};
 * expand (mockCards, 4);
 */
void expand (char* mockCards[], int numMockCards) {
  int i, j, k, l;
  for (i = 0; i < NUM_SUITS; ++i) {
    char a = pullSuit (i);
    for (j = 0; j < NUM_SUITS; ++j) {
      char b = pullSuit (j);
      for (k = 0; k < NUM_SUITS; ++k) {
        char c = pullSuit (k);
        for (l = 0; l < NUM_SUITS; ++l) {
          char d = pullSuit (l);
          char realizedSuits[] = {a, b, c, d};
          int legitimate = legitimateSuits (realizedSuits);
          if (legitimate) {
            int distinct = distinctSuits (realizedSuits);
            if (distinct) {
              printCards (mockCards, numMockCards, realizedSuits);
            }
          }
          unpullSuit (l);
        }
        unpullSuit (k);
      }
      unpullSuit (j);
    }
    unpullSuit (i);
  }
}

int main () {
  char* mockCards[] = {"9A", "8A", "7B", "6B"};
  expand (mockCards, 4);
  return 0;
}
trinithis