challenge ポーカーの役判定

引数に手札を与えると、ポーカーの役を表示するプログラムを作ってください。

条件:

  • スートはS,D,H,C、ランクはA,2~9,T,J,Q,Kのそれぞれ一文字で表します。
  • 手札は S2D5H3CQS9 のように10文字で指定されます。特にソートはされていません。
  • 手札にジョーカーは含まれません。
  • ストレートで取りうるランクの種類はA2345, 23456 ... 9TJQK, TJQKAの10種類で、JQKA2のようにK-A-2をまたぐものはストレートではありません。

実行例:

% ./poker SQSJSASKST
Royal flush

% ./poker D9D7D6D5D8
Straight flush

% ./poker C2D2S2H3H2
Four of a kind

% ./poker C2D3S2H3H2
Full house

% ./poker S9S4S8STSJ
Flush

% ./poker C4H7D5S6H3
Straight

% ./poker S6H6C5DQC6
Three of a kind

% ./poker S6HQC5DQC6
Two pair

% ./poker S6H4C5DQC6
One pair

% ./poker SJSQSKSAC2
No pair

お題にしようと思っていたのに間違えてしまいました。今から変更可能でしょうか?

(説明)
当初間違ってトピックに投稿していたので、このようなコメントを付けていたのですが、
このコメントに気づいた管理人さんにお題に移していただきました。
(最初の2つだけ投稿日時が早いのはそのためです)

Posted feedbacks - Flatten

Nested Hidden

お題にしようと思っていたのに間違えてしまいました。今から変更可能でしょうか?


素直に順番に条件チェックしてみました。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Sample121 {
    private static final String RANK_LIST = "A23456789TJQKA";

    private static final String ROYAL = "royal ";
    private static final String STRAIGHT = "straight";
    private static final String FLUSH = "flush";
    private static final String FULL_HOUSE = "Full house";
    private static final String CARDS_4 = "Four of a kind";
    private static final String CARDS_3 = "Three of a kind";
    private static final String PAIR_2 = "Two pair";
    private static final String PAIR_1 = "One pair";
    private static final String Nothing = "No pair";

    public static String getPokerRole(String cards) {
        if (cards.length() != 2 * 5) throw new IllegalArgumentException();
        String rankList = createRankList(cards);
        boolean flush = isFlush(cards);
        boolean straight = isStraight(rankList);
        if (flush || straight) {
            boolean royal = isRoyal(rankList);
            if (royal) {
                return format(flush? ROYAL + FLUSH: ROYAL + STRAIGHT);
            } else {
                if (flush && straight) {
                    return format(STRAIGHT + " " + FLUSH);
                } else {
                    return format(flush? FLUSH: STRAIGHT);
                }
            }
        }
        Integer[] integers = countSameNumber(rankList);
        switch (integers[0]) {
            case 4:
                return CARDS_4;
            case 3:
                if (integers[1] == 2) {
                    return FULL_HOUSE;
                } else {
                    return CARDS_3;
                }
            case 2:
                if (integers[1] == 2) {
                    return PAIR_2;
                } else {
                    return PAIR_1;
                }
        }
        return Nothing;
    }

    private static String format(String str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    private static boolean isFlush(String cards) {
        char suit = cards.charAt(0);
        for (int index = 1; index < 5; index++) {
            if (suit != cards.charAt(index * 2)) {
                return false;
            }
        }
        return true;
    }

    private static String createRankList(String cards) {
        StringBuilder builder = new StringBuilder();
        for (int index = 0; index < 5; index++) {
            final char rank = cards.charAt(index * 2 + 1);
            int insert = 0;
            for (; insert < builder.length(); insert++) {
                if (RANK_LIST.indexOf(builder.charAt(insert)) > RANK_LIST.indexOf(rank)) {
                    break;
                }
            }
            builder.insert(insert, rank);
        }
        return builder.toString();
    }
    private static boolean isStraight(String rankList) {
        return RANK_LIST.indexOf(rankList) >= 0;
    }
    private static boolean isRoyal(String rankList) {
        return rankList.equals("ATJQK");
    }

    private static Integer[] countSameNumber(String rankList) {
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        for (int index = 0; index < rankList.length(); index++) {
            char c = rankList.charAt(index);
            Integer integer = map.get(c);
            if (integer == null) {
                integer = 1;
                map.put(c, integer);
            } else {
                map.put(c, integer + 1);
            }
        }
        List<Integer> result = new ArrayList<Integer>(map.values());
        Collections.sort(result, Collections.reverseOrder());
        return result.toArray(new Integer[0]);
    }


    public static void main(String[] args) {
        System.out.println(getPokerRole("SQSJSASKST"));
        System.out.println(getPokerRole("D9D7D6D5D8"));
        System.out.println(getPokerRole("C2D2S2H3H2"));
        System.out.println(getPokerRole("C2D3S2H3H2"));
        System.out.println(getPokerRole("S9S4S8STSJ"));
        System.out.println(getPokerRole("C4H7D5S6H3"));
        System.out.println(getPokerRole("S6H6C5DQC6"));
        System.out.println(getPokerRole("S6HQC5DQC6"));
        System.out.println(getPokerRole("S6H4C5DQC6"));
        System.out.println(getPokerRole("SJSQSKSAC2"));
    }
}

こんな感じ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import sys
import collections

def solve(s):
    it = iter(s)
    cards = []
    for _ in xrange(5):
        suit = "SDHC".index(it.next())
        number = "A23456789TJQK".index(it.next()) + 1
        cards.append((suit, number))

    counts = collections.defaultdict(int)
    for suit, number in cards:
        counts[number] += 1
    counts = sorted(counts.itervalues())

    flush = all(cards[0][0] == suit for suit, number in cards[1:])

    numbers = sorted(number for suit, number in cards)
    royal_straight = (numbers == [1, 10, 11, 12, 13])
    straight = all(numbers[i] + 1 == numbers[i + 1] for i in xrange(4)) or royal_straight

    if royal_straight and flush:
        return "Royal flush"
    elif straight and flush:
        return "Straight flush"
    elif counts == [1, 4]:
        return "Four of a kind"
    elif counts == [2, 3]:
        return "Full House"
    elif flush:
        return "Flush"
    elif straight:
        return "Straight"
    elif counts == [1, 1, 3]:
        return "Three of a kind"
    elif counts == [1, 2, 2]:
        return "Two pair"
    elif counts == [1, 1, 1, 2]:
        return "One pair"
    else:
        assert counts == [1, 1, 1, 1, 1]
        return "No pair"

def main():
    if len(sys.argv) >= 2:
        for s in sys.argv[1:]:
            print solve(s)
    else:
        print solve("SQSJSASKST") # Royal flush
        print solve("D9D7D6D5D8") # Straight flush
        print solve("C2D2S2H3H2") # Four of a kind
        print solve("C2D3S2H3H2") # Full house
        print solve("S9S4S8STSJ") # Flush
        print solve("C4H7D5S6H3") # Straight
        print solve("S6H6C5DQC6") # Three of a kind
        print solve("S6HQC5DQC6") # Two pair
        print solve("S6H4C5DQC6") # One pair
        print solve("SJSQSKSAC2") # No pair

if __name__ == '__main__':
    main()

パターンマッチって素晴らしい。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
object Poker {
  def whatHand_?(_cs:String) = {
    val rank = Map((0 to 12).map(i => "A23456789TJQK"(i) -> (i+1)).toArray:_*)
    val cs = (0.until(_cs.size, 2)).map(_cs.substring).
               map{s=>(s(0), rank(s(1)))}.toList.sort(_._2<_._2)
    val royalSt_? = cs match{
      case List((_,1),(_,10),(_,11),(_,12),(_,13)) => true
      case _ => false
    }
    val flush_? = cs.forall(cs(0)._1 == _._1)
    val st_? = (1 to 9).map(i=>cs(0)._2==i && cs(4)._2==i+4).exists(true==) || royalSt_?
    val p = (((List(List[(char,int)]())) /: List.make(2, cs)){
              for(i <-_; j <-_) yield j::i
            }.filter(c=>c(0)._2 == c(1)._2).size - 5)/2

    (royalSt_?, flush_?, st_?, p) match {
      case (true, true, _, _) => "Royal flush"
      case (_, true, true,_)  => "Straight flush"
      case (_, true, _ ,_)  => "Flush"
      case (_, _, true ,_)  => "Straight"
      case (_,_,_, 6) => "Four of a kind"
      case (_,_,_, 4) => "Full house"
      case (_,_,_, 3) => "Three of a kind"
      case (_,_,_, 2) => "Two pairs"
      case (_,_,_, 1) => "One pair"
      case _ => "No pair"
    }
  }
}

ペア判定以外の上手な判定方法が思いつきませんでした。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <iostream>
using namespace std;

int main(int argc, char *argv[])
{
    // パラメータのチェック(不十分)
    if ( argc != 2 )
    {
        cout << "fatal arguments" << endl << "exp) ./poker SQSJSASKST" << endl;
        return -1;
    }

    // 手札を配列に直す
    int a[5];
    char suit = argv[1][0];
    bool flush_flag = true;
    for(char i = 0; i < 5; i++)
    {
        char num = argv[1][i*2+1];
        if ( num == 'A' )
            a[i] = 1;
        else if ( num == 'T' )
            a[i] = 10;
        else if ( num == 'J' )
            a[i] = 11;
        else if ( num == 'Q' )
            a[i] = 12;
        else if ( num == 'K' )
            a[i] = 13;
        else
            a[i] = num - '0';

        // フラッシュをついでに計算
        if ( suit != argv[1][i*2] )
            flush_flag = false;
    }

    // ペアを計算
    int count = 0;
    for(int y=0; y<5; y++)
        for(int x=y; x<5; x++)
            if ( x != y && a[x] == a[y] )
                count ++;

    // ストレートを計算
    bool straight_flag = true;
    bool royal_straight_flag = true;
    // sort
    for(int y=0; y<5; y++)
        for(int x=5; x>=y+1; x--)
            if ( a[x] < a[x-1] )
            {
                int t = a[x];
                a[x] = a[x-1];
                a[x-1] = t;
            }
    if ( a[4] == 13 )
    {
        straight_flag = false;
        for(int i=1; i<5; i++)
            royal_straight_flag &= (a[i] == i + 9);
        royal_straight_flag &= (suit == 'S');
    }
    else
    {
        royal_straight_flag = false;
        for(int i=0; i<5; i++)
            straight_flag &= (a[i] == i + a[0]);
    }
    straight_flag |= royal_straight_flag;

    // 役の表示
    if ( straight_flag )
    {
        if ( royal_straight_flag && flush_flag )
        {
            cout << "Royal flush" << endl; // Royal Straight Flush ??
        }
        else if ( flush_flag )
        {
            cout << "Straight flush" << endl;
        }
        else
        {
            cout << "Straight" << endl;
        }
    }
    else if ( flush_flag )
    {
        cout << "Flush" << endl;
    }
    else
    {
        switch ( count )
        {
            case 1:
                cout << "One pair" << endl;
                break;
            case 2:
                cout << "Two pair" << endl;
                break;
            case 3:
                cout << "Three of a kind" << endl;
                break;
            case 4:
                cout << "Full house" << endl;
                break;
            case 6:
                cout << "Four of a kind" << endl;
                break;
            default:
                cout << "No pair" << endl;
        }
    }
}

一切コメント無しでも充分な可読性を目指しました。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
//http://ja.doukaku.org/121/ 投稿用
using System;
using System.Collections.Generic;

class Program {
    const string Ranks = "A23456789TJQKA";
    const string Suits = "SDHC";
    static void Main(string[] args) {
        if(IsRoyalStraightFlush(args[0])) Console.WriteLine("Royal straight flush");
        else if(IsStraightFlush(args[0])) Console.WriteLine("Straight flush");
        else if(Is4cards(args[0])) Console.WriteLine("Four of a kind");
        else if(IsFullHouse(args[0])) Console.WriteLine("Full house");
        else if(IsFlush(args[0])) Console.WriteLine("Flush");
        else if(IsStraight(args[0])) Console.WriteLine("Straight");
        else if(Is3Cards(args[0])) Console.WriteLine("Three of a kind");
        else if(Is2Pair(args[0])) Console.WriteLine("Two pair");
        else if(Is1Pair(args[0])) Console.WriteLine("One pair");
        else Console.WriteLine("No pair");
        Console.ReadLine();
    }

    static bool IsRoyalStraightFlush(string cards){
        if(IsFlush(cards) && IsStraight(cards)){
            for(int i = 2; i <= 9; i++) {//2~9までの数字が含まれていたらNG
                if(cards.IndexOf(i.ToString()) != -1) return false;
            }
            return true;
        }
        return false;
    }

    static bool IsStraightFlush(string cards) {
        return IsFlush(cards) && IsStraight(cards);
    }

    static bool Is4cards(string cards) {
        return IsAnyCards(cards, 4);
    }

    static bool IsFullHouse(string cards) {
        return Is3Cards(cards) && Is1Pair(cards);
    }

    static bool IsFlush(string cards) {
        foreach(char suit in Suits.ToCharArray()) {//あるスーツの文字を切り取って
            if(cards.Length - cards.Replace(suit.ToString(),"").Length == 5) return true;
            //5文字減ったら5枚全て同じスーツ
        }
        return false;
    }

    static bool IsStraight(string cards) {
        List<string> rankListInCards = new List<string>();//カードのランクのみ
        //ランク切り出し
        for(int i = 1; i <= 9; i = i + 2) {
            rankListInCards.Add(cards[i].ToString());
        }
        //並び替え
        rankListInCards.Sort(cardRankSort);
        //ソート済みカードリスト作成
        string sortedCards = "";
        foreach(string card in rankListInCards) {
            sortedCards += card;
        }
        //判定
        if(Ranks.Contains(sortedCards)) return true;
        if(sortedCards[0] == 'A') {//先頭がAなら
            //末尾に移してもう一度判定
            if(Ranks.Contains(sortedCards.Remove(0, 1).PadRight(5, 'A'))) return true;
        }
        return false;
    }

    static bool Is3Cards(string cards) {
        return IsAnyCards(cards, 3);
    }

    static bool Is2Pair(string cards) {
        foreach(char Rank in Ranks.ToCharArray()) {
            if(cards.Length - cards.Replace(Rank.ToString(), "").Length == 2) {//One pairだったら
                return Is1Pair(cards.Replace(Rank.ToString(),""));//One pairが2回でTwo pair
            }
        }
        return false;
    }

    static bool Is1Pair(string cards) {
        return IsAnyCards(cards, 2);
    }

    //あるカードがany枚含まれているかどうかを判定
    static bool IsAnyCards(string cards, int any) {
        foreach(char Rank in Ranks.ToCharArray()) {
            if(cards.Length - cards.Replace(Rank.ToString(), "").Length == any) return true;
        }
        return false;
    }

    static int cardRankSort(string x, string y) {
        return Ranks.IndexOf(x) - Ranks.IndexOf(y); 
    }
}

すみません、1箇所ミスってました。

1
2
3
4
5
6
7
8
9
@@ -8,7 +8,7 @@
       case _ => false
     }
     val flush_? = cs.forall(cs(0)._1 == _._1)
-    val st_? = (1 to 9).map(i=>cs(0)._2==i && cs(4)._2==i+4).exists(true==) || royalSt_?
+    val st_? = (1 to 9).map(i=>(0 to 4).forall(j=>cs(j)._2==i+j)).exists(true==) || royalSt_?
     val p = (((List(List[(char,int)]())) /: List.make(2, cs)){
               for(i <-_; j <-_) yield j::i
             }.filter(c=>c(0)._2 == c(1)._2).size - 5)/2

思ったより時間かかってしまいました
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using System;

namespace Poker
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 1)
                return;

            Poker poker = new Poker(args[0]);
            Console.WriteLine(poker.Judge());
        }
    }

    public class Poker
    {
        static string RANK = "A23456789TJQK";
        string cards;
        string ranks;

        public Poker(string card)
        {
            cards = card;
            ranks = SortRanks();
        }

        public string Judge()
        {
            bool isRoyalStraight = ranks == "ATJQK";
            bool isStraight = RANK.IndexOf(ranks) >= 0;
            bool isFlush = IsFlush();
            string sameRanks = GetSameRanks();

            if (isRoyalStraight && isFlush)
                return "Royal flush";
            else if (isStraight && isFlush)
                return "Straight flush";
            else if (isStraight)
                return "Straight";
            else if (isFlush)
                return "Flush";
            else if (sameRanks == "4")
                return "Four of a kind";
            else if (sameRanks == "23")
                return "Full house";
            else if (sameRanks == "3")
                return "Three of a kind";
            else if (sameRanks == "22")
                return "Two pair";
            else if(sameRanks == "2")
                return "One pair";
            else
                return "No pair";
        }

        private string GetSameRanks()
        {
            int[] rankArray = new int[13];
            string sameRanks = "";

            for (int i = 0; i < rankArray.Length; i++)
                rankArray[i] = 0;

            for (int i = 0; i < ranks.Length; i++) {
                switch (ranks[i]) {
                    case 'A':
                    rankArray[0]++;
                    break;
                    case 'T':
                    rankArray[9]++;
                    break;
                    case 'J':
                    rankArray[10]++;
                    break;
                    case 'Q':
                    rankArray[11]++;
                    break;
                    case 'K':
                    rankArray[12]++;
                    break;
                    default:
                    rankArray[ranks[i] - 48 - 1]++;
                    break;
                }
            }

            Array.Sort(rankArray);
            foreach (int n in rankArray) {
                if (n > 1)
                    sameRanks += n.ToString();
            }

            return sameRanks;
        }

        private bool IsFlush()
        {
            char suit = cards[0];
            for (int i = 2; i < cards.Length; i += 2) {
                if (cards[i] != suit)
                    return false;
            }

            return true;
        }

        private string SortRanks()
        {
            char[] rankBuff = new char[5];
            string sortedRank = "";

            for (int i = 0; i < rankBuff.Length; i++)
                rankBuff[i] = cards[i * 2 + 1];

            for (int i = 0; i < RANK.Length; i++) {
                char c = RANK[i];
                foreach (char c2 in rankBuff) {
                    if (c == c2)
                        sortedRank += c.ToString();
                }
            }

            return sortedRank;
        }
    }
}

GetSameRanks()で文字列のフラグを生成する発想は無かったです。


(説明)
当初間違ってトピックに投稿していたので、このようなコメントを付けていたのですが、
このコメントに気づいた管理人さんにお題に移していただきました。
(最初の2つだけ投稿日時が早いのはそのためです)

文字列のチェックは最低限ですが
役判定も短めにまとめてみました☆
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<stdio.h>
#include<string.h>

void fortune(char *cards){
  char *ranklist = "A23456789TJQK";
  int suit[5], rank[5], straight = 0, flush = 0, pair = 0, i, j;

  for(i = 0; i < 5; i++)
    suit[i] = cards[i * 2],
    rank[i] = strchr(ranklist, cards[i * 2 + 1]) - ranklist,
    straight |= 1 << rank[i];
  while(straight % 2 < 1)
    straight /= 2;
  for(i = 0; i < 4; i++)
    for(j = i + 1; j < 5; j++)
      flush += (suit[i] == suit[j]),
      pair += (rank[i] == rank[j]);

  if(flush == 10 && straight == 7681)         printf("Royal flush\n");
  else if(flush == 10 && straight == 31)      printf("Straight flush\n");
  else if(pair == 6)                          printf("Four of a kind\n");
  else if(pair == 4)                          printf("Full house\n");
  else if(flush == 10)                        printf("Flush\n");
  else if(straight == 7681 || straight == 31) printf("Straight\n");
  else if(pair == 3)                          printf("Three of a kind\n");
  else if(pair == 2)                          printf("Two pair\n");
  else if(pair == 1)                          printf("One pair\n");
  else                                        printf("No pair\n");
}

int main(void){
  fortune("SQSJSASKST");
  fortune("D9D7D6D5D8");
  fortune("C2D2S2H3H2");
  fortune("C2D3S2H3H2");
  fortune("S9S4S8STSJ");
  fortune("C4H7D5S6H3");
  fortune("S6H6C5DQC6");
  fortune("S6HQC5DQC6");
  fortune("S6H4C5DQC6");
  fortune("SJSQSKSAC2");

  fortune("SQSJDASKST");
  fortune("S4S3DASKS2");
  return 0;
}

ワイルドカード(WWで表す)に対応させてみました。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//http://ja.doukaku.org/121/ 投稿用
using System;
using System.Collections.Generic;

class Program {
    const string Ranks = "A23456789TJQKA";
    const string Suits = "SDHC";
    enum Hands {
        Five_of_a_kind = 10,
        Royal_straight_flush = 9,
        Straight_flush = 8,
        Four_of_a_kind = 7,
        Full_house = 6,
        Flush = 5,
        Straight = 4,
        Three_of_a_kind = 3,
        Two_pair = 2,
        One_pair = 1,
        No_pair = 0
    }

    static void Main(string[] args) {
        Hands hand = 0;
        foreach(char suit in Suits.ToCharArray()) {
            foreach(char rank in Ranks.Remove(0, 1).ToCharArray()) {
                string wildCard = suit.ToString() + rank.ToString();
                Hands tmpHand = GetHands(args[0].Replace("WW", wildCard));
                hand = tmpHand > hand ? tmpHand : hand;
            }
        }
        Console.WriteLine(hand);
        Console.ReadLine();
    }

    static Hands GetHands(string cards) {
        if(Is5cards(cards)) return Hands.Five_of_a_kind;
        else if(IsRoyalStraightFlush(cards)) return Hands.Royal_straight_flush;
        else if(IsStraightFlush(cards)) return Hands.Straight_flush;
        else if(Is4cards(cards)) return Hands.Five_of_a_kind;
        else if(IsFullHouse(cards)) return Hands.Full_house;
        else if(IsFlush(cards)) return Hands.Flush;
        else if(IsStraight(cards)) return Hands.Straight;
        else if(Is3Cards(cards)) return Hands.Three_of_a_kind;
        else if(Is2Pair(cards)) return Hands.Two_pair;
        else if(Is1Pair(cards)) return Hands.One_pair;
        return Hands.No_pair;
    }

    static bool Is5cards(string cards) {
        return IsAnyCards(cards, 5);
    }

    static bool IsRoyalStraightFlush(string cards){
        if(IsFlush(cards) && IsStraight(cards)){
            for(int i = 2; i <= 9; i++) {//2~9までの数字が含まれていたらNG
                if(cards.IndexOf(i.ToString()) != -1) return false;
            }
            return true;
        }
        return false;
    }

    static bool IsStraightFlush(string cards) {
        return IsFlush(cards) && IsStraight(cards);
    }

    static bool Is4cards(string cards) {
        return IsAnyCards(cards, 4);
    }

    static bool IsFullHouse(string cards) {
        return Is3Cards(cards) && Is1Pair(cards);
    }

    static bool IsFlush(string cards) {
        foreach(char suit in Suits.ToCharArray()) {//あるスーツの文字を切り取って
            if(cards.Length - cards.Replace(suit.ToString(),"").Length == 5) return true;
            //5文字減ったら5枚全て同じスーツ
        }
        return false;
    }

    static bool IsStraight(string cards) {
        List<string> rankListInCards = new List<string>();//カードのランクのみ
        //ランク切り出し
        for(int i = 1; i <= 9; i = i + 2) {
            rankListInCards.Add(cards[i].ToString());
        }
        //並び替え
        rankListInCards.Sort(cardRankSort);
        //ソート済みカードリスト作成
        string sortedCards = "";
        foreach(string card in rankListInCards) {
            sortedCards += card;
        }
        //判定
        if(Ranks.Contains(sortedCards)) return true;
        if(sortedCards[0] == 'A') {//先頭がAなら
            //末尾に移してもう一度判定
            if(Ranks.Contains(sortedCards.Remove(0, 1).PadRight(5, 'A'))) return true;
        }
        return false;
    }

    static bool Is3Cards(string cards) {
        return IsAnyCards(cards, 3);
    }

    static bool Is2Pair(string cards) {
        foreach(char Rank in Ranks.ToCharArray()) {
            if(cards.Length - cards.Replace(Rank.ToString(), "").Length == 2) {//One pairだったら
                return Is1Pair(cards.Replace(Rank.ToString(),""));//One pairが2回でTwo pair
            }
        }
        return false;
    }

    static bool Is1Pair(string cards) {
        return IsAnyCards(cards, 2);
    }

    //あるカードがany枚含まれているかどうかを判定
    static bool IsAnyCards(string cards, int any) {
        foreach(char Rank in Ranks.ToCharArray()) {
            if(cards.Length - cards.Replace(Rank.ToString(), "").Length == any) return true;
        }
        return false;
    }

    static int cardRankSort(string x, string y) {
        return Ranks.IndexOf(x) - Ranks.IndexOf(y); 
    }
}

簡潔になるようにしたつもりが、混沌としたものになってしまいました…。
実行結果:
(format t "~A => ~A~%" x (hand x "SQSJSASKST"))
;==>
;SQSJSASKST => Royal straight flash 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
(defpackage :doukaku-121 (:use :cl :ppcre))
(in-package :doukaku-121)

(defun break-part (str &optional (ace-val 1) &aux suit rank)
  (do-matches-as-strings (match "([SDHC][A2-9TJQK])" str)
    (push (schar match 0) suit)
    (push (schar match 1) rank))
  (values
   (sublis `((A . ,ace-val) (T . 10) (J . 11) (Q . 12) (K . 13))
           (mapcar (lambda (x)(read-from-string (string x))) 
                   (nreverse rank)))
   suit))

(defun flashp (str)
  (let ((s (nth-value 1 (break-part str))))
    (and (every (lambda (x) (char= (car s) x)) s) 'flash)))

(defun kinds (str)
  (let ((k (length (delete-duplicates (break-part str)))))
    (if (= k 5) nil k)))

(defun straightp (str)
  (flet ((check (str ace)
           (let ((lst (sort (break-part str ace) #'<)))
             (mapc (lambda (x y) (unless (= 1 (- y x)) (return-from check nil)))
                   lst (cdr lst))
             (values 'straight (if (= 1 ace) nil 'royal)))))
    (or (check str 1) (check str 14))))

(defun 3-2p (str)
  (do ((l (break-part str) (cdr l)))
      ((null (cddr l)) nil)
    (case (count (car l) l)
      (3 (return t))
      (4 (return nil)))))

(defun hand (str &optional (out t))
  (multiple-value-bind (straightp royalp) (straightp str)
    (let ((hand (list royalp straightp (flashp str) (kinds str) (3-2p str))))
      (destructuring-bind (ignore flashp kinds 3-2p) hand
        (declare (ignore ignore))
        (if 3-2p
            (case kinds
              (2 (format out "Full house"))
              (3 (format out "Three of a kind")))
            (case kinds
              (2 (format out "Four of a kind"))
              (3 (format out "Two pair"))
              (4 (format out "One paire"))
              (otherwise (if (or straightp flashp)
                             (format out "~@(~{~@[~A ~]~}~)" hand)
                             (format out "No pair")))))))))

すいません、修正前のをコピペしてました。実行例も不要なxが混じってました。正確には、 (format t "~A => ~A~%" (hand "SQSJSASKST")) です。

1
2
3
4
40c40
<       (destructuring-bind (ignore flashp kinds 3-2p) hand
---
>       (destructuring-bind (ignore flashp straightp kinds 3-2p) hand

既にJavaで解かれていますが、ちょっと捻ってみました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Poker {
    public static void main(String[] args) {
        System.out.println(getPokerRole("SQSJSASKST"));
        System.out.println(getPokerRole("D9D7D6D5D8"));
        System.out.println(getPokerRole("C2D2S2H3H2"));
        System.out.println(getPokerRole("C2D3S2H3H2"));
        System.out.println(getPokerRole("S9S4S8STSJ"));
        System.out.println(getPokerRole("C4H7D5S6H3"));
        System.out.println(getPokerRole("S6H6C5DQC6"));
        System.out.println(getPokerRole("S6HQC5DQC6"));
        System.out.println(getPokerRole("S6H4C5DQC6"));
        System.out.println(getPokerRole("SJSQSKSAC2"));
    }

    private static String getPokerRole(String aCards) {
        List<int[]> tCards = new ArrayList<int[]>();

        for (int i = 0; i < aCards.length(); i += 2) {
            tCards.add(new int[] { aCards.charAt(i), 1 + "A23456789TJQK".indexOf(aCards.charAt(i + 1)) });
        }

        Collections.sort(tCards, new Comparator<int[]>() {
            public int compare(int[] aLeftCard, int[] aRightCard) {
                return aLeftCard[1] - aRightCard[1];
            }
        });

        int tRoyalCardCount = 0;
        boolean tIsStraight = true;
        boolean tIsFlush = true;
        int[] tPairs = new int[5];
        int tPairKinds = 0;
        for (int i = 0; i < tCards.size(); i++) {
            if (Arrays.binarySearch(new int[] { 1, 10, 11, 12, 13 }, tCards.get(i)[1]) >= 0) {
                tRoyalCardCount++;
            }
            if (i == 0) {
                continue;
            }
            if (tCards.get(i - 1)[0] != tCards.get(i)[0]) {
                tIsFlush = false;
            }
            if (tCards.get(i - 1)[1] + 1 != tCards.get(i)[1]) {
                tIsStraight = false;
            }
            if (tCards.get(i - 1)[1] == tCards.get(i)[1]) {
                tPairs[tPairKinds]++;
            } else {
                tPairKinds++;
            }
        }

        Arrays.sort(tPairs);
        if (tRoyalCardCount == 5 && tIsFlush) {
            return "Royal flush";
        } else if (tIsFlush && tIsStraight) {
            return "Straight flush";
        } else if (tIsFlush) {
            return "Flush";
        } else if (tIsStraight) {
            return "Straight";
        } else if (tPairs[4] == 3) {
            return "Four of a kind";
        } else if (tPairs[4] == 2 && tPairs[3] == 1) {
            return "Full house";
        } else if (tPairs[4] == 2) {
            return "Three of a kind";
        } else if (tPairs[4] == 1 && tPairs[3] == 1) {
            return "Two pair";
        } else if (tPairs[4] == 1 && tPairs[3] == 0) {
            return "One pair";
        }

        return "No pair";
    }
}

Squeak Smalltalk で。

バッグ(a Bag)はマルチセットとも呼ばれる特殊なコレクションのひとつで、要素を、その種類と出現数との組として管理しています。情報は内包される辞書に保持され、#valuesAndCounts をコールすることでアクセスできます。なお、Smalltalk で辞書(a Dictionary)とは、連想配列のことを指します。

辞書は #values で、自らが要素として持つ「キー -> 値」の組の「値」(この場合、出現数)のみを抽出した配列を返します。本スクリプトでは、この出現数のみを抽出した配列(ソート済み)のパターンを見て(#caseOf:otherwise:)、手の役(ストレートやフラッシュ以外の…)を判定しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
| 入力 ランク順 ランク スート |
入力 := 'SQSJSASKST'.
ランク順 := 'A', ($2 to: $9), 'TJQK'.
ランク := Bag new.
スート := Bag new.
入力 pairsDo: [:a :b | スート add: a. ランク add: (ランク順 indexOf: b)].
スート asSet size = 1 ifTrue: [
    ランク asSortedArray = #(1 10 11 12 13) ifTrue: [^'Royal flush'].
    ランク asSortedArray = (ランク min to: ランク min + 4) ifTrue: [^'Straight flush'].
    ^'Flush'].
ランク asSortedArray = (ランク min to: ランク min + 4) ifTrue: [^'Straight'].
^ランク valuesAndCounts values sort caseOf: {
    [#(1 4)] -> ['Four of a kind'].
    [#(2 3)] -> ['Full house'].
    [#(1 1 3)] -> ['Three of a kind'].
    [#(1 2 2)] -> ['Two pair'].
    [#(1 1 1 2)] -> ['One pair']} otherwise: ['No pair']

面倒なので、大文字ではなくて、小文字でカードを記述しています。スマートに出来るかと思ったけど、結局力業でした。

実行結果:
$ pl -qs 121.pl
sqsjsaskst->Royal flush
d9d7d6d5d8->Straight flush
c2d2s2h3h2->four of a kind
c2d3s2h3h2->Full house
s9s4s8stsj->Flush
c4h7d5s6h3->Straight
s6h6c5dqc6->Three of a kind
s6hqc5dqc6->Two pair
s6h4c5dqc6->One pair
sjsqsksac2->No Pair
?-

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
straight([(_,A)|As]):-seq(A,As).

seq(_,[]).
seq(P,[(_,N)|Ns]):-succ(P,N),seq(N,Ns).

flush([(A,_)|As]):-flush0(A,As).
flush0(_,[]).
flush0(A,[(A,_)|As]):-flush0(A,As).

count([],[]).
count([(_,N)|Ns],R):-count(Ns,Rs),countw(N,Rs,R).

countw(N,[],[(N,1)]).
countw(N,[(N,C0)|R],[(N,C)|R]):-succ(C0,C).
countw(N,[P|R0],[P|R]):-countw(N,R0,R).

sortcard(C,Cs):-predsort(cmp,C,Cs).
cmp(>,(_,N1),(_,N2)):-N1>N2,!.
cmp(<,_,_):-!.

p([(S,1),(S,10),(S,11),(S,12),(S,13)],'Royal flush'):-!.
p(C, 'Straight flush'):-straight(C),flush(C),!.
p(C, 'Flush'):-flush(C),!.
p(C, 'Straight'):-straight(C),!.
p(C, R) :- count(C,C1), maplist(arg(2),C1,C2),msort(C2,C3),n(C3, R).
p(_, 'No Pair').

n([2,3],'Full house'):-!.
n([1,4],'four of a kind'):-!.
n([1,1,3],'Three of a kind'):-!.
n([1,2,2],'Two pair'):-!.
n([1,1,1,2],'One pair'):-!.

tcard1([],[]).
tcard1([S,Na|Cs],[(S,N)|Cs1]):-nth1(N,[a,'2','3','4','5','6','7','8','9',t,j,q,k],Na),tcard1(Cs,Cs1).

poker(C,R):-atom_chars(C,Cs),tcard1(Cs,R0),sortcard(R0,R1),p(R1,R),!.

test(C):-poker(C,R),write(C->R),nl.

:-maplist(test,[sqsjsaskst,
                d9d7d6d5d8,
                c2d2s2h3h2,
                c2d3s2h3h2,
                s9s4s8stsj,
                c4h7d5s6h3,
                s6h6c5dqc6,
                s6hqc5dqc6,
                s6h4c5dqc6,
                sjsqsksac2]).

ああ、そうだ。このお題は良い問題だと思います。


Mac OS X (PowerPC 32bit) アセンブリで。PowerPCアセンブリ読めというのも無茶なので、以下やってることの説明です。先にほとんど同じロジック(#5168)で投稿されてしまいましたけど。

スート役は「すべてのスート役が同じ」場合のみ成り立つので、最初のスートを記録しておき、二枚目以降と比較すれば判断できます。途中に一つでも異なるスートがあれば、スート用レジスタを 0 にします。このレジスタには最初のスートのASCIIコードが入っているので、レジスタが 0 でなければフラッシュ系の役が成立することになります。

ランク役では、重複した枚数ごとにレジスタを用意します。このレジスタは1-13までのランクをビットで表したフラグです。

  • r8: ノーペア(1枚)
  • r9: ワンペア(2枚)
  • r10: スリーカード(3枚)
  • r11: フォーカード(4枚)

最初のカードのランクは、ノーペア用レジスタのビット 1<<ランク を立てます(ランクが5なら 1<<4 )。次のカードのランクも同じであれば、ワンペア用レジスタの同ビットを立てて、ノーペア用レジスタの同ビットを下ろします。これでペアは判断できます。

最後にストレートとロイヤルフラッシュですが、ノーペア用レジスタを使います。ロイヤルフラッシュはATJQKの組み合わせが決まっていますから、その箇所のビットを立てたビットマスクで判断できます。ストレートは連続した5つのビット(11111)をシフトしながらAND演算して判断します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
;; ------------------------------------------------
;; poker.s for Mac OS X (PowerPC 32bit)
;; % as -o poker.o poker.s && gcc poker.o -o poker
;; ------------------------------------------------

        .machine ppc
        .globl  _main

;; レジスタ
;;
;; r4:  char *argv[]
;; r5:  残りカードの枚数
;; r6:  スキャンする文字(スートか数字)
;; r7:  スート役(フラッシュ)フラグ(役候補スートのASCIIコードが入る)
;; r8:  ノーペアの数字用フラグ(13ビット使用)
;; r9:  ワンペア、ツーペア用フラグ(13ビット使用)
;; r10: スリーカード用フラグ(13ビット使用)
;; r11: フォーカード用フラグ(13ビット使用)
;;

_main:
        ;; レジスタの初期化
        addi    r4, r4, 4       ; argv[1] にポインタを進める
        lwz     r4, 0(r4)       ; argv[1] の文字列をレジスタにロード
        li      r5, 5           ; 残りカード5枚
        li      r7, 0
        li      r8, 0
        li      r9, 0
        li      r10, 0
        li      r11, 0

_scan_card:
        ;; 一文字目(スート)
        lbz     r6, 0(r4)
        bl      _scan_suit      ; サブルーチン
        addi    r4, r4, 1

        ;; 二文字目(数字)
        lbz     r6, 0(r4)
        bl      _scan_rank      ; サブルーチン

        ;; 繰り返しの準備
        subi    r5, r5, 1
        cmpli   cr7, r5, 0
        beq     cr7, _show_hand ; 5枚チェックし終えたら役を表示する
        addi    r4, r4, 1
        b       _scan_card      ; 繰り返し

;; スートのスキャン
_scan_suit:
        ;; 最初のカードならそのままフラグをセット
        cmpli   cr7, r5, 5
        beq     cr7, _scan_first_suit

        ;; 二枚目以降
        cmpli   cr7, r7, 0      ; フラグがゼロならフラッシュの可能性もゼロ
        beqlr   cr7
        cmpl    cr7, r7, r6     ; 前のスートと同じなら戻る
        beqlr   cr7
        li      r7, 0           ; 前のスートと異なるならフラグをクリア
        blr                     ; サブルーチン終了

;; 最初のカードのスートのスキャン
_scan_first_suit:
        add     r7, r0, r6      ; r0 は 0 とみなされるので、r7 = r6
        blr                     ; サブルーチン終了

;; ランクのスキャン
_scan_rank:
        ;; ASCIIコードを 0-12 の整数にする
        cmpli   cr7, r6, 65     ; 'A'
        beq     cr7, _scan_rank_ace
        cmpli   cr7, r6, 84     ; 'T'
        beq     cr7, _scan_rank_ten
        cmpli   cr7, r6, 74     ; 'J'
        beq     cr7, _scan_rank_jack
        cmpli   cr7, r6, 81     ; 'Q'
        beq     cr7, _scan_rank_queen
        cmpli   cr7, r6, 75     ; 'K'
        beq     cr7, _scan_rank_king
        
        ;; 2-9
        subi    r6, r6, 49
        b       _scan_rank_body

_scan_rank_ace:
        li      r6, 0
        b       _scan_rank_body

_scan_rank_ten:
        li      r6, 9
        b       _scan_rank_body

_scan_rank_jack:
        li      r6, 10
        b       _scan_rank_body

_scan_rank_queen:
        li      r6, 11
        b       _scan_rank_body

_scan_rank_king:
        li      r6, 12
        b       _scan_rank_body

;; r15: ランクのビット表現
;; r16: 論理演算結果
_scan_rank_body:
        li      r0, 1
        slw     r15, r0, r6     ; 左シフト (1<<r6)
        
        ;; フォーカード:同ランクのカードが三枚あるかチェック
        and     r16, r10, r15
        cmpli   cr7, r16, 0
        bgt     cr7, _switch_four_kind_flag

        ;; スリーカード:同ランクのカードが二枚あるかチェック
        and     r16, r9, r15
        cmpli   cr7, r16, 0
        bgt     cr7, _switch_three_kind_flag

        ;; ワンペア:同ランクのカードが一枚あるかチェック
        and     r16, r8, r15
        cmpli   cr7, r16, 0
        bgt     cr7, _switch_one_pair_flag
        
        ;; ノーペア
        or      r8, r8, r15     ; ビットフラグを立てる
        blr                     ; サブルーチン終了

;; ワンペア
_switch_one_pair_flag:
        or      r9, r9, r15     ; ビットフラグを立てる
        xor     r8, r8, r15     ; ノーペアのフラグを下ろす
        blr                     ; サブルーチン終了

;; スリーカード
_switch_three_kind_flag:
        or      r10, r10, r15   ; ビットフラグを立てる
        xor     r9, r9, r15     ; ワンペアのフラグを下ろす
        blr                     ; サブルーチン終了

;; フォーカード
_switch_four_kind_flag:
        or      r11, r11, r15   ; ビットフラグを立てる
        xor     r8, r8, r15     ; スリーカードのフラグを下ろす
        blr                     ; サブルーチン終了

;; 役の表示
_show_hand:
        ;; フラッシュ、ストレートフラッシュ、
        ;; ロイヤルフラッシュのチェック
        cmpli   cr7, r7, 0
        bne     cr7, _check_flushes

        ;; フォーカードのチェック
        andi.   r0, r11, 8191   ; (1<<13)-1
        cmpli   cr7, r0, 0
        bgt     cr7, _show_four_kind

        ;; フルハウス、スリーカードのチェック
        andi.   r0, r10, 8191
        cmpli   cr7, r0, 0
        bgt     cr7, _check_full_house_or_three_kind

        ;; ワンペア、ツーペアのチェック
        andi.   r0, r9, 8191
        cmpli   cr7, r0, 0
        bgt     cr7, _check_one_or_two_pair

        ;; ストレートのチェック
        bl      _check_straight
        cmpli   cr7, r2, 0
        bne     cr7, _show_straight

        ;; ノーペア
        b       _show_no_pair

;; フラッシュ、ストレートフラッシュ、ロイヤルフラッシュ
_check_flushes:
        ;; ロイヤルフラッシュのチェック
        ;; ランク ATJQK のビットマスク
        li      r0, 1|(1<<9)|(1<<10)|(1<<11)|(1<<12)
        and     r2, r8, r0
        cmpl    cr7, r2, r0
        beq     cr7, _show_royal_flush
        
        ;; ストレートフラッシュのチェック
        bl      _check_straight
        cmpli   cr7, r2, 0
        bne     cr7, _show_straight_flush
        
        ;; フラッシュ
        b       _show_flush

;; ストレートのチェック(サブルーチン)
;; ストレートなら r2 > 0 にして戻る
_check_straight:
        ;; ビットマスク 11111 を左にずらしながら9回AND演算する
        li      r0, (1<<5)-1    ; ビットマスク
        li      r2, 0           ; 左シフト数

_find_straight:
        slw     r15, r0, r2     ; 左シフト
        and     r17, r8, r15    ; ノーペア用フラグを調べる
        cmpl    cr7, r17, r15
        beq     cr7, _found_straight
        addi    r2, r2, 1       ; 次のループの準備
        cmpli   cr7, r2, 9
        ble     cr7, _find_straight
        
        li      r2, 0           ; 5つの連続した数字がなかった
        blr                     ; サブルーチン終了

_found_straight:
        li      r2, 1           ; 5つの連続した数字を発見
        blr                     ; サブルーチン終了

;; フルハウスかスリーカード
_check_full_house_or_three_kind:
        ;; ワンペアがあればフルハウス
        andi.   r0, r9, 8191
        cmpli   cr7, r0, 0
        bgt     cr7, _show_full_house
        b       _show_three_kind

;; ワンペアかツーペア
_check_one_or_two_pair:
        li      r0, 1           ; これをシフト
        li      r2, 0           ; 左シフト数
        li      r16, 0          ; ペアの数

;; ペア用フラグからペアの数をカウントする
_find_pairs:
        slw     r15, r0, r2
        and     r17, r9, r15
        addi    r2, r2, 1
        cmpli   cr7, r17, 0
        bgt     cr7, _found_pair
        cmpli   cr7, r2, 13     ; 0-12 の 13 回チェック
        ble     cr7, _find_pairs

        ;; ペアの数で分岐
        cmpli   cr7, r16, 1
        beq     cr7, _show_one_pair     
        b       _show_two_pair

;; 見つけたペアの数を増加
_found_pair:
        addi    r16, r16, 1
        b       _find_pairs     ; 戻る

;; ノーペア
_show_no_pair:
        lis     r4, hi16(no_pair)
        addi    r4, r4, lo16(no_pair)
        li      r5, 8
        b       _print_and_exit

;; ワンペア
_show_one_pair:
        lis     r4, hi16(one_pair)
        addi    r4, r4, lo16(one_pair)
        li      r5, 9
        b       _print_and_exit

;; ツーペア
_show_two_pair:
        lis     r4, hi16(two_pair)
        addi    r4, r4, lo16(two_pair)
        li      r5, 9
        b       _print_and_exit

;; スリーカード
_show_three_kind:
        lis     r4, hi16(three_kind)
        addi    r4, r4, lo16(three_kind)
        li      r5, 16
        b       _print_and_exit

;; ストレート
_show_straight:
        lis     r4, hi16(straight)
        addi    r4, r4, lo16(straight)
        li      r5, 9
        b       _print_and_exit
        
;; フラッシュ
_show_flush:
        lis     r4, hi16(flush)
        addi    r4, r4, lo16(flush)
        li      r5, 6
        b       _print_and_exit 

;; フルハウス
_show_full_house:
        lis     r4, hi16(full_house)
        addi    r4, r4, lo16(full_house)
        li      r5, 11
        b       _print_and_exit 

;; ストレートフラッシュ
_show_straight_flush:
        lis     r4, hi16(straight_flush)
        addi    r4, r4, lo16(straight_flush)
        li      r5, 15
        b       _print_and_exit 

;; フォーカード
_show_four_kind:
        lis     r4, hi16(four_kind)
        addi    r4, r4, lo16(four_kind)
        li      r5, 15
        b       _print_and_exit

;; ロイヤルフラッシュ
_show_royal_flush:
        lis     r4, hi16(royal_flush)
        addi    r4, r4, lo16(royal_flush)
        li      r5, 12
        b       _print_and_exit 

;; 文字列を出力して終了する
;; r4 に文字列のアドレスを、r5 に文字列の長さをセットしておく
_print_and_exit:
        ;; sys_write()
        li      r3, 1           ; 標準出力
        li      r0, 4           ; sys_write
        sc                      ; 呼び出し
        b       _exit

_exit:
        ;; sys_exit()
        li      r3, 0
        li      r0, 1
        sc


;; 定数

        .data
        .align 2

no_pair:
        .asciz  "No pair\n"
        .align  2

one_pair:
        .asciz  "One pair\n"
        .align  2

two_pair:
        .asciz  "Two pair\n"
        .align  2

three_kind:
        .asciz  "Three of a kind\n"
        .align  2

straight:
        .asciz  "Straight\n"
        .align  2

flush:
        .asciz  "Flush\n"
        .align  2

full_house:
        .asciz  "Full house\n"
        .align  2

four_kind:
        .asciz  "Four of a kind\n"
        .align  2

straight_flush:
        .asciz  "Straight flush\n"
        .align  2
        
royal_flush:
        .asciz  "Royal flush\n"
        .align  2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import std.stdio;
import std.string;

string poker(string cards){
    auto suit = "SDHC";
    auto rankOrder = "A23456789TJQK";
    auto transRankOrder = "abcdefghijklm";
    auto transRank = translate(cards, maketrans(rankOrder, transRankOrder), suit).sort;

    int[char] rankCount;
    foreach(c; transRank){
        rankCount[c]++;
    }
    auto pairPattern = rankCount.values.sort;
    auto hand = pairPattern == [1, 1, 1, 2] ? "One pair" :
                pairPattern == [1, 2, 2] ? "Two pair" :
                pairPattern == [1, 1, 3] ? "Three of a kind" :
                pairPattern == [2, 3] ? "Full house" :
                pairPattern == [1, 4] ? "Four of a kind" : "";
    if(hand == ""){
        auto isFlush = cards[0] == cards[2] && cards[0] == cards[4] &&
                       cards[0] == cards[6] && cards[0] == cards[8];
        hand = find(transRankOrder, transRank) != -1 ?
                   (isFlush ? "Straight flush" : "Straight") :
               transRank == (transRankOrder[0] ~ transRankOrder[($ - 4)..$]) ?
                   (isFlush ? "Royal flush" : "Straight") :
               (isFlush ? "Flush" : "No pair");
    }
    return hand;
}

void main(){
    writefln(poker("SQSJSASKST")); // Royal flush
    writefln(poker("D9D7D6D5D8")); // Straight flush
    writefln(poker("C2D2S2H3H2")); // Four of a kind
    writefln(poker("C2D3S2H3H2")); // Full house
    writefln(poker("S9S4S8STSJ")); // Flush
    writefln(poker("C4H7D5S6H3")); // Straight
    writefln(poker("S6H6C5DQC6")); // Three of a kind
    writefln(poker("S6HQC5DQC6")); // Two pair
    writefln(poker("S6H4C5DQC6")); // One pair
    writefln(poker("SJSQSKSAC2")); // No pair
}


	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys

def f(c):
  d = {'A': '1', 'T': 'a', 'J': 'b', 'Q': 'c', 'K': 'd'}
  s = len(set([c[i] for i in range(0,10,2)])) == 1
  r = ''.join(sorted([d[c[i]] if c[i] in d else c[i] for i in range(1,11,2)]))
  l = len(set(r))
  if l == 2:
    print 'Four of a kind' if r.count(r[0]) in [1,4] else 'Full house'
  elif l == 5:
    if '123456789abcd 1abcd'.find(r) >= 0:
      print ('Royal flush 'if r == '1abcd' else 'Straight flush') if s else 'Straight'
    elif s:
      print 'Flush'
    else:
      print 'No pair'
  elif s:
    print 'Flush'
  elif l == 4:
    print 'One pair'
  elif l == 3:
    print 'Three of a kind' if [i for i in set(r) if r.count(i) >= 3] else 'Two pair'

f(sys.argv[1])

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function doukaku121(hand){
  var S = [], R = [], sd = {S:0, H:1, D:2, C:3}, rd = {A:1, T:10, J:11, Q:12, K:13};
  hand.toUpperCase().replace(/([SHDC])([2-9TJQKA])/g, function(_, s, r){
    ++S[s = sd[s]]      || (S[s] = 1);
    ++R[r = rd[r] || r] || (R[r] = 1);
  });
  var flush = /5/.test(S +'');
  if(/1,1,1,1,1/.test(R +','+ R[1]))
    return flush ? (R[1] && R[13] ? 'Royal ' : '') +'Straight Flush' : 'Straight';
  switch((R = R.sort(function(x, y){ return y - x }))[0]){
   case 1: return flush     ? 'Flush'      : 'No Pair';
   case 2: return R[1] == 2 ? 'Two Pair'   : 'One Pair';
   case 3: return R[1] == 2 ? 'Full House' : 'Three of a Kind';
   case 4: return 'Four of a Kind';
  }
}

sedで書けそうな気がするのですが、めんどくさがってsortを使うことにしたのでシェルスクリプトになってしまいました。 行が長いです。ごめんなさい。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/sh

if echo $1 | grep -q -v '^\([CDHS][A2-9TJQK]\)\{5\}$'; then
    echo 'Error: Invalid hand'
    exit 1
fi

declare n=`echo $1 | sed 's/[CDHS]\(.\)/\1\n/g' | tr 'A2-9TJQK' 'a-m' | sort | tr -d '\n' | sed -e '/[a-e]$/y/abcde/ijklm/' -e '/[f-i]$/y/abcdefghi/efghijklm/' -e '/[jk]$/y/abcdefghijk/cdefghijklm/' -e '/l$/y/abcdefghijkl/bcdefghijklm/'`

if echo $1 | grep -q '\(.\).\(\1.\)\{4\}'; then
    echo $n | sed -e 's/ajklm/Royal flush/' -e 's/ijklm/Straight flush/' -e 's/....m/Flush/'
elif echo $n | grep -q '[ai]jklm'; then
    echo Straight
elif echo $n | grep -q '\(.\)\1\1\1'; then
    echo Four of a kind
else
    echo $n | sed -e 's/\(.\)\1\1//' -e 's/\(.\)\1//g' -e 's/^.....$/No pair/' -e 's/^...$/One pair/' -e 's/^..$/Three of a kind/' -e 's/^.$/Two pair/' -e 's/^$/Full house/'
fi

素直に

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#light
open List

let poker cs =
  let is_all_elems_same l = tryfind ((<>) (hd l)) (tl l) |> Option.is_none
  let tally l =
    let h = Hashtbl.create (length l)
    for x in l do
      if Hashtbl.mem h x then Hashtbl.replace h x (Hashtbl.find h x + 1)
                         else Hashtbl.add     h x 1
    h
  (* 手札の情報をスートとランクの 2 つの情報に分離 *)
  let maxi = String.length cs - 1
  let suit = [for i in 0..2..maxi -> cs.[i]]
  let rank = [for i in 1..2..maxi -> String.index "A23456789TJQK" cs.[i] + 1]
             |> sort (-)
  (* 判定基準となる条件役を算出 *)
  let rst = rank = [1;10;11;12;13]
  let flu = is_all_elems_same suit
  let str = is_all_elems_same (mapi (fun i x -> x - i) rank)
  let pnt = [for p in (tally rank) when p.Value > 1 -> p.Value] |> sort (-)
  (* 役判定 *)
  match rst, flu, str, pnt with
    true, true, _,    _     -> "Royal flush"
  | _,    true, true, _     -> "Straight flush"
  | _,    _,    _,    [4]   -> "Four of a kind"
  | _,    _,    _,    [2;3] -> "Full house"
  | _,    true, _,    _     -> "Flush"
  | _,    _,    true, _     -> "Straight"
  | _,    _,    _,    [3]   -> "Three of a kind"
  | _,    _,    _,    [2;2] -> "Two pair"
  | _,    _,    _,    [2]   -> "One pair"
  | _ -> "No pair"

let _ =
  let ($) f g x = f (g x)
  ["SQSJSASKST";"D9D7D6D5D8";"C2D2S2H3H2";"C2D3S2H3H2";"S9S4S8STSJ"
   "C4H7D5S6H3";"S6H6C5DQC6";"S6HQC5DQC6";"S6H4C5DQC6";"SJSQSKSAC2"]
  |> iter (printfn "%s" $ poker)

以下の様にコールすると役を表示します。

echo Poker('SQSJSASKST');

パラメータチェックを怠っています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
function Poker($c)
{
    CSort($c);
    if (IsFlush($c)) {
        if (IsStraight($c)) {
            if ($c[1] == 'A') {
                return 'Royal flush';
            }
            return 'Straight flush';
        }
        return 'Flush';
    } else {
        if (IsStraight($c)) {
            return 'Straight';
        }
        $wk = array();
        $wk[$c[1]]++;
        $wk[$c[3]]++;
        $wk[$c[5]]++;
        $wk[$c[7]]++;
        $wk[$c[9]]++;
        arsort($wk);
        switch (array_shift($wk).array_shift($wk)) {
        case '41':    return 'Four of a kind';
        case '32':    return 'Full house';
        case '31':    return 'Three of a kind';
        case '22';    return 'Two pair';
        case '21';    return 'One pair';
        }
    }
    return 'No pair';
}

function IsFlush($c)
{
    if ($c[0] == $c[2] && $c[0] == $c[4] && $c[0] == $c[6] && $c[0] == $c[8]) {
        return TRUE;
    }
    return FALSE;
}

function IsStraight($c)
{
    $rank = 'A23456789TJQK';
    $num = $c[1].$c[3].$c[5].$c[7].$c[9];
    if (strpos($rank, $num) !== FALSE || $num == 'ATJQK') {
        return TRUE;
    }
    return FALSE;
}

function CSort(&$c)
{
    $rep = array('1'=>'A', '10'=>'T', '11'=>'J', '12'=>'Q', '13'=>'K');
    $wk = array($c[0].$c[1] => $c[1], $c[2].$c[3] => $c[3],
                $c[4].$c[5] => $c[5], $c[6].$c[7] => $c[7],
                $c[8].$c[9] => $c[9]);
    $wk = str_ireplace($rep, array_flip($rep), $wk);
    asort($wk);
    $c = '';
    foreach ($wk as $key => $value) {
        $c .= $key;
    }
}
?>

その後、まじめにsedで書いてみました。こっちのほうがきれいかも。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/sed -f

/^\([CDHS][A2-9TJQK]\)\{5\}$/!b
/\([CDHS][A2-9TJQK]\).*\1/b

/\(.\).\(\1.\)\{4\}/{
    s/^/X/
}

s/[CDHS]//g

s/\([2468TQK]\+\)\([A3579J]\+\)/\2\1/g
s/\([2468TQK]\+\)\([A3579J]\+\)/\2\1/

s/\([3478JQK]\+\)\([A2569T]\+\)/\2\1/g
s/\([3478JQK]\+\)\([A2569T]\+\)/\2\1/

s/\([5-8K]\+\)\([A2349TJQ]\+\)/\2\1/g
s/\([5-8K]\+\)\([A2349TJQ]\+\)/\2\1/

s/\([9TJQK]\+\)\([A2-8]\+\)/\2\1/g
s/\([9TJQK]\+\)\([A2-8]\+\)/\2\1/

/[2-5]$/y/A2345/9TJQK/
/[6-9]$/y/A23456789/56789TJQK/
/[TJ]$/y/A23456789TJ/3456789TJQK/
/Q$/y/A23456789TJQ/23456789TJQK/

s/^\(.\)\1\1\1K$\|^.KKKK$/Four of a kind/

s/\(.\)\1\+//g

s/^$/Full house/
s/^.$/Two pair/
s/^..$/Three of a kind/
s/^...$/One pair/
s/^[A9]TJQK$/Straight/
s/^.....$/No pair/
s/^XATJQK$/Royal flush/
s/^X9TJQK$/Straight flush/
s/^X.....$/Flush/

rankとsuitを分離して扱いました.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def poker(hand)
  def one_pair(rank, count = 0)
    rank.values.map{|x| count += 1 if x.size == 2}
    count == 1
  end
  def two_pair(rank, count = 0)
    rank.values.each{|x| count += 1 if x.size == 2}
    count == 2
  end
  def three_cards(rank)
    rank.values.map{|x| x.size == 3 ? true : nil}.compact.shift
  end
  def straight(rank)
    rank.size == 5 ? rank.keys.sort.first + 4 == rank.keys.sort.last : false
  end
  def flush(suit); suit.size == 1 ;end
  def fourcards(rank)
    rank.values.map{|x| x.size == 4 ? true : nil}.compact.shift
  end
  def convert(char)
    case char
    when 'T': 10
    when 'J': 11
    when 'Q': 12
    when 'K': 13
    when 'A': 14
    else
      char.to_i
    end
  end
  def div_suit_and_rank(hand)
    suit_hash, rank_hash = Hash.new, Hash.new
    cards = hand.split('')
    Array.new(hand.size.div(2)){|i| 2*i}.each{|i|
      suit = cards[i]
      rank = convert(cards[i+1])
      eval("suit_hash[suit] #{(suit_hash[suit] ? '<< rank' : '= [rank]')}")
      eval("rank_hash[rank] #{(rank_hash[rank] ? '<< suit' : '= [suit]')}")
    }
    [suit_hash, rank_hash]
  end
  suit, rank = *div_suit_and_rank(hand)
  if straight(rank)
    "#{rank.keys.sort.first == 10 ? 'Loyal ' : ''}Straight #{(flush(suit) ? 'Flush' : '')}"
  elsif fourcards(rank)
    "Four of a Kind"
  elsif three_cards(rank) and one_pair(rank)
    "Full House"
  elsif flush(suit)
    "Flush"
  elsif three_cards(rank)
    "Three of a Kind"
  elsif two_pair(rank)
    "Two Pair"
  elsif one_pair(rank)
    "One Pair"
  else
    "No Pair"
  end
end
hands = ['SQSJSASKST', 'D9D7D6D5D8', 'C2D2S2H3H2', 'C2D3S2H3H2', 'S9S4S8STSJ', 'C4H7D5S6H3', 'S6H6C5DQC6', 'S6HQC5DQC6', 'S6H4C5DQC6', 'SJSQSKSAC2']
hands.each{|x| puts poker(x)}

ストレートの判定がなかなかきれいにまとまりませんね。ソートしておいて隣り合う数の差で判断してみましたが……うーん。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import Data.List as List

hand :: String -> (String, Bool)
hand cards =
    (case kinds of
       [1, 4] -> "Four of a kind"
       [2, 3] -> "Full house"
       [1, 1, 3] -> "Three of a kind"
       [1, 2, 2] -> "Two pair"
       [1, 1, 1, 2] -> "One pair"
       otherwise ->
           case rdiff of
               [1, 1, 1, 1] | head ranks == 10 && isFlush -> "Royal"
                            | True -> "Straight"
               [1, 1, 1, 9] -> "Straight"
               otherwise -> "No pair"
    , isFlush)
    where suits = List.nub [cards !! n | n <- [0,2..8]]
          isFlush = length suits == 1
          ranks = List.sort [rankToInt $ cards !! n | n <- [1,3..9]]
          rankToInt r = case List.elemIndex r "23456789TJQKA" of Just n -> n+2
          kinds = List.sort $ map length $ List.group ranks
          rdiff = zipWith (-) (tail ranks) ranks

main = mapM test ["SQSJSASKST", "D9D7D6D5D8", "C2D2S2H3H2", "C2D3S2H3H2",
                  "S9S4S8STSJ", "C4H7D5S6H3", "S6H6C5DQC6", "S6HQC5DQC6",
                  "S6H4C5DQC6" ,"SJSQSKSAC2"]
    where test s = putStrLn (s ++ " => " ++ (showHand $ hand s))
          showHand ("No pair", True) = "Flush"
          showHand (s, True) = s ++ " flush"
          showHand (s, False) = s

残念ながらこれだと、xAx2x3x4x5の形のランクの並びがストレートと判断されないようです。

Aは最初または最後とされているみたいですね。


permutationとパターンマッチで簡単に.効率は犠牲に...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pokerHand([X,X,X,X,X], R, 'Royal flush') :-
    permutation(R, ['T','J','Q','K','A']).

pokerHand([X,X,X,X,X], R, 'Straight flush') :-
    sub_atom('A23456789TJQK', _, 5, _, A),
    atom_chars(A, P), permutation(R, P).

pokerHand([X,X,X,X,X], _, 'Flush').

pokerHand(_, R, 'Straight') :-
    sub_atom('A23456789TJQKA', _, 5, _, A),
    atom_chars(A, P), permutation(R, P).

pokerHand(_, R, 'Four of a kind' ) :- permutation(R, [X,X,X,X,_]).
pokerHand(_, R, 'Full house'     ) :- permutation(R, [X,X,X,Y,Y]).
pokerHand(_, R, 'Three of a kind') :- permutation(R, [X,X,X,_,_]).
pokerHand(_, R, 'Two pair'       ) :- permutation(R, [X,X,Y,Y,_]).
pokerHand(_, R, 'One pair'       ) :- permutation(R, [X,X,_,_,_]).
pokerHand(_, _, 'No pair'        ).

poker(Card) :-
    atom_chars(Card, [S1,R1,S2,R2,S3,R3,S4,R4,S5,R5]),
    pokerHand([S1,S2,S3,S4,S5], [R1,R2,R3,R4,R5], H), !,
    writeln(H).

初投稿です。Cleanです。この辺のライブラリを使ってます。

http://sourceforge.net/projects/cleanoptenv

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
module Main

import System, Int, Char, String, List, Misc, MergeSort, ValueCast

Start
  | flush cards
    | straight cards
      | 14 == num (head cards)
        = "Royal flush"
      = "Straight flush"
    = "Flush"
  | straight cards
    = "Straight"
  | 4 == head groups
    = "Four of a kind"
  | 3 == head groups
    | 2 == groups !! 1
      = "Full house"
    = "Three of a kind"
  | 2 == head groups
    | 2 == groups !! 1
      = "Two pair"
    = "One pair"
  = "No pair"
  where
  cards = sortBy (>) $ parseCards getCommandLine.[1]
  groups = sortBy (>) $ map length $ groupCards [[]] 0 cards

:: Card = Card !Char !Int //suit num(2..14)
instance < Card where
  (<) (Card s0 n0) (Card s1 n1) = n0 < n1

suit (Card s n) = s
num (Card s n) = n

parseCards t = p 0
  where
  l = size t
  p i | i >= l = []
      = [Card s n: p (i+2)]
    where
    s = t.[i]
    n = case t.[i+1] of
          'A' = 14
          'K' = 13
          'Q' = 12
          'J' = 11
          'T' = 10
          c   = toInt (c - '0')

groupCards ls _ [] = ls
groupCards [l:ll] m [c=:Card s n: rest]
  | m == n = groupCards [[c:l]:ll] n rest
           = groupCards [[c],l:ll] n rest

flush cards = and $ zipWith (==) suits (tail suits)
  where
  suits = map suit cards

straight cards = (and $ zipWith (\a b = a == b + 1) nums (tail nums))
              || caseAce nums
  where
  nums = map num cards
  caseAce [14,5,4,3,2] = True
  caseAce _ = False

なるほど。permutationでリスト中の順番に関係なくパターンとつきあわせることができるんですか。
ソートとかしなくてもいいんだ。(効率を考えなければ)
勉強になります。

こういうときに便利なスライスの記法を思い出したのと
文字の置換も必要なかったので、ちょっと書き直しました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import sys

def f(c):
  s = len(set(c[0::2])) == 1
  r = ''.join(sorted(c[1::2]))
  l = len(set(r))
  if l == 2:
    print 'Four of a kind' if r.count(r[0]) in [1,4] else 'Full house'
  elif l == 5:
    if '2345A23456789T789JT89JQT9JKQTAJKQT'.find(r) >= 0:
      print ('Royal flush' if r == 'AJKQT' else 'Straight flush') if s else 'Straight'
    elif s:
      print 'Flush'
    else:
      print 'No pair'
  elif s:
    print 'Flush'
  elif l == 3:
    print 'Three of a kind' if [i for i in set(r) if r.count(i) >= 3] else 'Two pair'
  else:
    print 'One pair'

f(sys.argv[1])

出題者です。

初めにこのお題を思いついたときは、ただのif文の山をどう片付けるかという要素しかないものかと思っていたのですが、試しに自分で解いたときに、

(1)フラッシュかどうかをどう判断するか
(2)ストレートかどうかをどう判断するか
(3)ランクの構造はどうなっているか
(4)上記3つの判断要素をどう組み合わせるか

などが、言語によって特色がでそうなので出題してみました。

(4)の処理はHaskell、OCaml、Scalaなどパターンマッチがある言語や、PythonやD言語などリストの一致を簡単に比較できる言語では書きやすそうに見えましたが、#5186 shimakumaさんのJavaScript版はパターンマッチ/リスト比較など無しでかなりコンパクトにまとめていてうまいと思いました。

そんな中、#5187 GEOJさんのA2-9TJQKをa-mに置換して扱うというアイデアが面白かったので、Perlで真似してみました。
ランクの構造は、ソートした後に正規表現を使って調べています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/usr/bin/perl

($_ = shift) =~ tr /A2-9TJQK/a-m/;
$rank = join('', sort(split /[SCDH]/));
$flag = (/^(.).((\1).){4}/) * 4 + ($rank eq "ajklm") * 2 + ("abcdefghijklm" =~ /$rank/);

die "Royal flush\n"     if ($flag == 6);
die "Straight flush\n"  if ($flag == 5);
die "Flush\n"           if ($flag == 4);
die "Straight\n"        if ($flag);
die "Four of a kind\n"  if ($rank =~ /(.)\1{3}/);
die "Full house\n"      if ($rank =~ /((.)\2\2(.)\3)|((.)\5(.)\6\6)/);
die "Three of a kind\n" if ($rank =~ /(.)\1\1/);
die "Two pair\n"        if ($rank =~ /(.)\1.?(.)\2/);
die "One pair\n"        if ($rank =~ /(.)\1/);
die "No pair\n";

確かに, Aを14としたのが敗因でした. straightメソッドに場合分けを追加する必要がありますね.


data使いたがりなhaskellコード。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import System
import List(sort,group,elemIndex)
import Maybe(fromJust)

data Card = Card {suits::Suits,rank::Int} deriving Show
data Suits = S | D | H | C deriving (Read,Eq,Show)
data Hand = RF | STF | FK | FH | F | ST | TK | TP | OP | NO

read_cards :: String -> [Card]
read_cards str = map read_card $ split_2 str
    where 
      read_card [s,r] = Card (read [s])
                        (fromJust (elemIndex r "DD23456789TJQKA"))
      split_2 [] = []
      split_2 list = let (hd,rest) = splitAt 2 list in  hd : split_2 rest

instance Show Hand where
    show h = case h of
               RF  -> "Royal flush"
               STF -> "Straight flush"
               FK  -> "Four of a kind"
               FH  -> "Full House"
               F   -> "Flush"
               ST  -> "Straight"
               TK  -> "Three of a kind"
               TP  -> "Two pair"
               OP  -> "One pair"
               NO  -> "No pair"

hand :: [Card] -> Hand
hand  cards
    | check_st  && check_flush  && head rank_seq == 10 = RF
    | check_st  && check_flush  = STF
    | max_group == 4 = FK
    | sort group_count == [2,3] = FH
    | check_flush  = F
    | check_st = ST
    | max_group ==  3 = TK
    | pair_count == 2 = TP
    | pair_count == 1 = OP
    | otherwise = NO
    where
      rank_seq = sort $ map rank cards
      groups = group rank_seq
      group_count = map length groups
      max_group = foldl1 max group_count
      pair_count = length (filter (\l->length l == 2) groups)

      check_flush = all (\c->suits c==suits (head cards)) cards
      check_st = normal || rank_seq == [2,3,4,5,14]
          where
            normal = and $ zipWith (\a b->a-b == head rank_seq) rank_seq [0..]

main = getArgs >>= print . hand . read_cards . head

-- Test

test = zip testlist (map (hand .read_cards) testlist)

testlist = ["SQSJSASKST",
            "D9D7D6D5D8",
            "C2D2S2H3H2",
            "C2D3S2H3H2",
            "S9S4S8STSJ",
            "C4H7D5S6H3",
            "S6H6C5DQC6",
            "S6HQC5DQC6",
            "S6H4C5DQC6",
            "SJSQSKSAC2"]

短さ優先。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
ranks = []
1.step(10, 2) do |i| ranks << ARGV[0][i, 1] end
rank_hash = Hash.new(0)
ranks.each do |rank| rank_hash[rank] += 1 end
flush = ARGV[0].count(ARGV[0][0,1].to_s) == 5
straight = ("23456789AJKQT".include? ranks.sort!.to_s or "2345A".include? ranks.to_s)
case
when (ranks.to_s == "AJKQT" and flush) then puts "Royal Flush"
when (straight and flush) then puts "Straight Flush"
when rank_hash.values.max == 4 then puts "Four of a Kind"
when (rank_hash.values.max == 3 and rank_hash.values.include? 2) then puts "Full House"
when flush then puts "Flush"
when straight then puts "Straight"
when rank_hash.values.max == 3 then puts "Three of a Kind"
when rank_hash.values.to_s.count("2") == 2 then puts "Two Pair"
when rank_hash.values.max == 2 then puts "One Pair"
else puts "No Pair" 
end

Schemeがないので, 適当なパターンマッチで書いたものを投稿します

エラー判定はしていないので "ASASASASAS"等も受け入れます

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(use srfi-1)
(use util.match)

(define (poker hand)
  (define (rank->num a)
    (cond [(char-set-contains? #[\d] a) (digit->integer a)]
          [(char=? #\A a) 14]
          [(char=? #\T a) 10]
          [(char=? #\J a) 11]
          [(char=? #\Q a) 12]
          [(char=? #\K a) 13]
          [else a]))
  (define (decision nums suits)
    (let1 flags (map - (cdr nums) nums) ;;次のカードとの差
          (cond ((fold (lambda (a r) (and (eq? a (car suits)) r)) #t suits) ;; FLUSH
                 (match flags
                        ((1 1 1 1) (if (= 10 (car nums)) "Royal flush" "Straight flush"))
                        ((1 1 1 9) "Straight flush") ;; 2 3 4 5 14用
                        (else "Flush")))
                (else
                 (match flags
                        ((0 0 0 _) "Four of a kind")
                        ((_ 0 0 0) "Four of a kind")
                        ((0 0 _ 0) "Full house")
                        ((0 _ 0 0) "Full house")
                        ((1 1 1 1) "Straight")
                        ((1 1 1 9) "Straight") ;; 2 3 4 5 14用
                        ((_ _ 0 0) "Three of a kind")
                        ((_ 0 0 _) "Three of a kind")
                        ((0 0 _ _) "Three of a kind")
                        (else
                         (match (sort flags <)
                                ((0 0 _ _) "Two pair")
                                ((0 _ _ _) "One pair")
                                (else "No pair"))))))))
  (receive (nums suits)
           (partition number? (map rank->num (string->list hand)))
           (decision (sort nums <) suits)))

(map poker
     '("SQSJSASKST" "D9D7D6D5D8" "C2D2S2H3H2" "C2D3S2H3H2" "S9S4S8STSJ"
       "C4H7D5S6H3" "S6H6C5DQC6" "S6HQC5DQC6" "S6H4C5DQC6" "SJSQSKSAC2"))
;=>
;("Royal flush" "Straight flush" "Four of a kind" "Full house" "Flush"
; "Straight" "Three of a kind" "Two pair" "One pair" "No pair")

PHPの関数はいっぱいある。
array_count_values は、こんなのあるかな?と思ってマニュアル探したらやっぱりあった。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/php
<?php
$suits = array();
$ranks = array();
for ($i = 0; $i < 10; $i += 2) {
    $suits[] = $argv[1][$i];
    $ranks[] = strtr($argv[1][$i+1], "TJQKA", "ABCDE");
}
sort($suits);
sort($ranks);
$suitCnt = array_count_values($suits);
asort($suitCnt);
$rankCnt = array_count_values($ranks);
arsort($rankCnt);

$straight = false;
$flush = false;
$suitHist = array_values($suitCnt);
if ($suitHist[0] == 5) {
    $flush = true;
}
$rankHist = array_values($rankCnt);
if (strstr("23456789ABCDE,2345E", implode('',$ranks))) {
    $straight = true;
}

$hand = '';
if ($straight && $flush) {
    if (implode('', $ranks) == 'ABCDE') {
        $hand = "Royal straight flush";
    } else {
        $hand = "Straight flush";
    }
} else if ($rankHist[0] == 4) {
    $hand = "Four of a kind";
} else if ($rankHist[0] == 3 && $rankHist[1] == 2) {
    $hand = "Full house";
}
if ($hand == '') {
    if ($flush) {
        $hand = "Flush";
    } else if ($straight) {
        $hand = "Straight";
    } else if ($rankHist[0] == 3) {
        $hand = "Three of a kind";
    } else if ($rankHist[0] == 2 && $rankHist[1] == 2) {
        $hand = "Two pairs";
    } else if ($rankHist[0] == 2) {
        $hand = "One pair";
    } else {
        $hand = "No Pair";
    }
}

echo $hand . "\n";
?>

素直に書いてみた。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class Poker
    def initialize str
        suits=[]
        nums=[]
        str.scan(/[SDHCTJQKA0-9]{2}/).each{|card|
            tmp=card.scan(/./)
            num=case tmp[1]
                when "T": 10
                when "J": 11
                when "Q": 12
                when "K": 13
                when "A": 1
                else tmp[1].to_i
            end
            suits << tmp[0]
            nums << num
        }
        @cards = {
            :suit=>suits,
            :num=>nums,
            :layer => nums.inject({}){|result,n| # [1,2,2,2,3,3,4] => [1,3,2,1]
                result[n]=0 if !result[n]
                result[n]+=1
                result
            }.values
        }
    end

    def one_pair 
        @cards[:layer].max == 2 && @cards[:layer].length == 4
    end

    def two_pair 
        @cards[:layer].max == 2 && @cards[:layer].length == 3 
    end

    def three_card 
        @cards[:layer].max == 3 && @cards[:layer].length == 3 # not full house
    end

    def four_card 
        @cards[:layer].max == 4 && @cards[:layer].length == 2 
    end

    def full_house 
        @cards[:layer].max == 3 && @cards[:layer].length == 2
    end

    def five_card
        @cards[:layer].max == 5 && @cards[:layer].length == 1
    end

    def straight 
        ((1..13).to_a.join(" ")).include?(@cards[:num].sort.join(" "))
    end

    def flush 
        @cards[:suit].uniq.length == 1
    end

    def royal_straight_flush 
        flush() && @cards[:num].sort == [1,10,11,12,13]
    end

    def cards_rank 
        # p @cards
        case
            when (royal_straight_flush()) : "Royal Straight Flush"
            when (straight() && flush()): "Straight Flush"
            when (four_card()): "Four of a kind"
            when (full_house()): "Full house"
            when (flush()) : "Flush"
            when (straight()) : "Straight"
            when (three_card()): "Three of a kind"
            when (two_pair()) : "Two pair"
            when (one_pair()) : "One pair"
            else "No pair"
        end
    end
end

arr="SQSJSASKST D9D7D6D5D8 C2D2S2H3H2 C2D3S2H3H2 S9S4S8STSJ C4H7D5S6H3 S6H6C5DQC6 S6HQC5DQC6 S6H4C5DQC6 SJSQSKSAC2".split(/\s/)


arr.each{|cards|
    puts Poker.new(cards).cards_rank
}


# str=ARGV.shift
# puts poker(str)

しまった。ジョーカーないんだからファイブカードになるはずないし、あったとしてもこのチェックじゃダメだ。


結構綺麗にかけたんじゃないかと自負しています。

パターンマッチ様々です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import List
import Maybe
import System.Environment

card str=  map (\(x:y:_)-> (x,rankInt y)) $ every 2 str

testData = "SQSJSASKST"


allRank = "_A23456789TJQK"
rankInt char =  fromJust (elemIndex char allRank)

every n xs = unfoldr f xs 
    where  
     f [] = Nothing
     f cs = Just (splitAt n cs)



isRoyal xs  =  xs == [1,10,11,12,13]
isStraight xs  = (zipWith (-) (tail xs) xs) == [1,1,1,1] 
same xs = reverse $ sort $ map (\x-> length $ filter (\a-> x == a) xs) (nub xs)

isFlush xs = nub xs == [head xs]

cardAnalysis xs = let ranks    = sort $ map snd xs
                      suits    = map fst xs
                      royal    = isRoyal ranks
                      straight = isStraight ranks
                      sameRanks  = same ranks
                      flush    = isFlush suits
--                  in (flush,royal,straight,sameRanks)
                  in  hand flush royal straight sameRanks


hand True True  False _      = "Royal flush"
hand True False True  _      = "Straight flush"
hand _    _     _    (4:_)   = "Four of a kind"
hand _    _     _    (3:2:_) = "Full house"
hand True _     _     _      = "Flush"
hand _    _     True  _      = "Straight"
hand _    True  _     _      = "Straight"
hand _    _     _    (3:_)   = "Three of a kind"
hand _    _     _    (2:2:_) = "Two Pair"
hand _    _     _    (2:_)   = "One Pair"
hand _    _     _    _       = "No Pair"

main = do args <- getArgs
          putStrLn $ cardAnalysis $ card $ head args

LINQを多用して実現してみました。
これ以上短くできるのかなぁ・・・。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using System;
using System.Collections.Generic;
using System.Linq;

namespace Doukaku
{
    class PorkerJudge
    {
        protected static char[] Suits = new char[] { 'S', 'D', 'H', 'C' };
        protected static char[] Cards = new char[] { '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'J', 'K', 'Q', 'T' };
        public static bool IsStraight( IEnumerable<char> numbers)
        {
            return ("A" + new string(Cards)).IndexOf(new string(numbers.OrderBy(n => n).ToArray())) != -1;
        }

        public static int CardRank(List<char> numbers)
        {
            int value = 1;
            foreach (char n in numbers.Distinct())
            {
                value *= numbers.Count(nn => nn == n);
            }
            return value;
        }

        public static string Run(string cards)
        {
            char[] cAry = cards.ToCharArray();
            bool isFlush = cAry.Where(c => Suits.Contains(c)).Distinct().Count() == 1;
            bool isStraight = IsStraight(cAry.Where(c => Cards.Contains(c)));
            List<char> numbers = cAry.Where(c => Cards.Contains(c)).ToList();
            bool isRoyal = ! numbers.Intersect( Cards.Take(8) ).Any();
            int rank = CardRank(numbers);
            if (isStraight && isFlush && isRoyal)
                return "Royal flush";
            else if (isStraight && isFlush)
                return "Straight flush";
            if ((from n in numbers select numbers.Count(nn => n == nn)).Contains(4))
                return "Four of a kind";
            else if (rank == 6)
                return "Full house";
            else if (isFlush)
                return "Flush";
            else if (isStraight)
                return "Straight";
            else if (rank == 3)
                return "Three of a kind";
            else if (rank == 4)
                return "Two pair";
            else if (rank == 2)
                return "One pair";
            return "No pair";
        }
    }
}

合っているかいるかどうかわからないので全組合せを喰わせてみました。
かなり汚いできではありますが、うまくいっているようです。
皆様のコードを参考にさせていただきました。


結果は
 Royal flush           4   1/649740 
 Straight flush       36 1/72193.33 
 Four of a kind      624     1/4165 
 Full house         3744   1/694.17 
 Flush              5108    1/508.8 
 Straight          10200    1/254.8 
 Three of a kind   54912    1/47.33 
 Two pair         123552    1/21.04 
 One pair        1098240     1/2.37 
 No pair         1302540        1/2 
 total           2598960            
でした。

全組合せは以下で作りました。


suits = ["S","D","H","C"]
rank = (["A"] + ("2".."9").to_a + ["T","J","Q","K"])

card = []

for i in suits.product(rank)
  card << i.join
end

for i in card.combination(5).to_a
  puts i.shuffle.join #一応、シャッフル。
end


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def porker(hand)
  narabi =(["0","A"] + ("2".."9").to_a + ["T","J","Q","K"])
  
  def num(a)
    (["0","A"] + ("2".."9").to_a + ["T","J","Q","K"]).index(a)
  end
  
  tmp = hand.scan(/[SDHCTJQKA2-9]{2}/).map(&:chars).map(&:to_a)
  
  card = Hash.new{|h,k|h[k]=[]}
  for i in tmp
    i[0]
    card[i[0]] << i[1]
  end
  
  suuji = Hash.new(0)
  for i in card.values.flatten
    suuji[i] += 1
  end
  
  case
  when  card.keys.size == 1 && card.values.flatten.sort_by{|a|num(a)}.join(" ") == "A T J Q K"
    "Royal flush"
  when card.keys.size == 1 && %r(#{suuji.keys.sort_by{|a|num(a)}.join(" ")}) =~ narabi.join(" ")
    "Straight flush"
  when suuji.select{|k,v|v == 4}.size == 1
    "Four of a kind"
  when suuji.keys.size == 2
    "Full house"
  when card.keys.size == 1
    "Flush"
  when suuji.keys.size == 5 && (%r(#{suuji.keys.sort_by{|a|num(a)}.join(" ")}) =~ narabi.join(" ") ||
    suuji.keys.sort_by{|a|num(a)}.join(" ") == "A T J Q K")
    "Straight"
  when suuji.select{|k,v|v == 3}.size == 1
    "Three of a kind"
  when suuji.select{|k,v|v == 2}.size == 2
    "Two pair"
  when suuji.select{|k,v|v == 2}.size == 1
    "One pair"
  else
    "No pair"
  end
end

#ruby porker.rb file|SQSJSASKST|なしの場合は__END__以下
if ARGV.size == 1
  if File.exist?(ARGV[0])
    f = open(ARGV[0])
    while f.gets
      hand = $_.chomp
      puts [hand,porker(hand)].join(" ")
    end
  else
    puts [ARGV[0],porker(ARGV[0])].join(" ")
  end
else
  while DATA.gets
      hand = $_.chomp
      puts [hand,porker(hand)].join(" ")
  end
end
__END__
SQSJSASKST
D9D7D6D5D8
C2D2S2H3H2
C2D3S2H3H2
S9S4S8STSJ
C4H7D5S6H3
S6H6C5DQC6
S6HQC5DQC6
S6H4C5DQC6
SJSQSKSAC2

Lost_dogです。もうあと出しじゃんけんすぎるんですが、みなさんの知恵と涙を結集して書き上げました。

特に#8152のtaninswさんのコードが綺麗だったので、ベースにさせていただきました。ポーカーの役は特に規則性がないので、こうやってベタ書きするのが分かりやすいですね。。

Sample Input 1


SQSJSASKST
D9D7D6D5D8
C2D2S2H3H2
C2D3S2H3H2
S9S4S8STSJ
C4H7D5S6H3
S6H6C5DQC6
S6HQC5DQC6
S6H4C5DQC6
SJSQSKSAC2

Sample Output 1


Royal flush
Straight flush
Four of a kind
Full house
Flush
Straight
Three of a kind
Two Pair
One Pair
No Pair

.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import List

main = interact $ unlines.map (hand.readCard).lines

data Card = Card { suit::Char , rank::Int }

readCard :: String -> [Card]
readCard []       = []
readCard (s:r:cs) = Card s r' : readCard cs
  where Just r' = r`elemIndex`"0A23456789TJQK"

isRoyal :: [Card] -> Bool
isRoyal = ([]==).(\\[1,10,11,12,13]).map rank

isFlush :: [Card] -> Bool
isFlush = (1==).length.group.map suit

isStraight :: [Card] -> Bool
isStraight cs = (1==) $ length $ group $ zipWith (-) (tail ds) ds
  where ds = sort $ map rank cs

groups :: [Card] -> [Int]
groups = sortBy (flip compare).map length.group.sort.map rank

hand :: [Card] -> String
hand cs = hand' (isFlush cs) (isRoyal cs) (isStraight cs) (groups cs)
 where hand' True True  True  _      = "Royal straight flush"
       hand' True True  _     _      = "Royal flush"
       hand' True _     True  _      = "Straight flush"
       hand' _    _     _    (4:_)   = "Four of a kind"
       hand' _    _     _    (3:2:_) = "Full house"
       hand' True _     _     _      = "Flush"
       hand' _    _     True  _      = "Straight"
       hand' _    True  _     _      = "Straight"
       hand' _    _     _    (3:_)   = "Three of a kind"
       hand' _    _     _    (2:2:_) = "Two Pair"
       hand' _    _     _    (2:_)   = "One Pair"
       hand' _    _     _    _       = "No Pair"

Index

Feed

Other

Link

Pathtraq

loading...