# 総合練習

変数、条件分岐、配列（またはリスト）、列挙、メソッドとクラスを使い、以下のスクリプト作りましょう

## ポーカー手札の判定

参考：[ポーカー・ハンドの一覧](https://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%BC%E3%82%AB%E3%83%BC%E3%83%BB%E3%83%8F%E3%83%B3%E3%83%89%E3%81%AE%E4%B8%80%E8%A6%A7)

### 目標

手札にある５枚のカードを確認し、評価し、Debug.Logで判定を表示。

例：A♥ A♦ 10♣ 7♦ 3♠ - ワンペア

を表示するのは目標である

### 解析

かなり大きな課題なので、わかりやすい「ミニ課題」に分割しましょう！

#### スート

スートは「ハート（Heart）、ダイヤ（Diamond）、クラブ（Club）、スペード（Spade）」のことである。固定である値なので、[列挙](https://class.illogic.games/books/c/page/f7177#bkmrk-%C2%A0)として表現できるので、「`CardSuit`」の列挙を作る：

<details id="bkmrk-cardsuit%E5%88%97%E6%8C%99%EF%BC%88%E7%AD%94%E3%81%88%EF%BC%89-%2F%2F-%E3%82%AB%E3%83%BC"><summary>CardSuit列挙（答え）</summary>

```c#
// カードのスート（列挙）を宣言
public enum CardSuit
{
	Heart,
	Spade,
	Diamond,
	Club,
}
```

</details>#### カード

カードはスートと数値を持っているし、最後に表示しないといけないので、カードから文字列に変換できるメソッドがほしい。  
 ※ ポイント：数値は１～１３まで確保しましょう。

<details id="bkmrk-card%E3%82%AF%E3%83%A9%E3%82%B9%EF%BC%88%E7%AD%94%E3%81%88%EF%BC%89-using-un"><summary>Cardクラス - メンバー変数（答え）</summary>

```c#
// MonoBehaviour（Unityのクラス）ではないので
public class Card
{
	// カードの数字
	private int number;

	// カードのスート
	private CardSuit suit;

	// カードの数字
	public int Number
	{
		get { return number; }
		set
		{
			// Mathf.Clampは最小値と最大値の制限をかける
			number = Mathf.Clamp(value, 1, 13);
		}
	}

	// カードのスート
	public CardSuit Suit
	{
		get { return suit; }
		set { suit = value; }
	}
}
```

</details>##### 文字列にする

文字列にするとき「１，１１、１２、１３」は「Ａ、Ｊ、Ｑ、Ｋ」にする。

```c#
// 文字列にするメソッド
public string ConvertToString()
{
}
```

<details id="bkmrk-card%E3%82%AF%E3%83%A9%E3%82%B9---converttos"><summary>Cardクラス - ConvertToString メソッド</summary>

```c#
public class Card　//（続き）
{
	// 文字列にするメソッド
	public string ConvertToString()
	{
		string result = "";

		// 数字を文字列にする
		if (number >= 2 && number <= 10) // 2～10までの場合
		{
			result = $"{number}"; // 数字を文字列にする
		}
		else if (number == 1) // 「A」の場合
		{
			result = "A";
		}
		else if (number == 11) // 「J」の場合
		{
			result = "J";
		}
		else if (number == 12) // 「Q」の場合
		{
			result = "Q";
		}
		else // 「K」の場合
		{
			result = "K";
		}

		// 模様も文字列にして、連結する
		switch (suit) // "suit"で切り替える
		{
			case CardSuit.Heart:
				result += "♥"; // ハート
				break;
			case CardSuit.Diamond:
				result += "♦"; // ダイヤ
				break;
			case CardSuit.Club:
				result += "♣"; // クラブ
				break;
			case CardSuit.Spade:
				result += "♠"; // スペード
				break;
		}

		return result;
	}
}
```

</details>#### ハンド

ハンドはカード５枚のことである。まず、メンバー変数を考えましょう。必ず5枚になるので、伸びるリスト必要なく、ただの配列で十分。

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---setcard-%E3%83%A1%E3%82%BD"><summary>Handクラス - メンバー変数（答え）</summary>

```c#
// 手札を表すクラス（C#クラス)
public class Hand
{
    // カードの配列 
    Card[] cards = new Card[5]; // 5枚
}
```

</details>また、それぞれのカード（１枚目、２枚目、３枚目、４枚目、５枚目）の設定できるメソッドもほしい。

```c#
// カードを設定
// i: 位置（0～4）
// suit: スート
// number: 数値
public void SetCard(int i, CardSuit suit, int number)
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---setcard%E3%83%A1%E3%82%BD%E3%83%83"><summary>Handクラス - SetCardメソッド</summary>

```c#
public class Hand //（続き）
{
	// カードを設定
  　// i: 位置（0～4）
    // suit: スート
    // number: 数値
	public void SetCard(int i, CardSuit suit, int number)
	{
		// 範囲を確認
		if (i < 0 || i > 5)
		{
			Debug.Log("無効なカードの位置!");
			return; // なにもしない
		}

		// 新しいカードを作る
		Card card = new Card();
		card.Suit = suit;
		card.Number = number;
		
		// 設定
		cards[i] = card;
	}
}
```

</details>#### 評価・判定

つぎ、それぞれのハンドの役を評価しましょう。個別に対応すれば、楽になるでしょう。

##### 枚数を数える

まず、「同じ数値のカード何枚あるのか」はよく現れるパターンであるので、メソッドにしましょう：

```c#
// 同じカードの枚数を数える
// number: 数えたい数値（1〜13）
private int CountSameNumber(int number)
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---countsamen"><summary>Handクラス - CountSameNumber メソッド</summary>

```c#
// 同じカードの枚数を数える
// number: 数えたい数値（1〜13）
private int CountSameNumber(int number)
{
    // 範囲を確認
    if (number < 1 || number > 13)
    {
        Debug.Log("無効なカードの数値!");
        return 0; // なにもしない
    }
    
    int count = 0;
    foreach (Card c in cards)
    {
        if (c.Number == number)
            count++;
    }

    return count;
}
```

</details>##### ワンペア

同じ数値のカードが２枚ある？ - `CountSameNumber` を活用！

```c#
// ワンペアであるか？
private bool IsOnePair()
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---isonepair-"><summary>Handクラス - IsOnePair メソッド</summary>

```c#
// ワンペアであるか？
private bool IsOnePair()
{
    // いずれかの２枚のカードは同じ数値だったら、ワンペアになる
    for (int i = 1; i <= 13; i++)
    {
        if (CountSameCard(i) == 2)
            return true;
    }
    return false;
}
```

</details>##### ツーペア

ワンペアと似ているが、１つ目のペアの数値は２つ目のペアと異なることが前提条件であるので、ご注意ください！

```c#
// ワンペアであるか？
private bool IsTwoPair()
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---istwopair-"><summary>Handクラス - IsTwoPair メソッド</summary>

```c#
// ツーペアであるか？
private bool IsTwoPair()
{
    // ワンペアと似ているが、２つのペアは異なるカードじゃないとだめ
    int match = 0;  // １つ目のペアの数値
    for (int i = 1; i <= 13; i++)
    {
        if (CountSameCard(i) == 2)
            match = i;
    }
    
    // なにも一致しなかった
    if (match == 0)
        return false;
    
    // もう一つのペアがないか確認する
    for (int i = 1; i <= 13; i++)
    {
        // ２枚がある「かつ」前のペアと異なる
        if (CountSameCard(i) == 2　&& i != match)
            return true;
    }
    
    return false;
}	
```

</details>##### スリー・オブ・ア・カインド

ワンペアと似ているが、２枚ではなく、３枚を確認すべき。

```c#
// スリー・オブ・ア・カインドであるか？
private bool IsThreeOfAKind()
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---isthreeofa"><summary>Handクラス - IsThreeOfAKind メソッド</summary>

```c#
// スリー・オブ・ア・カインドであるか？
private bool IsThreeOfAKind()
{
    // いずれかの３枚のカードは同じ数値だったら、 スリー・オブ・ア・カインドになる
    for (int i = 1; i <= 13; i++)
    {
        if (CountSameCard(i) == 3)
            return 3;
    
    return false;
}	
```

</details>##### ストレート

これは新しいやつ！すべての数値が連続であることが確認すべき。

ヒント：

- まず、最も数値が小さいカードを求める
- ストレートであれば、以下は事実である： 
    - 最も小さいカード +0 → 1枚
    - 最も小さいカード +1 → 1枚
    - 最も小さいカード +2 → 1枚
    - 最も小さいカード +3 → 1枚
    - 最も小さいカード +4 → 1枚

```c#
// ストレートであるか？
private bool IsStraight()
{     
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---isstraight"><summary>Handクラス - IsStraight メソッド</summary>

```c#
// ストレートであるか？
private bool IsStraight()
{
    // 連続になっているのかを確認が必要
    // 例えば: 8-9-10-J(11)-Q(12)
    
    // まず、最低のカードを求めましょう
    int smallCard = 14; // Kよりも高いから始まる
    foreach (Card c in cards)
    {
        if (c.Number < smallCard)
            smallCard = c.Number; // 最低カードを更新
    }
    
    // では、5枚も連続であれば、各カード +1 は
    // 必ず１枚ずつがあるはず
    for (int i = smallCard; i < smallCard + 5; i++)
    {
        // 1つだけ！
        if (CountSameNumber(i) != 1)
            return false;
    }
    
    // 順番に１枚ずつがあった！
    return true;

    // メモ：A-K-Q-J-10 に対応していない
    // 興味がある学生、ご自分でお試しください。		
}
```

</details>##### フラッシュ

すべてのカードが同じスートであるかを確認しましょう！

```c#
// フラッシュであるか？
private bool IsFlush()
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---isflush-%E3%83%A1%E3%82%BD"><summary>Handクラス - IsFlush メソッド</summary>

```c#
// フラッシュであるか？
private bool IsFlush()
{
    // すべて同じスートでであればOK
    CardSuit suit = cards[0].Suit;
    
    // 残りのを確認
    foreach(Card c in cards)
    {
        if (c.Suit != suit)
            return false;
    }
    
    return true;
}
```

</details>##### フールハウス

これはツーペアと似ているが、２枚と２枚ではなく、２枚と３枚のパターンである

```c#
// フルハウスであるか？
private bool IsFullHouse()
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---isfullhous"><summary>Handクラス - IsFullHouse メソッド</summary>

```c#
// フルハウスであるか？
private bool IsFullHouse()
{
    // ツーペアと似ているが、枚数は２枚と３枚になる
    int match = 0;  // １つ目のペアの数値
    for (int i = 1; i <= 13; i++)
    {
        if (CountSameNumber(i) == 2)
            match = i;
    }
    
    // なにも一致しなかった
    if (match == 0)
        return false;
    
    // 残りの3枚を数える
    for (int i = 1; i <= 13; i++)
    {
        // ３枚がある「かつ」前のペアと異なる
        if (CountSameNumber(i) == 3　&& i != match)
            return true;
    }
    
    return false;
}
```

</details>##### フォー・オブ・ア・カインド

もうわかるでしょう～

```c#
// フルハウスであるか？
private bool IsFullHouse()
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---isfourofak"><summary>Handクラス - IsFourOfAKind メソッド</summary>

```c#
// フォー・オブ・ア・カインドであるか？
private bool IsFourOfAKind()
{
    // いずれかの４枚のカードは同じ数値だったら、 フォー・オブ・ア・カインドになる
    for (int i = 1; i <= 13; i++)
    {
        if (CountSameNumber(i) == 4)
            return true;
    
    return false;
}	
```

</details>##### ストレートフラッシュ

ストレート「かつ」フラッシュである

```c#
// ストレートフラッシュであるか？
private bool IsStraightFlush()
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---isstraight-1"><summary>Handクラス - IsStraightFlush メソッド</summary>

```c#
// ストレートフラッシュであるか？
private bool IsStraightFlush()
{
    // ストレート「かつ」フラッシュなので…
    return IsStraight() && IsFlush();
}
```

</details>##### 結果を表示

最後、強い→弱い順番で各判定を確認し、手札の中身とその結果をコンソールで表示しましょう：

```c#
// ハンドの判定し、表示する
public void CheckResult()
{
}
```

<details id="bkmrk-hand%E3%82%AF%E3%83%A9%E3%82%B9---checkresul"><summary>Handクラス - CheckResult メソッド</summary>

```c#
// ハンドの判定し、表示する
public void CheckResult()
{
    // まず、ハンドの文字列を構築
    string result = "";
    foreach (Card c in cards)
    {
        result += c.ConvertToString();
    }

    result += " - ";
    
    // 強い→弱い順番で評価
    if (IsStraightFlush())
    {
        result += "ストレートフラッシュ";
    }
    else if (IsFourOfAKind())
    {
        result += "フォー・オブ・ア・カインド";
    }
    else if (IsFullHouse())
    {
        result += "フルハウス";
    }
    else if (IsFlush())
    {
        result += "フラッシュ";
    }
    else if (IsStraight())
    {
        result += "ストレート";
    }
    else if (IsThreeOfAKind())
    {
        result += "スリー・オブ・ア・カインド";
    }		
    else if (IsTwoPair())
    {
        result += "ツーペア";
    }
    else if (IsOnePair())
    {
        result += "ワンペア";
    }
    else
    {
        result += "ハイカード";
    }
    
    // 最後に表示
    Debug.Log(result);
}
```

</details>### 確認用のスクリプト

いくつかのハンドを確認してみてください（[ダウンロード](https://class.illogic.games/attachments/12)）

```c#
// TestPoker.cs

using UnityEngine;

public class TestPoker : MonoBehaviour
{
    void Start()
    {
        // いろんな組み合わせを試す
        Hand hand = new Hand();

        // フールハウス
        hand.SetCard(0, CardSuit.Heart, 8);
        hand.SetCard(1, CardSuit.Club, 8);
        hand.SetCard(2, CardSuit.Diamond, 8);
        hand.SetCard(3, CardSuit.Heart, 11);
        hand.SetCard(4, CardSuit.Spade, 11);
        hand.CheckResult();
        
        // ストレート
        hand.SetCard(0, CardSuit.Heart, 5);
        hand.SetCard(1, CardSuit.Club, 7);
        hand.SetCard(2, CardSuit.Diamond, 8);
        hand.SetCard(3, CardSuit.Heart, 6);
        hand.SetCard(4, CardSuit.Spade, 9);
        hand.CheckResult();

        // ストレートフラッシュ
        hand.SetCard(0, CardSuit.Spade, 5);
        hand.SetCard(1, CardSuit.Spade, 7);
        hand.SetCard(2, CardSuit.Spade, 8);
        hand.SetCard(3, CardSuit.Spade, 6);
        hand.SetCard(4, CardSuit.Spade, 9);
        hand.CheckResult();
        
        // ツーペア
        hand.SetCard(0, CardSuit.Heart, 5);
        hand.SetCard(1, CardSuit.Diamond, 5);
        hand.SetCard(2, CardSuit.Club, 12);
        hand.SetCard(3, CardSuit.Spade, 12);
        hand.SetCard(4, CardSuit.Diamond, 13);
        hand.CheckResult();
        
        // ハイカード
        hand.SetCard(0, CardSuit.Heart, 5);
        hand.SetCard(1, CardSuit.Diamond, 1);
        hand.SetCard(2, CardSuit.Club, 8);
        hand.SetCard(3, CardSuit.Spade, 12);
        hand.SetCard(4, CardSuit.Diamond, 9);
        hand.CheckResult();
    }
}
```

#### 期待の結果

[![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/Lmeimage.png)](https://class.illogic.games/uploads/images/gallery/2026-04/Lmeimage.png)