Код Гольф: у кого лучшая покерная рука?
Я люблю такие задачи, надеюсь, скоро представлю свой ответ.
У какого игрока лучшая 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 ответов:
В Perl,
414398370/458344/416 гольцаРазрывы строк не являются существенными.
Это решает проблему "10 карт" (сдается 10 карт, у игрока 1 есть первые 5 карт, а у игрока 2-вторые 5 карт). Первый раздел определяет подпрограмму%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}
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 лучше.
Эта линия является" прямым " детектором. Стрит стоит 23 очка и происходит, когда в руке нет пар ($s=23 if$s<11&&($_[0]-$_[4]<5||$_[0]-$_[1]>8&&push@_,shift);
$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
796806826864904901880863
Поскольку текстовый файл не соответствует 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
258241247/341217/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";