解答例 - j2.lesson04.PokerHand

package j2.lesson04;

import j2.lesson04.card.Card;

/**
 * 課題1704 - 解答例 (2/2).
 * ポーカーの手札を表す.
 @author arakawa
 @version $Id: PokerHand_java.rps,v 1.1 2006/03/06 12:56:14 java2005 Exp $
 */
public class PokerHand {

    /** 役なし. */
    private static final int NONE = 0;

    /** ロイヤルストレートフラッシュ. */
    private static final int ROYAL_FLUSH = 1;

    /** ストレートフラッシュ. */
    private static final int STRAIGHT_FLUSH = 2;

    /** フォーカード. */
    private static final int FOUR_OF_KIND = 3;

    /** フルハウス. */
    private static final int FULL_HOUSE = 4;

    /** フラッシュ. */
    private static final int FLUSH = 5;

    /** ストレート. */
    private static final int STRAIGHT = 6;

    /** スリーカード. */
    private static final int THREE_OF_KIND = 7;

    /** ツーペア. */
    private static final int TWO_PAIR = 8;

    /** ワンペア. */
    private static final int ONE_PAIR = 9;

    /** 役の名前. */
    private static final String[] RANK_NAME = {
        "なし""ロイヤルストレートフラッシュ""ストレートフラッシュ""フォーカード""フルハウス",
        "フラッシュ""ストレート""スリーカード""ツーペア""ワンペア"};

    /**
     * 実際の手札。
     */
    private final Card[] cards;
    
    /**
     * 役を表す整数。
     @see #ROYAL_FLUSH
     @see #STRAIGHT_FLUSH
     @see #FOUR_OF_KIND
     @see #FULL_HOUSE
     @see #FLUSH
     @see #STRAIGHT
     @see #THREE_OF_KIND
     @see #TWO_PAIR
     @see #ONE_PAIR
     @see #NONE
     */
    private final int rankOfHand;

    /**
     * 手札を生成する.
     @param c1 1番目のカード
     @param c2 2番目のカード
     @param c3 3番目のカード
     @param c4 4番目のカード
     @param c5 5番目のカード
     */
    public PokerHand(Card c1, Card c2, Card c3, Card c4, Card c5) {
        super();
        this.cards = new Card[] { c1, c2, c3, c4, c5 };
        this.rankOfHand = checkRank();
    }
    
    /**
     * 指定した位置のカードを指定したカードと交換する。
     * 位置は <code>0..4</code> の範囲内で指定する必要がある。
     @param at 交換する位置
     @param card 交換するカード
     @return 指定した位置にあった古いカード
     */
    public Card change(int at, Card card) {
        Card old = this.cards[at];
        this.cards[at= card;
        return old;
    }
    
    /**
     * 指定した位置にあるカードを取得する。
     * 位置は <code>0..4</code> の範囲内で指定する必要がある。
     @param at 取得するカードの位置
     @return カード
     */
    public Card get(int at) {
        return this.cards[at];
    }
    
    /**
     * 現在の手札の役を文字列として取得する。
     @return 手札の役を表す文字列
     */
    public String getRank() {
        return RANK_NAME[this.rankOfHand];
    }

    /**
     * 手札の役を調べる。
     @return 手札の役を表す数値
     @see #ROYAL_FLUSH
     @see #STRAIGHT_FLUSH
     @see #FOUR_OF_KIND
     @see #FULL_HOUSE
     @see #FLUSH
     @see #STRAIGHT
     @see #THREE_OF_KIND
     @see #TWO_PAIR
     @see #ONE_PAIR
     @see #NONE
     */
    private int checkRank() {
        int[] ranks = new int[13];
        // ランクごとの出現枚数を数える
        for (int i = 0; i < this.cards.length; i++) {
            ranks[this.cards[i].getRankAsInt() 1]++;
        }

        boolean flush = hasFlush();
        boolean straight = hasStraight(ranks);
        boolean four = countSameRank(4, ranks0;
        boolean three = countSameRank(3, ranks0;
        int pairs = countSameRank(2, ranks);

        // 強い順に調べる
        if (hasRoyalRanks(ranks&& flush) {
            return ROYAL_FLUSH;
        }
        else if (straight && flush) {
            return STRAIGHT_FLUSH;
        }
        else if (four) {
            return FOUR_OF_KIND;
        }
        else if (three && pairs == 1) {
            return FULL_HOUSE;
        }
        else if (flush) {
            return FLUSH;
        }
        else if (straight) {
            return STRAIGHT;
        }
        else if (three) {
            return THREE_OF_KIND;
        }
        else if (pairs == 2) {
            return TWO_PAIR;
        }
        else if (pairs == 1) {
            return ONE_PAIR;
        }
        else {
            return NONE;
        }
    }

    /**
     * 現在の手札が A, 10, J, Q, K のランクからなるか調べる。
     @param ranks ランクごとの出現枚数
     @return 現在の手札が A, 10, J, Q, K のランクからなるならば <code>true</code>
     */
    private boolean hasRoyalRanks(int[] ranks) {
        return ranks[0== && ranks[9== && ranks[10== && ranks[11== && ranks[12== 1;
    }

    /**
     * 指定した枚数の同じランクを持つカードが何セットあるか調べる。
     @param n 数える枚数
     @param ranks ランクごとの出現枚数
     @return 上記のようなセットの個数
     */
    private int countSameRank(int n, int[] ranks) {
        int count = 0;
        for (int i = 0; i < ranks.length; i++) {
            if (ranks[i== n) {
                count++;
            }
        }
        return count;
    }

    /**
     * カードのランクが5枚連続しているかどうか調べる。
     @param ranks ランクごとの出現枚数
     @return ランクが5枚連続していれば <code>true</code>
     */
    private boolean hasStraight(int[] ranks) {
        // カードが連続した数
        int count = 0;
        for (int i = 0; i < ranks.length; i++) {
            if (ranks[i== 0) {
                // 一度でも 1 を見ていたら、ランクの連続はここで終わり
                if (count >= 1) {
                    break;
                }
            }
            else if (ranks[i== 1) {
                // 1 を連続して見た回数を増やす
                count++;
            }
            else {
                // ペアがあればストレートにはならない
                return false;
            }
        }

        // 5枚連続していればストレート
        if (count == 5) {
            return true;
        }
        else {
            return false;
        }
    }
    
    /**
     * カードのマークが全て同じかどうか調べる。
     @return 全て同じならば <code>true</code>
     */
    private boolean hasFlush() {
        // 最初のカードと一枚でもマークが違えば何もしない
        int firstSuite = this.cards[0].getSuiteAsInt();
        for (int i = 1; i < this.cards.length; i++) {
            if (firstSuite != this.cards[i].getSuiteAsInt()) {
                return false;
            }
        }
        // 全て同じならフラッシュ
        return true;
    }
}