language agnostic - Code Golf: ¿Quién tiene la mejor mano de poker?
language-agnostic code-golf (6)
GolfScript - 151/187 caracteres
Este programa funciona en una lista de entrada de 10 tarjetas por línea, es decir, dos manos de 5 tarjetas.
n%0.@{3/5/{[zip~;.&,(!15*/[{n),*"TJQKA"+?}/]:|$),-4>=14*+1|{.2/?|@-,5/-.49?@*@+/.+@+/}/.16445=13*@+/]}%.~={0):0;;}{~>.!@+/@+/}if}/"1: "@@n"2: "@n"D: "0
Este programa funciona en una lista de entrada de 9 tarjetas por línea, del formato descrito en las especificaciones.
n%0.@{3/.4>:D;2/2<{D+.{3/1$^.{3/1$^[zip~;.&,(!15*/[{n),*"TJQKA"+?}/]$:|),-4>=14*+1|{.2/?|@-,5/-.49?@*@+/.+@+/}/.16445=13*@+/]}%/;~}%$-1=/;}%.~={0):0;/(/}*~>.!@+/@+/}/"1: "@@n"2: "@n"D: "0
Me encantan los desafíos como este, espero que pronto envíe mi respuesta.
¿Qué jugador tiene la mejor mano de 7 cartas?
Dada una lista desordenada de 9 cartas (separadas por un espacio), determine qué jugador tiene la mejor mano de póquer. Aquí hay una lista de los rankings de manos de poker . Ejemplo de entrada:
2C 5H AS KS 2D 4D QD KH 3S
(ie: [[2C 5H] [AS KS] [2D 4D QD KH 3S]])
Las primeras 2 cartas en la matriz representan la mano del jugador 1, las segundas 2 en la matriz representan la mano del jugador 2. Las últimas 5 cartas representan las cartas comunitarias, cartas que ambos jugadores comparten. En efecto, ambos jugadores tienen 7 cartas, y usted debe determinar qué jugador tiene la mejor mano de póquer de 5 cartas.
Una carta se define como una cadena, donde el primer carácter representa el valor de la carta y el segundo valor representa la demanda. Siempre en mayúsculas. Ninguna carta puede aparecer dos veces.
La función calculará si la mano es un empate o una victoria para cualquiera de los jugadores. Obtendrá los totales al final de la entrada. El formato de salida se define más adelante en esta publicación.
Ejemplos
2C 5H AS KS 2D 4D QD KH 3S
(ie: [[2C 5H] [AS KS] [2D 4D QD KH 3S]])
Player 2 wins this hand. Player 1 has a pair of 2''s, player 2 has a pair of kings.
5S 6S 8H 9D 7S 8S JH TS 2H
(ie: [[5S 6S] [8H 9D] [7S 8S JH TS 2H]])
Player 1 wins this hand Player 1 has a flush, player 2 has a straight.
2S 2H AC AS 2C AH 9H TS 2D
(ie: [[2S 2H] [AC AS] [2C AH 9H TS 2D]])
Player 1 wins this hand. Player 1 has quads, player 2 has a full house
5S 6S 2D 4D 9S AS KD JC 9D
(ie: [[5S 6S] [2D 4D] [9S AS KD JC 9D]])
A draw. Both players have Ace high.
Más información
Gracias a mgroves por el siguiente enlace al Proyecto Euler, que tiene un problema similar: http://projecteuler.net/index.php?section=problems&id=54
Datos de prueba
Utilizaremos los datos de prueba del Proyecto Euler:
http://projecteuler.net/project/poker.txt
Su solución debe aceptar ese archivo de texto como entrada y generar un total de ganancias y sorteos.
Ejemplo de salida
La salida debe estar en este formato:
1: 45
2: 32
D: 12
El jugador 1 ganó 45 manos, el jugador 2 ganó 32 manos y hubo 12 empates. (No resultados reales)
Reglas
- No tiene que devolver el tipo de mano ganadora, solo la OMS ganó si alguien
- La entrada de la lista de tarjetas no tiene un orden particular
- Ninguna carta aparece dos veces en la entrada
- La entrada es siempre mayúscula
- Toma los datos de prueba del Proyecto Euler como entrada
- Produce un conteo, de los cuales el jugador ganó la mayor cantidad de manos y el total de los sorteos en el formato dado arriba
Haskell: 793 796 806 826 864 904 901 880 863
Dado que el archivo de texto es inconsistente con las 9 manos de la tarjeta, solo estoy leyendo una línea desde la consola y obteniendo quién gana.
Corrección de errores:
- Ace ahora cuenta con menos de 2 en una carrera de bajo as.
- Comparando casas completas fijas (de nuevo: D).
- Garantiza que se elija la mejor versión de un tipo de mano dado. Por ejemplo, si un jugador puede elegir entre una carrera de 2-6 y una carrera de 3-7, se elige la carrera de 3-7 (descarga a un lado).
- ¡Ahora más corto que la solución PHP!
Jugó al golf
import Data.List
(%)=mod
m=map
y=foldr1
t=0<1
z=13
w=[0,1,2,3,12]
n&x|length x<n=[]|t=take n x
b?x|b=x|t=[]
n!k= /c->e(n&m(%k)c)?(n&c)
e[]=1<1
e(x:y)=all(x==)y
k q c|any null[q c,p$c//q c]=[]|t=q c
f=5!4
s c=(sort(m(%z)c)`elem`w:[[n..n+4]|n<-[0..8]])?c
r=3!z
p=2!z
g x y|c x y<2=x|t=y
q x(_,[])=x
q _ y=y
b h=y q$m($h)$zipWith(/t f->(,)t.y g.m(f.take 5).permutations)[1..][1!1,p,k p,r,s,f,k r,4!z,s.f]
h=reverse.a.m(%z)
a v|w//v==[]=[-1..3]|t=sort v
c x y=o(h x)$h y
o[](_:_)=2
o[]_=0
o _[]=1
o(a:b)(k:d)|a>k=1|a<k=2|t=o b d
d n(a,k)|a==[]=0|n<1=0|r>s=1|r<s=2|f/=0=f|t=d(n-length o)(a//o,k//u)where(r,o)=b a;(s,u)=b k;f=c o u
i x=head.findIndices(x==)
u(n:k)c@[r,s]|n%z==i r"23456789TJQKA"&&n%4==i s"HDSC"=n|t=u k c
l c=(2&c++snd(splitAt 4c),drop 2c)
main=getLine>>=print.d 5.l.m(u[0..]).words
Ungolfed
import Control.Exception (assert)
import Data.List (permutations, sort, intersect, findIndices, (//))
import Data.Function (on)
(%) = mod
aceLowRun = [0,1,2,3,12]
tryTake n xs
| length xs < n = []
| otherwise = take n xs
cond ? xs
| cond = xs
| otherwise = []
eqOn n f cards = allEq (tryTake n $ map f cards) ? tryTake n cards
allEq [] = False
allEq (x:xs) = all (== x) xs
combWithPair pokerHand cards
| any null [picked1, picked2] = []
| otherwise = pokerHand cards
where
picked1 = pokerHand cards
picked2 = pair $ cards // picked1
straightFlush = straight . flush
quads = eqOn 4 (% 13)
fullHouse = combWithPair triples
flush = eqOn 5 (% 4)
straight cards = (sort (map (% 13) cards) `elem` runs) ? cards
where
runs = aceLowRun : [[n..n+4] | n <- [0..8]]
triples = eqOn 3 (% 13)
twoPair = combWithPair pair
pair = eqOn 2 (% 13)
single = eqOn 1 id
bestVersionOfHand [] ys = ys
bestVersionOfHand xs [] = xs
bestVersionOfHand xs ys
| compareSameRankedHands xs ys < 2 = xs
| otherwise = ys
rate rating pokerHand cards = (rating, handResult)
where
handResult = foldr1 bestVersionOfHand
(map (pokerHand . take 5) $ permutations cards)
pokerHands = zipWith rate [1..] [
single
, pair
, twoPair
, triples
, straight
, flush
, fullHouse
, quads
, straightFlush
]
bestHand hand = foldr1 (/xs ys -> if null (snd ys) then xs else ys)
(map ($ hand) pokerHands)
highestVals = reverse . arrangeVals . map (% 13)
where
arrangeVals vals = if vals `intersect` aceLowRun == aceLowRun
then [-1..3]
else sort vals
compareSameRankedHands = compareSameRankedHands'' `on` highestVals
compareSameRankedHands'' [] [] = 0
compareSameRankedHands'' (card1:cards1) (card2:cards2)
| card1 > card2 = 1
| card1 < card2 = 2
| otherwise = compareSameRankedHands'' cards1 cards2
decideWinner n cards1 cards2
| null cards1 = assert (null cards2) 0
| n < 1 = 0
| rating1 > rating2 = 1
| rating1 < rating2 = 2
| cmpRes /= 0 = cmpRes
| otherwise = decideWinner
(n - assert (length bests1 == length bests2) (length bests1))
(cards1 // bests1)
(cards2 // bests2)
where
(rating1, bests1) = bestHand cards1
(rating2, bests2) = bestHand cards2
cmpRes = compareSameRankedHands bests1 bests2
indexOf x = head . findIndices (x==)
toNum = toNum'' [0..]
toNum'' (n:ns) [rank, suit]
| n % 13 == indexOf rank "23456789TJQKA" && n % 4 == indexOf suit "HDSC" = n
| otherwise = toNum'' ns [rank, suit]
cluster cards = (take 2 cards ++ snd (splitAt 4 cards), drop 2 cards)
main = getLine >>= print
. uncurry (decideWinner 5)
. cluster
. map toNum
. words
GolfScript 258 241 247/341 217/299 char
Solución para el problema de 10 tarjetas. Sólo las últimas dos nuevas líneas son significativas:
10:T):J):Q):K):A;0:a;0:b;0:d;"/r/n"%{'' ''/5/{.{)/;}/4*-+++!:f;{);~}%{$0:z(%{.z-
!99*+:z}%}5*.{+}*99/:P!{..)/(@4+-!2*/;/.2<~9+-!/;+}and:s;[s f*6P=4P=f s P 6$]/;}
%.~={;;d):d;}{~>{a):a;}{b):b;}if}if}/
''1: ''a''
2: ''b''
D: ''d n
El problema de las 9 tarjetas actualmente necesita unos 80 caracteres más.
10:T):J):Q):K):A;0:a;0:b;0:d;"/r/n"%{'' ''/);{(''Z''%+}2*[0$2>/7<]
{:H;7,{H=:x;H{x=!},:I;6,{I=:x;I{x=!},}/}%{.{)/;}/4*-+++!:f;
{);~}%{$0:z(%{.z-!99*+:z}%}5*.{+}*99/:P!{..)/(@4+-!2*/;/.2<~9+-!/;+}and:s;[
s f*6P=4P=f s P 6$]/;}%{[/].~>{~;}{~/;}if}*}%.~={;;d):d;}{~>{a):a;}{b):b;}if}if}/
''1: ''a''
2: ''b''
D: ''d n
Versión menos golfeada del problema de 10 cartas.
10:T;11:J;12:Q;13:K;14:A; # map for face cards
0:a;0:b;0:d; # other initialization
"/r/n"% # split input on /n
{ # on each line of input
'' ''/ # divide line into ten cards
5/ # split into five card hands
{. # on each of the two hands
{)/;}% # chop last character of each card
.(5*/;/{+}*= # check sum of elem == 5*1st elem
:f; # this is the flush flag
{);~}%$ # reduce cards to numerical values
0:z;{.z- 20%{}
{;z 20+}if:z}%{-1*}$ # detect pairs
.(:h;; # extract value of highest card
20h>{..)/(@4+-!2*/;/ # detect straight
.2<~9+-!/;+}and:s; # s=2 for regular straight, s=1 for A-5 straight
# result of this mapping - 6 elem array
[ 0$ # #6 - cards in the hand
.{20/}%{+}*:P # #5 - number of pairs
s # #4 - is this a straight?
f # #3 - is this a flush?
4P= # #2b - is this a full house?
h 59> # #2 - is this 4 of a kind?
s f * # #1 - is this a straight flush?
]-1%
/;
}/
/.@.@ # put [hand1 hand2 hand1 hand2] on stack
= # check hand1==hand2
{;;d):d;} # if equal, increment d (draw)
{>{a):a;} # if >, increment a (player 1 wins)
{b):b;}if # if <, increment b (player 2 wins)
}if
}/
# output results
''1: ''a''
2: ''b''
D: ''d n
PHP, 799 caracteres
Los saltos de línea no son significativos. Esto toma la entrada de la url vinculada, que es diferente de la entrada del ejemplo (no se trata de tarjetas comunitarias). El procesamiento es similar a la respuesta perl de mobrule, con un método de puntuación diferente.
<?php
function s($i){$o=array_map(''intval'',$i);$f=(count(array_unique(str_replace($o,'''',$i)))==1);
sort($o);$v=array_count_values($o);arsort($v);$u=array_keys($v);$h=max($u);$m=$u[0];$c=reset($v);
$p=count($v);$e=$c==1&&$o[4]==14&&$o[3]==5;$r=$o==range($o[0],$o[0]+4)||$e;$q=$e?5:$h;
$s=($f&&$r&&($h==12)?2<<11:($f&&$r?(2<<10)+$q:0))+($c==4?(2<<9)+$m:0)+($c==3&&$p==2?(2<<8)+$m:0)+($f?(2<<7)+$h:0)+
($r?(2<<6)+$q:0)+($c==3?(2<<5)+$m:0)+($c==2&&$p==3?(2<<4)+$m:0)+($p==4?(2<<3)+$m:0);$s+=!$s?$h:0;return array($s,$u);}
foreach(file($argv[1]) as $d){
list($y,$z)=array_chunk(explode('' '',trim(strtr($d,array(''T''=>10,''J''=>11,''Q''=>12,''K''=>13,''A''=>14)))),5);
$y=s($y);$z=s($z);$w=$y[0]-$z[0];$x=1;while(!$w&&$x<5){$w=$y[1][$x]-$z[1][$x++];}if(!$w)@$t++;elseif($w<0)@$l++;else @$k++;}
@print "1: $k/n2: $l/nD: $t";
Perl, 414 398 370/458 344/416 char
Los saltos de línea no son significativos.
%M=map{$_,$Z++}0..9,T,J,Q,K,A;sub N{/.$/;$M{$`}.$&}
sub B{$s=@p=();
for$m(@_){$m-$_||($s+=2,++$p[$m])for@_}
@_=sort{$p[$b]-$p[$a]||$b-$a}@_;
$s=23 if$s<11&&($_[0]-$_[4]<5||$_[0]-$_[1]>8&&push@_,shift);
"@_"=~/.$/;$s+=14*(4<grep/$&/,@_);
$s=100*$s+$_ for@_;$s}
++$X{B((@c=map{N}split)[0..4])<=>B(@c[5..9])}for<>;
printf"1: %d/n2: %d/nD: %d/n",@X{1,-1,0}
Esto resuelve el problema de "10 cartas" (se reparten 10 cartas, el jugador 1 tiene las primeras 5 cartas y el jugador 2 tiene las segundas 5 cartas).
La primera sección define una subrutina N
que puede transformar cada tarjeta para que tenga un valor numérico. Para las tarjetas que no son de cara, este es un mapeo trivial (5H ==> 5H) pero transforma las tarjetas de cara (KC => 13C, AD => 14D).
La última sección analiza cada línea de entrada en tarjetas, transforma las tarjetas para que contengan valores numéricos, divide las tarjetas en manos separadas para los dos jugadores, y analiza y compara esas manos. Cada mano incrementa un elemento del hash %X
Cuando se analiza toda la entrada, %X
contiene el número de manos ganadas por el jugador 1, ganadas por el jugador 2 o empates.
La sección central es una subrutina que toma un conjunto de cinco cartas como entrada y produce un número de 12 dígitos con la propiedad de que las manos de póquer más fuertes tendrán números de mayor valor. Así es como funciona:
for$m(@_){$m-$_||($s+=2,++$p[$m])for@_}
Este es el detector "par". Si cualquiera de las dos tarjetas tiene el mismo valor numérico, incremente un elemento hash para una de las tarjetas y aumente la variable de "puntuación" $s
en dos. Tenga en cuenta que terminaremos comparando cada tarjeta a sí mismo, por lo que $s
será al menos 10 y $p[$x]
será al menos uno por cada tarjeta $x
. Si la mano contiene tres de una clase, entonces esas tres cartas coincidirán con las otras dos cartas; será como si hubieran 9 partidos entre esas tres cartas y la "puntuación" sea al menos 18.
@_=sort{$p[$b]-$p[$a]||$b-$a}@_;
Ordene las tarjetas por (1) el número de veces que esa tarjeta es parte de un "par" y (2) el valor de la tarjeta. Así, en una mano con dos 7 y dos 3, los dos 7 aparecerán primero, seguidos de los dos 3, seguidos por el pateador. En una mano con dos 7 y tres 3, los tres 3 serán seguidos primero por los dos 7. El objetivo de este ordenamiento es distinguir dos manos que tienen la misma puntuación: una mano con un par de 8 y una mano con un par de 7, ambas tienen un par, pero debemos poder decir que un par de 8 es mejor.
$s=23 if$s<11&&($_[0]-$_[4]<5||$_[0]-$_[1]>8&&push@_,shift);
Esta línea es el detector "recto". Una recta vale 23 puntos y se produce cuando no hay pares en la mano ( $s<11
significa solo 5 "pares" - se encontró cada tarjeta que coincide con ella misma) y (1) el valor de la tarjeta más alta es exactamente cuatro más que el valor de la carta más baja ( $_[0]-$_[4]==4
), o (2) la carta de mayor valor es un As y la siguiente carta más alta es un 5 ( $_[0]-$_[1]==9
), lo que significa que la mano tiene una recta A-2-3-4-5. En el último caso, el As es ahora la carta menos valiosa en la mano, por lo que manipulamos @_
para reflejar eso ( push@_,shift
)
"@_"=~/.$/;$s+=14*(4<grep/$&/,@_);
Esta línea es el detector de descarga. Un color vale 14 puntos más y ocurre cuando el último personaje es el mismo para cada carta. La primera expresión ( "@_"=~/.$/
) tiene el efecto secundario de establecer $&
el último carácter (el palo) de la última carta en la mano. La expresión final ( 4<grep/$&/,@_
) será verdadera si y solo si todos los elementos de @_
tienen el mismo último carácter.
$s=100*$s+$_ for@_;$s}
Crea y devuelve un valor que comienza con el puntaje de la mano y luego contiene los valores de las cartas, en orden de importancia de la carta. Las puntuaciones para las diferentes manos serán
Hand Score
---------- ------
High card 10 (each card matches itself for two points)
One pair 14 (2 additional matches)
Two pair 18 (4 additional matches)
Three of a kind 22 (6 additional matches)
Straight 23 (no pair, but 23 points for straight)
Flush 24 (no pair, but 14 additional points for the flush)
Full house 26 (8 additional matches)
4 of a kind 34 (12 additional matches)
Straight flush 37 (23 + 14 points)
que es consistente con las reglas del poker. Las manos con la misma puntuación se pueden distinguir por los valores de las cartas de la mano, en orden de importancia para la mano, hasta la carta menos valiosa de la mano.
La solución al problema de las 9 cartas (dos cartas para el jugador 1, dos cartas para el jugador 2, los jugadores comparten las siguientes 5 cartas y construyen su mejor mano de 5 cartas) necesita unos 70 golpes más para elegir la mejor mano de 5 cartas del 7 cartas disponibles para cada jugador:
%M=map{$_,$Z++}0..9,T,J,Q,K,A;sub N{/./;$M{$&}.$''}
sub A{my$I;
for$k(0..41){@d=@_;splice@d,$_,1for$k%7,$k/7;$s=@p=();
for$m(grep$_=N,@d){$m-$_||($s+=2,$p[$m]++)for@d}
@d=sort{$p[$b]-$p[$a]||$b-$a}@d;
$s=23 if$s<11&&($d[0]-$d[4]<5||$d[0]-$d[1]>8&&push@d,shift@d);
"@d"=~/.$/;$s+=14*(4<grep/$&/,@d);
$s=100*$s+$_ for@d;
$I=$s if$s>$I}$I}
++$X{A((@c=split)[0,1,4..8])<=>A(@c[2..8])}for<>;
printf"1: %d/n2: %d/nD: %d/n",@X{1,-1,0}
C, 665 + 379 caracteres
Aquí está mi respuesta en 2 partes.
El primero es un evaluador de 7 tarjetas completo, que incluye la macro A
"AddCard". Devuelve un número de 32 bits que clasifica la mano. El mordisco alto es el tipo, los bits 13..25 indican la (s) tarjeta (s) alta (s) y los bits 0..12 indican el kicker (s). Al comparar los resultados, la mejor mano siempre tendrá el valor más grande.
#define U unsigned
#define c(a)X=a;i=C=0;while(X){C|=(X&1)<<i++;X/=4;}
#define A(h,c)h[c&7]+=c,h[3]|=c
U C,i,X;
U E(U h[]){
U a=h[0]+h[1]+h[2]+h[4]-(h[3]&-16),t,v,k,e=a&0x55555540,o=a&0xAAAAAA80;
if(v=e&o/2){t=7;k=h[3]^v;i=0;while(k/=4)i++;k=1<<2*i;}
else if(v=o&o-1){t=6;v/=2;k=o/2^v;}
else if(e>1&o>1){t=6;v=o/2;k=(i=e&e-1)?i:e;}
else{a=h[3];
if(t=h[i=1]-(a&1)&4||h[i=2]-(a&2)&8||h[i=4]-(a&4)&16||h[i=0]-(a&8)&32)a=h[i];
a&=-64;v=a|a>>26&16;t*=5;
if(v=v&v<<2&v<<4&v<<6&v<<8){t+=4;a=v&=~(v/2);}
else if(t)for(i=(h[i]&63)/(i?i:8),v=a;i-->5;)a&=a-1;
else if(v=o/2)t=3;
else if (e){o=e&e-1;v=(i=o&o-1)?o:e;t=1+(o>0);}
k=a^v;k&=k-1;k&=k-(i==0);}
c(v);v=C/8;c(k);
return t<<28|v<<13|C/8;}
El segundo es el procesador de entrada. Analiza el archivo del proyecto Euler como tarjetas 2 + 2 + 5 (ignorando la tarjeta 10). Utiliza la macro Parse, P
para crear valores de 32 bits que representan cada tarjeta. La representación es 0A0K0Q0J0T090807060504030200shdc
. Una mano se almacena como una matriz de 5 pulgadas.
char*gets(char*);char*strchr(char*,char);
#define P(c)X=strchr(R,*c++)-R;C=1<<strchr(S,*c++)-S|64<<X*2;c++;
#define L(n)for(i=0;i<n;i++)
U g[5],h[5];
char*c,b[32];
char*S="CDHS";
char*R="23456789TJQKA";
int d,r[3]={0};
main(q){while(c=gets(b)){
L(2){P(c)A(g,C);}
L(2){P(c)A(h,C);}
L(5){P(c)A(g,C);A(h,C);}
d=E(g)-E(h);
r[d>0?0:d<0?1:2]++;
L(7)g[i]=h[i]=0;
}L(3)printf("%c:%d/n","12D"[i],r[i]);}
Estoy seguro de que hay algunos caracteres más para recortar. Agregaré una explicación pronto.
El evaluador ejecuta @ 17.6 Millones de manos / segundo en mi 3Ghz Core2 Duo. Eso es solo 3.5 veces más lento que el evaluador de PokerSource, que utiliza al menos 56 K de tablas de búsqueda.