Almost straight forward

With confidence after cleaning up the Pair, TwoPair and ThreeOfAKind identifiers, I look at identifying a straight. Now, this is a little different, but we start simply by adding the tests and a blank StraightIdentifier implementation.

I tackle the problem as follows: if I get the face value of the cards, ignoring duplicates and in descending order, then I can simply start with the highest card and then ask if the 5th card along is 4 less than us, we must have a straight. If not, take the next highest card. Repeat until we either find a straight, or there are less than five cards.

For example, if we have the seven cards with face values of 2, 6, 11 (Jack), 4, 11, 3, and 5, then arranged in unique descending order we get 11, 6, 5, 4, 3, 2. Then we start with the highest card, a Jack (11). Is the 5th card from the Jack a 7 (which would give us a Jack-high straight)? Nope. Remove the Jack and try again. We can try again as there are still 5 or more cards, so a straight is still a possibility. Next up is the 6. The 5th card along from the 6 is a 2, and therefore we can determine we have a straight:

public RankedHand accept(String player, Iterable<Card> cards) {
  List<Map.Entry<Integer, Collection<Card>>> inOrder = Hands.existSameValuedCards(cards, 1);
  while (inOrder.size() >= 5) {
    if (inOrder.get(0).getKey() - 4 == inOrder.get(4).getKey())
      return new RankedHand(player, cards, this, cardsFrom(inOrder));
    inOrder.remove(0);
  }
  return null;
}

Ace!

This does it - the tests now pass again. But there is a problem: Ace cards. We can have an Ace-high straight or an Ace-low straight. Ace-high is covered by the above, the problem is ace-low. The simplest solution that comes to mind is to try ace-high first - that is do the current implementation. Then, if no straight is found, substitute the ace in inOrder (i.e. having a Map.Entry key value of 14) with a key value of 1, then re-run the straight identification logic:

public RankedHand accept(String player, Iterable<Card> cards) {
  List<Map.Entry<Integer, Collection<Card>>> inOrder = Hands.existSameValuedCards(cards, 1);
  RankedHand straight = findStraight(player, cards, inOrder);
  if (straight != null) return straight;
  return findStraight(player, cards, aceLow(inOrder));
}

The findStraight method is the while loop of the initial implementation. The aceLow implementation is a bit long and not particularly nice (noted in the issues to revisit later). The main reason is that it needs to create a new Map.Entry instance. This happens to be an interface, so for the time being I just create an anonymous instance right inline. I probably want to introduce a Tuple class (as using the Map.Entry like this feels a bit wrong), but that involves changing a number of methods, so decide against doing that right away.

Nothing for a Pair

After fixing the ace-low problem, one of the tests continued to fail. That was unexpected. Even though there was a straight, it was saying the hand only had a pair. It became obvious were the bug lie after looking at the first line: Hands.existSameValuedCards(cards, 1) .

The 1 argument is really just saying I want to group by face value, not that I'm looking for 'no duplicates'. I decide to add an argument to this method which indicates the type of match to perform: SameValueMatchType.Minimum which means look for hands that have at least the specified number of matches - e.g. if we pass 2, this would return hands with two-of-a-kind, three-of-a-kind, etc. By default, this value is SameValueMatchType.Exact.

Problem solved.

Home straight

This one has thrown up a few problems, but after I complete the bit of refactoring to the aceLow method I'll be fairly happy with the result. Next up: Flush. Must be almost on the home straight now?


Comment Guidelines
See the FAQ for details on the full rules and guidelines. No Spam. Write clearly and thoughtfully - no bad language.