I am aware of the compare rules in Java (and in general) as described here.
I have an ArrayList of Strings.
Each String represent a Texas Hold'em Poker Hand ignoring the suit.
Each String is exactly 13 characters long.
Each String consists of digits only whose sum is 7.
For example, "0100300200100" represent a Poker Hand which consists of one card of 3, three cards of 6, two cards of 9 and one card of Queen.
(In this case the hand represent a Full House - sixes full of nines).
I want to sort this list according to the strength of the Poker hands.
I have the following java code who implements the Comparator's compare method.
final Comparator<String> COMBINATION_ORDER = new Comparator<String>() {
@Override
public int compare(String c1, String c2) {
if (c1.indexOf('4') != -1 || c2.indexOf('4') != -1) { // Four of a kind
if (c1.indexOf('4') == c2.indexOf('4')) {
for (int i = 12; i >= 0; i--) {
if (c1.charAt(i) != '0' && c1.charAt(i) != '4') {
if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
return 0;
}
return 1;
}
if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
return -1;
}
}
}
return c1.indexOf('4') - c2.indexOf('4');
}
int tripleCount1 = StringFunctions.countOccurrencesOf(c1, "3");
int tripleCount2 = StringFunctions.countOccurrencesOf(c2, "3");
if (tripleCount1 > 1 || (tripleCount1 == 1 && c1.indexOf('2') != -1) || tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) { // Full house
int higherTriple = c1.lastIndexOf('3');
if (higherTriple == c2.lastIndexOf('3')) {
for (int i = 12; i >= 0; i--) {
if (i == higherTriple) {
continue;
}
if (c1.charAt(i) == '2' || c1.charAt(i) == '3') {
if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
return 0;
}
return 1;
}
if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
return -1;
}
}
}
return higherTriple - c2.lastIndexOf('3');
}
return 0;
}
};
In the meantime I refer only to Four-of-a-Kind and Full House. That means that every other hand will be treated as equal to each other (but inferior to Four-of-a-Kind or Full House).
But when I'm sorting:
combinations.sort(COMBINATION_ORDER);
(where combinations is my ArrayList).
I get an Exception.
Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:773)
at java.util.TimSort.mergeAt(TimSort.java:510)
at java.util.TimSort.mergeCollapse(TimSort.java:437)
at java.util.TimSort.sort(TimSort.java:241)
at java.util.Arrays.sort(Arrays.java:1512)
at java.util.ArrayList.sort(ArrayList.java:1454)
at Poker.main(Poker.java:120)
Please help me understand what's wrong with the code.
Thanks very much.
EDIT:
As @ajb said I didn't take into consideration Three-of-a-Kind that has no Full House.
SOLUTION:
final Comparator<String> COMBINATION_ORDER = new Comparator<String>() {
@Override
public int compare(String c1, String c2) {
if (c1.indexOf('4') != -1 || c2.indexOf('4') != -1) { // Four of a kind
if (c1.indexOf('4') == c2.indexOf('4')) {
for (int i = 12; i >= 0; i--) {
if (c1.charAt(i) != '0' && c1.charAt(i) != '4') {
if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
return 0;
}
return 1;
}
if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
return -1;
}
}
}
return c1.indexOf('4') - c2.indexOf('4');
}
int tripleCount1 = StringFunctions.countOccurrencesOf(c1, "3");
int tripleCount2 = StringFunctions.countOccurrencesOf(c2, "3");
if (tripleCount1 > 1 || (tripleCount1 == 1 && c1.indexOf('2') != -1)) { // c1 Full house
if (tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) { // c2 Full house too
int higherTriple = c1.lastIndexOf('3');
if (higherTriple == c2.lastIndexOf('3')) {
for (int i = 12; i >= 0; i--) {
if (i == higherTriple) {
continue;
}
if (c1.charAt(i) == '2' || c1.charAt(i) == '3') {
if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
return 0;
}
return 1; // only c1 Full house
}
if (c2.charAt(i) == '2' || c2.charAt(i) == '3') { // only c2 Full house
return -1;
}
}
}
return higherTriple - c2.lastIndexOf('3');
}
return 1;
}
if (tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) {
return -1;
}
return 0;
}
};