Name that Poker hand - Tiny Code Challenge

Beloved minions,

I was thoroughly bored, so I wrote a function that determines the name of your poker hand (5 cards). I couldn’t help myself and started to optimize for code size. :clue:


int h(char[] g){
	int n=99,i=-2,j,p=n,q=-p,r=n,s=q;
	int[][]y=new int[5][n];
	int[]a=y[0],b=y[1],c=y[2],d=y[3],e=y[4];
	for(;i<13;a[" A23456789TJQK".indexOf(g[i+=2])]++,b[g[++i]]++);
	for(i=n;--i>=0;c[a[i]]=i)if(a[i]>0){p=p<i?p:i;q=q>i?q:i;j=i==1?14:i;r=r<j?r:j;s=s>j?s:j;}
	for(i=n;--i>=0;d[b[i]]=i,e[a[i]]++);boolean u,v,w=q-p==4|s-r==4;
	for(int f:a)w&=f<2;v=d[5]>0;u=w&v;
	for(boolean h:new boolean[]{u&s-r<5,u,c[4]>0,e[3]==1&e[2]==1,v,w,c[3]>0,e[2]==2,e[2]==1}){if(h)break;n--;}
	return n;
}

Code size: 477 chars (after trimming lines and removing newlines)

Can you find a bug, or can you come up with a smaller code size? Scraping off some bytes, to prove how I wasn’t paying attention is appreciated too!

Usage:


String[] hands = new String[9];
hands[0] = "3H 5D JS 3C 7C"; // one pair
hands[1] = "JH 4C 2C JD 2H"; // two pair
hands[2] = "7H 3S 7S 7D 7C"; // four of a kind
hands[3] = "8C 3H 8S 8H 3S"; // full house
hands[4] = "8C 3C 4S 8C 3C"; // two pair
hands[5] = "4C 6C 8S 5C 7C"; // straight
hands[6] = "AC 2C 3C 4C 5C"; // straight flush: A,2,3,4,5 (ace low)
hands[7] = "AC KC QC JC TC"; // royal flush: T,J,Q,K,A (ace high)
hands[8] = "AC 8C QA JH TC"; // high card
		
String[] name = new String[10];
name[0] = "royal flush";
name[1] = "straight flush";
name[2] = "four of a kind";
name[3] = "full house";
name[4] = "flush";
name[5] = "straight";
name[6] = "three of a kind";
name[7] = "two pair";
name[8] = "one pair";
name[9] = "high card";

for(String hand : hands)
	System.out.println(hand+": "+name[99-h(hand.toCharArray())]);

This is really cool! :slight_smile:

EDIT :
You might like this, it’s codegolf, accomplish a task in a minimal amount of characters.


HackerRank.com has alot of great challenges, and you dont have to sign up. It compiles your code for you, and sends back the result, and you can do the challenges in any language.

I just realized I totally forgot about ‘royal flush’ (same as straight flush with ace-high). :cranky:

Edit: fixed, +8 chars :emo:
Edit: optimized, -12 chars :slight_smile:

I remember that site, back when you could sort the leaderboard by language I was 1st in all the code golf challenges using Java :smiley:
Granted, Java is terrible for code golf…

I’ll have to try melting a few more brain cells on Riven’s code and see if I can shrink it any more…

First thing I see (depending on how you count chars) is [icode]char[] g[/icode] can be [icode]char[]g[/icode], saving the space.

https://www.hackerrank.com/challenges/fizzbuzz/leaderboard/filter/language=java

I saw your #1 ranking… I’m #11th atm :slight_smile: I only have to shave off 2 more bytes!

To be practically useful it should not generate an ArrayIndexOutOfBoundsException when you have nothing special in your hand (e.g. on input “3H 5D JS 1C 7C”). For shrinking, wow, I have to exterminate a couple of additional brain cells…

“1C” is an invalid card, it should be “AC” (there are no ones, just aces), it will correctly respond with “high card”

Duh :persecutioncomplex: I’ll go find a pack of cards now…


	   int h(char[]g){
		   int n=99,i=-2,j,p=n,q=-p,r=p,s=q,y[][]=new int[5][n],a[]=y[0],b[]=y[1],c[]=y[2],d[]=y[3],e[]=y[4];
		   for(;i<13;a[" A23456789TJQK".indexOf(g[i+=2])]++,b[g[++i]]++);
		   for(i=n;--i>=0;c[a[i]]=i)if(a[i]>0){p=p<i?p:i;q=q>i?q:i;j=i==1?14:i;r=r<j?r:j;s=s>j?s:j;}
		   boolean v,w=q-p==4|s-r==4;for(i=n;--i>=0;d[b[i]]=i,e[a[i]]++)w&=a[i]<2;v=d[5]>0;
		   for(boolean h: new boolean[]{w&v&s-r<5,w&v,c[4]>0,e[3]==1&e[2]==1,v,w,c[3]>0,e[2]==2,e[2]==1}){if(h)break;n--;}
		   return n;
		}

Code size: 465 (thx BurntPizza for that 1 char!)

Few more:

int h(char[]g){
         int n=99,i=-2,j,p=n,q=-p,r=p,s=q,y[][]=new int[5][n],a[]=y[0],b[]=y[1],c[]=y[2],d[]=y[3],e[]=y[4];
         for(;i<13;a[" A23456789TJQK".indexOf(g[i+=2])]++,b[g[++i]]++);
         for(i=n;i-->0;c[a[i]]=i)if(a[i]>0){p=p<i?p:i;q=q>i?q:i;j=i==1?14:i;r=r<j?r:j;s=s>j?s:j;} // --i>=0    -->     i-->0    (high postfix precedence)
         boolean v,w=q-p==4|s-r==4;for(i=n;i-->0;d[b[i]]=i,e[a[i]]++)w&=a[i]<2;v=d[5]>0;   // same as before
         for(boolean h:new boolean[]{w&v&s-r<5,w&v,c[4]>0,e[3]==1&e[2]==1,v,w,c[3]>0,e[2]==2,e[2]==1}){if(h)break;n--;}
         return n; //  ^ removed space
}

Thx, I can remove even more bytes, but I’m on my mobile now, so I can’t easily compile or test…

int h(char[]g){
         int n=99,i=0,j,p=n,q=-p,r=p,s=q,y[][]=new int[5][n],a[]=y[0],b[]=y[1],c[]=y[2],d[]=y[3],e[]=y[4];
         for(;i<13;a[" A23456789TJQK".indexOf(g[i])]++,b[g[++i]]++)i+=2;
         for(i=n;i-->0;c[a[i]]=i)if(a[i]>0){p=p<i?p:i;q=q>i?q:i;j=i==1?14:i;r=r<j?r:j;s=s>j?s:j;} // --i>=0    -->     i-->0    (high postfix precedence)
         boolean v,w=q-p==4|s-r==4;for(i=n;i-->0;d[b[i]]=i,e[a[i]]++)w&=a[i]<2;v=d[5]>0,t=e[2]==1;   // same as before
         for(boolean h:new boolean[]{w&v&s-r<5,w&v,c[4]>0,e[3]==1&t,v,w,c[3]>0,e[2]==2,t}){if(h)break;n--;}
         return n; //  ^ removed space
}

"t cannot be resolved to a variable"

Also -5 chars:


//int n=99,i=-2,j,p=n,q=-p,r=p,s=q,y[][]=new int[5][n],a[]=y[0],b[]=y[1],c[]=y[2],d[]=y[3],e[]=y[4];
  int n=99,i=-2,j,p=n,q=-p,r=p,s=q,y[][]=new int[5][n];int[]a=y[0],b=y[1],c=y[2],d=y[3],e=y[4];

Edit: Also replace any of those a,b,c,d,e that have less than 3 usages (looks to be b and d) with y[q], where q is the number it would’ve been. Each declaration is 7 chars (except the last one, no comma), each substitution adds only 3. Should save another 8 chars, if I count right.

This truly is the devil’s work :stuck_out_tongue:

Great! Reused ‘i’ as cache:


	int h(char[]g){
		int n=99,i=-2,j,p=n,q=-p,r=p,s=q,y[][]=new int[5][n];int[]a=y[0],b=y[1],c=y[2],d=y[3],e=y[4];
      for(;i<13;a[" A23456789TJQK".indexOf(g[i+=2])]++,b[g[++i]]++);
      for(i=n;i-->0;c[a[i]]=i)if(a[i]>0){p=p<i?p:i;q=q>i?q:i;j=i==1?14:i;r=r<j?r:j;s=s>j?s:j;}
      boolean v,w=q-p==4|s-r==4;for(i=n;i-->0;d[b[i]]=i,e[a[i]]++)w&=a[i]<2;v=d[5]>0;i=e[2];
      for(boolean h:new boolean[]{w&v&s-r<5,w&v,c[4]>0,e[3]==1&i==1,v,w,c[3]>0,i==2,i==1}){if(h)break;n--;}
      return n;
	}

Code size: 455 bytes!

You remove 2x 7 bytes (14)
Then you add 4x 3 bytes (12)
Net reduction of 2, not 8, right?


	int h(char[]g){
		int n=99,i=-2,j,p=n,q=-p,r=p,s=q,y[][]=new int[5][n];int[]a=y[0],c=y[2],e=y[4];
      for(;i<13;a[" A23456789TJQK".indexOf(g[i+=2])]++,y[1][g[++i]]++);
      for(i=n;i-->0;c[a[i]]=i)if(a[i]>0){p=p<i?p:i;q=q>i?q:i;j=i==1?14:i;r=r<j?r:j;s=s>j?s:j;}
      boolean v,w=q-p==4|s-r==4;for(i=n;i-->0;y[3][y[1][i]]=i,e[a[i]]++)w&=a[i]<2;v=y[3][5]>0;i=e[2];
      for(boolean h:new boolean[]{w&v&s-r<5,w&v,c[4]>0,e[3]==1&i==1,v,w,c[3]>0,i==2,i==1}){if(h)break;n--;}
      return n;
	}

Code size: 453 bytes

Yep, that’s enough brain cells for today…

Finally! A logic optimization… ;D


... i=e[2] ....
... e[3]==1&i==1 ...

basically said:


... (e[3]==1)&(e[2]==1) ... // full house: (a set of 3) and (a set of 2)

You can never have 2+ sets of 3-of-a-kind (as that would require 6+ out of 5 cards), so e[3] is either 0 or 1.
You can never have 3+ sets of 2-of-a-kind (for the same reason), so e[2] is either 0, 1 or 2.

Possibility table:

[tr][td][/td][td]0x 3-of-a-kind[/td][td]1x 3-of-a-kind[/td][/tr]
[tr][td]0x pair[/td][td]high card[/td][td]3 of a kind[/td][/tr]
[tr][td]1x pair[/td][td]one pair[/td][td]full house[/td][/tr]
[tr][td]2x pair[/td][td]two pairs[/td]td[/td][/tr]

Result table:

[tr][td][/td][td]0x 3-of-a-kind[/td][td]1x 3-of-a-kind[/td][/tr]
[tr][td]0x pair[/td][td]0&0 = 0[/td][td]0&1 = 0[/td][/tr]
[tr][td]1x pair[/td][td]1&0 = 0[/td][td]1&1 = 1[/td][/tr]
[tr][td]2x pair[/td][td]2&0 = 0[/td][td]2&0 = 0[/td][/tr]

This means we can safely check for a full house like so: icode>0[/icode], which means we can replace [icode]e[3]==1&i==1[/icode] with icode>0[/icode], shaving another 2 bytes off, further I changed [icode]i==1?14:i[/icode] to [icode]i<2:14:i[/icode] because i can never be zero or negative (with valid input) - which saved a 3rd byte, as after [icode]i=e[2][/icode] can never be higher than 2, i can replace [icode]i==2[/icode] with [icode]i>1[/icode] in the array of booleans. The last value in the array [icode]i==1[/icode] can only be reached if [icode]i>1[/icode] is false, so [icode]i==1[/icode] can be replaced with [icode]i>0[/icode], still yielding true iff i==1.


	int h(char[]g){
		int n=99,i=-2,j,p=n,q=-p,r=p,s=q,y[][]=new int[5][n];int[]a=y[0],c=y[2],e=y[4];
      for(;i<13;a[" A23456789TJQK".indexOf(g[i+=2])]++,y[1][g[++i]]++);
      for(i=n;i-->0;c[a[i]]=i)if(a[i]>0){p=p<i?p:i;q=q>i?q:i;j=i<2?14:i;r=r<j?r:j;s=s>j?s:j;}
      boolean v,w=q-p==4|s-r==4;for(i=n;i-->0;y[3][y[1][i]]=i,e[a[i]]++)w&=a[i]<2;v=y[3][5]>0;i=e[2];
      for(boolean h:new boolean[]{w&v&s-r<5,w&v,c[4]>0,(e[3]&i)>0,v,w,c[3]>0,i>1,i>0}){if(h)break;n--;}
      return n;
	}

Code size: 448

Riven you’re hurting my head…

Ah, well done, I haven’t fully comprehended your algorithm yet, so I’ve only been making syntactic optimizations.

Oh, I just make histograms for suits and ranks, transpose them, and compose new histograms from those :slight_smile: … that’s what happens here [icode]y[1][g[++i]]++[/icode] and here [icode]y[3][y[1][i]]=i[/icode] :persecutioncomplex:

The straight was the hardest to calculate, due to the aces, so I kept two min/max values for its dual role:
Range A…K: [icode] p=p<i?p:i; q=q>i?q:i;[/icode]
Range 2…A: [icode]j=i<2?14:i; r=r<j?r:j; s=s>j?s:j;[/icode]
There is a simple check whether there can be a straight:
[icode]w=q-p==4|s-r==4;[/icode] (max distance between min and max is 5 cards -1)
Then I check that there aren’t any pairs (or 2,3,4 of a kinds), which would leave holes in the range:
[icode]for(…)w&=a[i]<2;[/icode] I piggy-back on a loop that didn’t have a body.

I started with beautiful code, that was, shall we say… maintainable, madness ensued.

When I said I was done killing cells, I lied.

'Nother 12 chars, but throws exception (still outputs correctly otherwise):


//for(boolean h:new boolean[]{w&v&s-r<5,w&v,c[4]>0,(e[3]&i)>0,v,w,c[3]>0,i>1,i>0}){if(h)break;n--;}
  for(j=0;!new boolean[]{w&v&s-r<5,w&v,c[4]>0,(e[3]&i)>0,v,w,c[3]>0,i>1,i>0}[j++];n--);