Код Гольф: у кого лучшая покерная рука?


Я люблю такие задачи, надеюсь, скоро представлю свой ответ.

У какого игрока лучшая 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 за следующую ссылку на проект Euler, который имеет аналогичную проблему: http://projecteuler.net/index.php?section=problems&id=54

Тестовые Данные

Мы будем использовать тестовые данные проекта Эйлера:

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

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

Пример Вывода

Вывод должен быть в следующем формате:

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

Правила

  • не нужно возвращать тип выигрышной руки, только кто выиграл, если кто-то
  • ввод списка карт не имеет определенного порядка
  • ни одна карта не появляется дважды на входе
  • ввод всегда в верхнем регистре
  • принимает тестовые данные проекта Эйлера в качестве входных данных
  • выводит кол-во, из которого игрок выиграл наибольшее количество раздач и общее количество розыгрышей в указанном выше формате
6 18

6 ответов:

В Perl, 414 398 370/458 344/416 гольца

Разрывы строк не являются существенными.

%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}
Это решает проблему "10 карт" (сдается 10 карт, у игрока 1 есть первые 5 карт, а у игрока 2-вторые 5 карт). Первый раздел определяет подпрограмму N, которая может преобразовать каждую карту так, чтобы она имела числовое значение. Для не-лицевых карт это тривиальное отображение (5H ==> 5H), но оно преобразует лицевые карты (KC => 13C, ОБЪЯВЛЕНИЯ => 14Д). Последний раздел разбирает каждую строку ввода на карты, преобразует карты в числовые значения, делит карты на отдельные руки для двух игроков и анализирует и сравнивает эти руки. Каждая рука увеличивает один элемент хэша %X. Когда все входные данные проанализированы, %X содержит количество рук, выигранных игроком 1, выигранных игроком 2 или галстуков.

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

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

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

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

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

    $s=23 if$s<11&&($_[0]-$_[4]<5||$_[0]-$_[1]>8&&push@_,shift);
Эта линия является" прямым " детектором. Стрит стоит 23 очка и происходит, когда в руке нет пар ($s<11 означает, что найдено только 5 " пар " - каждая карта соответствует самой себе), и либо (1) значение старшей карты ровно на четыре больше, чем значение самой младшей картой ($_[0]-$_[4]==4), или (2) самой старшей картой является туз, а следующей старшей картой является 5 ($_[0]-$_[1]==9), что означает, что рука имеет а-2-3-4-5 прямо. В последнем случае Туз теперь является наименее ценной картой в руке, поэтому мы манипулируем @_, чтобы отразить это (push@_,shift)
    "@_"=~/.$/;$s+=14*(4<grep/$&/,@_);

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

    $s=100*$s+$_ for@_;$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){@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}

GolfScript-151/187 chars

Эта программа работает с входным списком из 10 карт на линию, то есть с двумя 5-карточными раздачами.
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

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

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

Хаскелл: 793 796 806 826 864 904 901 880 863


Поскольку текстовый файл не соответствует 9 карточным раздачам, я просто читаю строку из консоли и выводю, кто выигрывает.

Исправления ошибок:

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

Гольфист:

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 гольца

Решение задачи о 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% 

    \;
    }/

    \.@.@                             # 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

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 тысяч таблиц подстановки.

PHP, 799 символов

Разрывы строк не являются существенными. При этом вводится связанный url-адрес, который отличается от примера ввода (не имеет дело с общими картами). Обработка похожа на perl-ответ mobrule, с другим методом подсчета очков.

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