# Word Poker, Who Wins?

The input will be two five letter words. They don't actually have to be dictionary words, just any five letters each, all lowercase or all uppercase, your choice. Only A-Z will appear in the input words and they will always be 5 characters in length.

Your program is to score them both as though they were poker hands and output the higher hand. Of course suits won't apply here, only rankings so there's no flushes.

The typical poker ranking system goes: '1 pair', '2 pairs', '3 of a kind', 'straight', 'full house', '4 of a kind', '5 of a kind', and of course there is the possibility the hand (or word in this case) could be worth nothing.

In the case of ties, letters closer to A are considered higher, so a pair of As beats a pair of Bs. In some cases both hands might be identical, but in a different order (or not), in that case output either hand or a resorted version of it.

This external page contains information about how to identify the winner and especially addresses ties within the specific rankings, in case you aren't familiar with how to score poker hands.

In the case of straights: the letters must be adjacent in the alphabet and are not allowed to wrap around. So 'defgh' in any order is a straight, 'xyzab' is not.

Examples of how to score a single hand:

word   | scored as
---------------------
ccccc  | 5 of a kind     <-- highest ranking
woooo  | 4 of a kind
opopo  | full house
vurst  | straight
vovvu  | 3 of a kind
ppoww  | 2 pairs
upper  | 1 pair
kjsdf  | high card only (in this case D) <-- lowest ranking


So the program will actually produce results like this:

input        |  output
-----------------------
voviu,kjsdf  |  voviu     because a pair beats nothing
opoqo,upper  |  opoqo     because 3 of a kind beats a pair
woooo,ggegg  |  ggegg     because 4 Gs beats 4 Os
queue,hopup  |  queue     because 2 pairs beats 1 pair
lodpl,ddkop  |  ddkop     because pair DD beats pair LL
huhyg,hijht  |  huhyg     both have pair HH, but G beats I
ddffh,ccyyz  |  ccyyz     both have 2 pairs, but CC(yyz) beats DD(ffh)
okaok,nkunk  |  nkunk     KK ties with KK, but NN beats OO
abcdf,bcdef  |  bcdef     because it is a straight
qtery,retyq  |  qtery     identical! so doesnt matter
abedc,vyxwz  |  abedc     because it is a "higher" straight
hhhij,hijkl  |  hijkl     because straight beats 3 of a kind
aaabb,zzzzz  |  zzzzz     because nothing beats 5 of a kind


The order of the letters in both the input and output are irrelevant, so the order in your output can be different than the input, but the same inventory of letters needs to be present.

The output must contain exactly five letters -- no more, no less.

The usual codegolf rules apply. Shortest code wins.

## JavaScript (224218 213 bytes)

s=>t=>(v=s=>(o={},l=n=0,z=3.5,[...s].sort().map(c=>(n+=o[c]=-~o[c],z*=!l|l+1==(l=c.charCodeAt()))),[n+z,Object.keys(o).sort((a,b)=>o[b]-o[a]||(o[b]>o[a]?1:-1))]),w=v(s),x=v(t),w[0]>x[0]||w[0]==x[0]&&w[1]<x[1]?s:t)


Ungolfed:

s=>t=>(
v=s=>(
o={},
l=n=0,
z=3.5,
[...s].sort().map(c=>(
n+=o[c]=-~o[c],
z*=!l|l+1==(l=c.charCodeAt())
)),
[n+z,Object.keys(o).sort((a,b)=>o[b]-o[a]||(o[b]>o[a]?1:-1))]
),
w=v(s),x=v(t),
w[0]>x[0] || w[0]==x[0] && w[1]<x[1] ? s : t
)


After map() runs, n + z determines the ranking of a hand:

(You can understand why I initialized z to 3.5.)

In case of a tie, Object.keys(o).sort() is used to determine the higher ranking hand.

Snippet:

f=

s=>t=>(v=s=>(o={},l=n=0,z=3.5,[...s].sort().map(c=>(n+=o[c]=-~o[c],z*=!l|l+1==(l=c.charCodeAt()))),[n+z,Object.keys(o).sort((a,b)=>o[b]-o[a]||(o[b]>o[a]?1:-1))]),w=v(s),x=v(t),w[0]>x[0]||w[0]==x[0]&&w[1]<x[1]?s:t)

console.log(/voviu/.test(f('voviu')('kjsdf')))       //because a pair beats nothing
console.log(/opoqo/.test(f('opoqo')('upper')))       //because 3 of a kind beats a pair
console.log(/ggegg/.test(f('woooo')('ggegg')))       //because 4 Gs beats 4 Os
console.log(/queue/.test(f('queue')('hopup')))       //because 2 pairs beats 1 pair
console.log(/ddkop/.test(f('lodpl')('ddkop')))       //because pair DD beats pair LL
console.log(/huhyg/.test(f('huhyg')('hijht')))       //both have pair HH, but G beats I
console.log(/ccyyz/.test(f('ddffh')('ccyyz')))       //both have 2 pairs, but CC(yyz) beats DD(ffh)
console.log(/nkunk/.test(f('okaok')('nkunk')))       //KK ties with KK, but NN beats OO
console.log(/bcdef/.test(f('abcdf')('bcdef')))       //because it is a straight
console.log(/qtery|retyq/.test(f('qtery')('retyq'))) //identical! so doesnt matter
console.log(/abedc/.test(f('abedc')('vyxwz')))       //because it is a "higher" straight
console.log(/hijkl/.test(f('hhhij')('hijkl')))       //because straight beats 3 of a kind
console.log(/zzzzz/.test(f('aaabb')('zzzzz')))       //because nothing beats 5 of a kind

# Jelly,  28 27 29  27 bytes

+2 & then -2 fix a bug, then re-golf.

FI=1ȦḤW
OµNĠLÞṚịµL€+Ç,N
ÇÞṪ


A monadic link taking a list of "hands" and returning (one of) the winners.

Works for all-uppercase OR all-lowercase input.
(...but not mixed, for that prepend the final line with Œl or Œu).

Try it online! or see the test suite.

### How?

FI=1ȦḤW - Link 1, straight offset: grouped and reverse sorted hand ordinals
-                     e.g. [[-101],[-100],[-99],[-98],[-97]]
F       - flatten                  [-101,-100,-99,-98,-97]
I      - increments               [1,1,1,1]
=1    - equal 1? (vectorises)    [1,1,1,1]
Ȧ   - any and all?             1
Ḥ  - double                   2
W - wrap in a list           [2]
-   The purpose of this is so that when "a" from Link 2 represents a straight we
-   get [2], whereas for any other hand we get [0]. Adding the [2] to [1,1,1,1,1]
-   (the lengths of a straight's groups) yields [3,1,1,1,1], placing it between
-   three of a kind, [3,1,1], and a full house, [3,2], as required.

OµNĠLÞṚịµL€+Ç,N - Link 2, hand rank key function: list of characters       e.g. "huhyg"
O               - cast to ordinals                                [104,117,104,121,103]
µ              - monadic chain separation, call that o
N             - negate (to give them a reverse-sort order) [-104,-117,-104,-121,-103]
Ġ            - group indices by value                            [[4],[2],[1,3],[5]]
Þ          - sort by key function:
L           -   length                                          [[4],[2],[5],[1,3]]
Ṛ         - reverse                                           [[1,3],[5],[2],[4]]
ị        - index into o                       [[-104,-104],[-103],[-117],[-121]]
µ       - monadic chain separation (call that a)
L€     - length of €ach                                              [2,1,1,1]
Ç   - call last link (2) as a monad -> [isStraight? * 2]                [0]
N - negate o                                [[104,104],[103],[117],[121]]
,  - pair                        [[2,1,1,1],[[104,104],[103],[117],[121]]]
-   now sorting by this will first be comparing the hand class, and if
-   and only if they match comparing the card values in the required order.

ÇÞḢ - Main link: list of lists of characters (list of hands)
Þ  - sort by key function:
Ṫ - tail (best or an equal-best hand)

• So freaking short compared to what I'm making in JS 0.o – Stephen May 25 '17 at 22:45
• @StephenS Welcome to PPCG, where you make something in some non-golfing language and then someone makes something in Jelly, 05AB1E, Pyth, CJam, etc that's shorter than your language name :I :P – HyperNeutrino May 25 '17 at 22:48
• @StephenS - JS should compete with JS. Do not let golfing languages deter you from submitting well thought out solutions in other languages! – Jonathan Allan May 26 '17 at 3:08
• @JonathanAllan it deters me from putting too much effort into overthinking and abstracting a problem that can be solved in ~25 characters, here's the fiddle I was working on - I wrote all the boilerplate and none of the actual code – Stephen May 26 '17 at 4:26
• This is awesome, but I recently added a test case that this doesn't compute, specifically ["hhhij","hijkl"]. I think it's because of the way you rank a straight as [3,1,1,1,1]? – Octopus May 26 '17 at 20:11

## JavaScript (250247 232 bytes)

S=d=>(x={},l=99,h=s=0,[...d].map(v=>x[v]=-~x[v]),Object.keys(x).map(v=>(c=91-v.charCodeAt(),t=x[v],s+=1e4**t,c<x[t]?0:x[t]=c,g=(h=c>h?c:h)-(l=c<l?c:l))),[5,4,3,2,1].map(v=>s+=0|x[v]**v),s+(s<5e7&&g<5?1e13:0)),C=a=>b=>(S(a)>S(b)?a:b)


### Ungolfed code & test cases in JSFiddle: https://jsfiddle.net/CookieJon/8yq8ow1b/

Saved some bytes with thanks to @RickHitchcock. @StephenS & @Arnauld

• This is what I was trying to make but had no clue how to make. – Stephen May 26 '17 at 4:31
• Nor did I until after I started! :-) – Bumpy May 26 '17 at 4:33
• s=0,h=0 => s=h=0 I believe – Stephen May 26 '17 at 4:34
• Fixed now after much hair-pulling. Determining the tie-breaker in cases where the hand is the same AND the lowest characters in the 1st & 2nd largest groups were the same was the killer (33 bytes or so JUST for that!?) :-( – Bumpy May 26 '17 at 13:29
• x[v]=x[v]?++x[v]:1 can become x[v]=(x[v]|0)+1, saving 3 bytes. – Rick Hitchcock May 26 '17 at 14:20

# Python 2.7, 242 223 bytes

from collections import*
s=sorted
f=lambda x,y:s(map(lambda h:(lambda (r,n):((3,1.5)if len(r)==5 and ord(r[0])+4==ord(r[4])else n,[-ord(d) for d in r],h))(zip(*s(Counter(h).items(),key=lambda z:(-z[1],z[0])))),(x,y)))[1][2]


Similar in basic concept to the javascript examples (sort by hand strength with an exception for straights; then by rank); but taking advantage of collections.Counter Unfortunately, .most_common doesn't have quite the desired behavior; so had to add a custom sort key.

Edit: a bit more code golfing to trim down 19 bytes.

Un-golfed code

from collections import Counter

def convertHand(h):
# first get item counts, appropriately ordered; e.g. cbabc -> (('b',2), ('c',2),('a',1))
sortedPairs = sorted(Counter(h).items(),key=lambda x:(-x[1],x[0]))

# 'unzip' the tuples to get (('b','c','a'), (2,2,1))
ranks, numberFound = zip(*sortedPairs)

if len(ranks)==5:
# no pairs; is it a straight? well, since they are in increasing order...
if ord(ranks[0])+4 == ord(ranks[4]):
# replace numberFound with something that will sort above 3 of a kind but below full house
numberFound = (3,1.5)

# invert the values of the ranks, so they are in decreasing, rather then increasing order
ranks = [-ord(r) for r in ranks]

# arrange tuples so we can sort by numberFound, and then ranks; and keep a reference to the hand
return (numberFound, ranks, h)

# put it all together...
def f(x,y):
hands = [convertHand(h) for h in (x,y)]
rankedHands = sorted(hands)
return rankedHands[1][2]


## Mathematica, 635 bytes

H[x_]:=Block[{t},T=Sort@ToCharacterCode[x];L=Last/@Tally@T;t=0;S=Count;If[S[L,2]==1,t=1];If[S[L,2]==2,t=2];If[S[L,3]==1,t=3];If[S[Differences@T,1]==4,t=4];If[S[L,2]==1&&S[L,3]==1,t=5];If[S[L,4]==1,t=6];If[S[L,5]==1,t=7];t];F[K_,v_]:=First@Flatten@Cases[Tally@K,{_,v}];B=ToCharacterCode;(Z=Sort@B@#1;Y=Sort@B@#2;a=H[#1];b=H[#2];If[a>b,P@#1,If[a<b,P@#2]]If[a==b&&a==0,If[Z[[1]]<Y[[1]],P@#1,P@#2]];If[a==b&&(a==1||a==2),If[F[Z,2]<F[Y,2],P@#1,If[F[Z,2]==F[Y,2],If[F[Z,1]<F[Y,1],P@#1,P@#2],P@#2]]];If[a==b&&(a==3||a==5),If[F[Z,3]<F[Y,3],P@#1,P@#2]];If[a==b&&a==6,If[F[Z,4]<F[Y,4],P@#1,P@#2]];If[a==b&&(a==7||a==4),If[Tr@Z<Tr@Y,P@#1,P@#2]])&


.
.
Input form

["abcde", "kkekk"]

• Is there a way to test this online? – Octopus May 26 '17 at 3:31
• sandbox.open.wolframcloud.com/app/objects paste with ctrl+v add the input at the end of the code and run with shift+enter – J42161217 May 26 '17 at 3:51