Подтвердить что ты не робот

Code Golf: у кого лучшая покерная рука?

Мне нравятся такие проблемы, я надеюсь, скоро отправлю ответ.

Какой игрок имеет лучшую руку с 7 картами?

Учитывая неупорядоченный список из 9 карт (разделенных пробелом), выясните, какой игрок имеет лучшую покерную комбинацию. Вот список рангов в покере. Пример ввода:

2C 5H AS KS 2D 4D QD KH 3S
(ie: [[2C 5H] [AS KS] [2D 4D QD KH 3S]])

Первые 2 карты в массиве представляют игрока 1 руку, вторая 2 в массиве представляет руку игрока 2. Последние 5 карт представляют собой общие карты, которые разделяют обе игроки. По сути, у обоих игроков есть 7 карт, и вы должны определить, у какого игрока есть лучшая 5-карточная покерная комбинация.

Карта определяется как строка, с первым символом, представляющим значение карты, и вторым значением, представляющим костюм. Всегда в верхнем регистре. Карта не может отображаться дважды.

Функция будет вычислять, если рука - ничья или выигрыш для любого игрока. Он будет выводить итоговые значения в конце ввода. Формат вывода определяется позже в этом сообщении.

<сильные > Примеры

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.

Дополнительная информация

Благодаря mgroves для следующей ссылки на Project Euler, которая имеет аналогичную проблему: http://projecteuler.net/index.php?section=problems&id=54

Данные тестирования

Мы будем использовать тестовые данные Project Euler:

http://projecteuler.net/project/poker.txt

Ваше решение должно принять этот текстовый файл в качестве входных данных и вывести итоговые выигрыши и розыгрыши.

Результат

Выход должен быть в таком формате:

1: 45
2: 32
D: 12

Игрок 1 выиграл 45 рук, игрок 2 выиграл 32 руки, и было 12 ничьих. (Не фактические результаты)

Правила

  • Не нужно возвращать тип выигрышной руки, только выиграет, если кто-то
  • Вход в список карт не имеет особого порядка.
  • Карта не отображается дважды на вкладке
  • Вход всегда заглавными.
  • Принимает тестовые данные Project Euler как входные данные
  • Выводит счет, из которого игрок выиграл больше всего рук и итоговые ничьи в указанном формате выше
4b9b3361

Ответ 1

Perl, 414 398 370/458 344/416 char

Разрывы строк незначительны.

%M=map{$_,$Z++}0..9,T,J,Q,K,A;sub N{/.$/;$M{$`}.$&}

sub B{[email protected]=();
for$m(@_){$m-$_||($s+=2,++$p[$m])[email protected]_}
@_=sort{$p[$b]-$p[$a]||$b-$a}@_;
$s=23 if$s<11&&($_[0]-$_[4]<5||$_[0]-$_[1]>8&&[email protected]_,shift);
"@_"=~/.$/;$s+=14*(4<grep/$&/,@_);
$s=100*$s+$_ [email protected]_;$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}

Это решает проблему "10 карт" (раздаются 10 карт, у игрока 1 есть первые 5 карт, а у игрока 2 - 5 карт).

В первом разделе определяется подпрограмма N, которая может преобразовывать каждую карту так, чтобы она имела числовое значение. Для карт без лица это тривиальное отображение (5H == > 5H), но оно преобразует лицевые карты (KC = > 13C, AD = > 14D).

Последний раздел анализирует каждую строку ввода в карты, преобразует карты в числовые значения, делит карты на отдельные руки для двух игроков и анализирует и сравнивает эти руки. Каждая ручка увеличивает один элемент хеша %X. Когда весь вход обрабатывается, %X содержит количество выигранных рук игроком 1, выигранное игроком 2 или галстуки.

Средняя часть - это подпрограмма, в которую вводят набор из пяти карточек и создает 12-значный номер с тем свойством, что более сильные руки в покере будут иметь более высокие значения. Вот как это работает:

    for$m(@_){$m-$_||($s+=2,++$p[$m])[email protected]_}

Это "парный" детектор. Если две карты имеют одинаковое числовое значение, увеличьте хэш-элемент для одной из карт и увеличьте переменную "score" $s на два. Обратите внимание, что мы закончим сопоставление каждой карты с самим собой, поэтому $s будет не менее 10, а $p[$x] будет по крайней мере для каждой карты $x. Если рука содержит три вида, то эти три карты будут совпадать с двумя другими картами - это будет похоже на 9 матчей среди этих трех карт, а "оценка" будет не менее 18.

    @_=sort{$p[$b]-$p[$a]||$b-$a}@_;

Сортируйте карты по (1) количеству раз, когда эта карта является частью "пары" и (2) значением карты. Таким образом, в руке с двумя 7 и двумя 3, сначала появятся два 7, а затем два 3, а затем кикер. В руке с двумя 7 и тремя 3, три три будут сначала сопровождаться двумя 7. Цель этого заказа состоит в том, чтобы отличить две руки с одинаковой оценкой - рука с парой 8 и рука с парой из 7 имеют одну пару, но нам нужно сказать, что пара 8 лучше.

    $s=23 if$s<11&&($_[0]-$_[4]<5||$_[0]-$_[1]>8&&[email protected]_,shift);

Эта линия является "прямым" детектором. Прямая стоит 23 балла и возникает, когда в руке нет пар ($s<11 означает только 5 "пар" - каждая карта, соответствующая самой себе), и либо (1) значение самой высокой карты равно ровно четырем больше, чем значение самой низкой карты ($_[0]-$_[4]==4), или (2) карта с наивысшим значением - это Ace, а следующая старшая карта - 5 ($_[0]-$_[1]==9), что означает, что у руки есть A-2-3 -4-5 прямо. В последнем случае Туз теперь является наименее ценной картой в руке, поэтому мы манипулируем @_, чтобы отразить, что ([email protected]_,shift)

    "@_"=~/.$/;$s+=14*(4<grep/$&/,@_);

Эта линия является детектором флеша. Флеш стоит еще 14 очков и возникает, когда последний символ одинаковый для каждой карты. Первое выражение ("@_"=~/.$/) имеет побочный эффект установки $& на последний символ (костюм) последней карты в руке. Окончательное выражение (4<grep/$&/,@_) будет истинным тогда и только тогда, когда все элементы из @_ имеют один и тот же последний символ.

    $s=100*$s+$_ [email protected]_;$s}

Создает и возвращает значение, начинающееся с оценки руки, а затем содержит значения карт в порядке важности карты. Оценки для разных рук будут

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)

что согласуется с правилами покера. Руки с одинаковой оценкой можно различать значениями карт рук в порядке важности для руки, вплоть до наименее ценной карты в руке.

Решение проблемы с 9-карточной картой (две карты для игрока 1, две карты для игрока 2, игроки разделяют следующие 5 карт и строят лучшую руку из 5 карт) нуждается в 70 дополнительных ударах, чтобы выбрать лучшую руку из 5 карт из 7 карт, доступных каждому игроку:

%M=map{$_,$Z++}0..9,T,J,Q,K,A;sub N{/./;$M{$&}.$'}

sub A{my$I;
for$k(0..41){@[email protected]_;[email protected],$_,1for$k%7,$k/7;[email protected]=();
for$m(grep$_=N,@d){$m-$_||($s+=2,$p[$m]++)[email protected]}
@d=sort{$p[$b]-$p[$a]||$b-$a}@d;
$s=23 if$s<11&&($d[0]-$d[4]<5||$d[0]-$d[1]>8&&[email protected],[email protected]);
"@d"=~/.$/;$s+=14*(4<grep/$&/,@d);
$s=100*$s+$_ [email protected];
$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}

Ответ 2

Haskell: 793 796 806 826 864 904 901 880 863


Так как текстовый файл несовместим с 9-карточными руками, я просто читаю строку с консоли и выводя, кто победит.


Bugfixes:

  • Туз теперь считается ниже, чем 2 в туз-малой перспективе.
  • Сравнение фиксированных домов (снова: D).
  • Гарантирует, что выбрана лучшая версия данного типа руки. Например, если игрок может выбирать между прогоном 2-6 и пробелом 3-7, выбирается прогон 3-7 (сбрасывается).
  • Теперь короче, чем решение PHP!

Golfed:

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)[email protected][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

Ответ 3

GolfScript - 151/187 символов

Эта программа работает с входным списком по 10 карт на строку, т.е. двумя руками 5 карт.

n%[email protected]{3/5/{[zip~;.&,(!15*\[{n),*"TJQKA"+?}/]:|$),-4>=14*+1|{.2\?|@-,5\[email protected]*@+\[email protected]+\}/.16445=13*@+\]}%.~={0):0;;}{~>[email protected]+\@+\}if}/"1: "@@n"2: "@n"D: "0

Эта программа работает с входным списком по 9 карт на строку, в формате, указанном в спецификациях.

n%[email protected]{3/.4>:D;2/2<{D+.{3/1$^.{3/1$^[zip~;.&,(!15*\[{n),*"TJQKA"+?}/]$:|),-4>=14*+1|{.2\?|@-,5\[email protected]*@+\[email protected]+\}/.16445=13*@+\]}%\;~}%$-1=\;}%.~={0):0;\(\}*~>[email protected]+\@+\}/"1: "@@n"2: "@n"D: "0

Ответ 4

GolfScript 258 241 247/341 217/299 char

Решение проблемы с 10 картами. Только последние пару строк перевода важны:

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

Проблема с 9-карточкой в ​​настоящее время требует еще 80 символов.

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

Меньшая проблема с карточной карточкой в ​​10 раз.

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% 

    \;
    }/

    \[email protected]@                             # 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

Ответ 5

PHP, 799 символов

Разрывы строк незначительны. Это требует ввода связанного URL-адреса, который отличается от входного примера (не касается общих карточек). Обработка похожа на протокол perl-ответа, с другим методом оценки.

<?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";

Ответ 6

C, 665 + 379 символов

Вот мой ответ в 2 частях.

Первый - это полный оценщик 7 карт, включая макрос "AddCard" A. Он возвращает 32-битное число, оценивающее руку. Высоким грызком является тип, бит 13..25 указывают на высокую карту (карты) и биты 0..12 указывают кикер (ы). При сравнении результатов лучшая рука всегда будет иметь большее значение.

#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;}

Второй - это входной процессор. Он анализирует файл проекта Euler как 2 + 2 + 5 карт (игнорируя 10-ю карту). Он использует макрос Parse, P для создания 32-битных значений, представляющих каждую карту. Представление 0A0K0Q0J0T090807060504030200shdc. Рука хранится как массив из 5 целых чисел.

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]);}

Я уверен, что еще несколько персонажей будут урезаны. Я скоро добавлю объяснение.

Оценщик работает с 17,6 миллионами рук в секунду на моем 3Ghz Core2 Duo. Это всего в 3,5 раза медленнее, чем оценщик PokerSource, который использует не менее 56 Кбайт таблиц поиска.