Weekend Challenge #2 - Poker Hand Evaluation
Finding a straight with wildcards
Because of a size limit on Code Review I have to split my weekly challenge result in two, and here is a part where I think there is some room for speed optimization. I started writing this method in another approach, but after several hours it still didn't work as expected so I threw it away and started writing this method which directly worked as I wanted it to.
The method should take an int array as input, loops through the array to find the highest possible straight (at least five consecutive values of > 0). If encountering a 0
and it has a wildcard to use, it should use the wildcard to "fill in the gap". For example, the array 1 0 1 1 1
with one wildcard is a straight, while the array 1 0 0 1 1
with a wildcard is not a straight.
There is no limit to how many wildcards can be used, but if there are five or more wildcards then the wildcards themselves should return the highest possible straight.
I'm not quite sure what the time complexity is in this method I have written, but I think worst case is between \$O(n)\$ and \$O(n^2)\$, I think it is about \$O(n^2 / 2)\$ or something. If anyone knows, please let me know.
Is it possible to write this method with worst case not higher than \$O(n)\$?
/**
* Checks for a normal STRAIGHT. Returns null if no straight was found
*/
public class PokerStraight implements PokerHandResultProducer {
/**
* Scans an array of ints to search for sequence of 1s. Can fill in gaps by using wildcards
* @param ranks An array of the ranks provided by {@link PokerHandAnalyze}
* @param wildcards Number of usable wildcards to fill gaps for the straight
* @return The highest rank (index + 1) for which the straight started
*/
public static int findHighestIndexForStraight(int[] ranks, int wildcards) {
return findHighestIndexForStraight(ranks, ranks.length - 1, wildcards);
}
private static int findHighestIndexForStraight(int[] ranks, int startIndex, int wildcards) {
int wildsLeft = wildcards;
for (int i = startIndex; i >= 0; i--) {
int count = ranks[i];
if (count > 0) {
// do nothing
}
else if (wildsLeft > 0) {
wildsLeft--;
}
else {
return findHighestIndexForStraight(ranks, startIndex - 1, wildcards);
}
if (startIndex - i + 1 >= PokerHandResultProducer.HAND_SIZE)
return startIndex + 1;
}
return -1;
}
@Override
public PokerHandResult resultFor(PokerHandAnalyze analyze) {
int straight = findHighestIndexForStraight(analyze.getRanks(), analyze.getWildcards());
if (straight != -1)
return new PokerHandResult(PokerHandType.STRAIGHT, straight, 0, null);
// We don't need to provide any kickers since we have a straight of 5 cards.
return null;
}
}
Here is some code to test the method, also showing what I expect it will return:
private int findHighestIndexForStraight(int[] ranks, int wildcards) {
int res = PokerStraight.findHighestIndexForStraight(ranks, wildcards);
return res == -1 ? res : res - 1; // For testing, I want to know the index and not the "rank"
}
@Test
public void straights() {
assertEquals(7, findHighestIndexForStraight(new int[]{ 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0 }, 0));
assertEquals(7, findHighestIndexForStraight(new int[]{ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1 }, 0));
assertEquals(7, findHighestIndexForStraight(new int[]{ 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1 }, 0));
assertEquals(-1, findHighestIndexForStraight(new int[]{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 }, 1));
assertEquals(8, findHighestIndexForStraight(new int[]{ 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0 }, 1));
assertEquals(6, findHighestIndexForStraight(new int[]{ 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0 }, 1));
assertEquals(10, findHighestIndexForStraight(new int[]{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, 1));
assertEquals(4, findHighestIndexForStraight(new int[]{ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, 1));
assertEquals(8, findHighestIndexForStraight(new int[]{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0 }, 1));
assertEquals(10, findHighestIndexForStraight(new int[]{ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 }, 1));
assertEquals(-1, findHighestIndexForStraight(new int[]{ 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1 }, 0));
}