総合練習
変数、条件分岐、配列(またはリスト)、列挙、メソッドとクラスを使い、以下のスクリプト作りましょう
ポーカー手札の判定
参考:ポーカー・ハンドの一覧
目標
手札にある5枚のカードを確認し、評価し、Debug.Logで判定を表示。
例:A♥ A♦ 10♣ 7♦ 3♠ - ワンペア
を表示するのは目標である
解析
かなり大きな課題なので、わかりやすい「ミニ課題」に分割しましょう!
スート
スートは「ハート(Heart)、ダイヤ(Diamond)、クラブ(Club)、スペード(Spade)」のことである。固定である値なので、列挙として表現できるので、「CardSuit」の列挙を作る:
CardSuit列挙(答え)
// カードのスート(列挙)を宣言
public enum CardSuit
{
Heart,
Spade,
Diamond,
Club,
}
カード
カードはスートと数値を持っているし、最後に表示しないといけないので、カードから文字列に変換できるメソッドがほしい。
※ ポイント:数値は1~13まで確保しましょう。
Cardクラス - メンバー変数(答え)
// 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; }
}
}
文字列にする
文字列にするとき「1,11、12、13」は「A、J、Q、K」にする。
// 文字列にするメソッド
public string ConvertToString()
{
}
Cardクラス - ConvertToString メソッド
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;
}
}
ハンド
ハンドはカード5枚のことである。まず、メンバー変数を考えましょう。必ず5枚になるので、伸びるリスト必要なく、ただの配列で十分。
Handクラス - メンバー変数(答え)
// 手札を表すクラス(C#クラス)
public class Hand
{
// カードの配列
Card[] cards = new Card[5]; // 5枚
}
また、それぞれのカード(1枚目、2枚目、3枚目、4枚目、5枚目)の設定できるメソッドもほしい。
// カードを設定
// i: 位置(0~4)
// suit: スート
// number: 数値
public void SetCard(int i, CardSuit suit, int number)
{
}
Handクラス - SetCardメソッド
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;
}
}
評価・判定
つぎ、それぞれのハンドの役を評価しましょう。個別に対応すれば、楽になるでしょう。
枚数を数える
まず、「同じ数値のカード何枚あるのか」はよく現れるパターンであるので、メソッドにしましょう:
// 同じカードの枚数を数える
// number: 数えたい数値(1〜13)
private int CountSameNumber(int number)
{
}
Handクラス - CountSameNumber メソッド
// 同じカードの枚数を数える
// 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;
}
ワンペア
同じ数値のカードが2枚ある? - CountSameNumber を活用!
// ワンペアであるか?
private bool IsOnePair()
{
}
Handクラス - IsOnePair メソッド
// ワンペアであるか?
private bool IsOnePair()
{
// いずれかの2枚のカードは同じ数値だったら、ワンペアになる
for (int i = 1; i <= 13; i++)
{
if (CountSameCard(i) == 2)
return true;
}
return false;
}
ツーペア
ワンペアと似ているが、1つ目のペアの数値は2つ目のペアと異なることが前提条件であるので、ご注意ください!
// ワンペアであるか?
private bool IsTwoPair()
{
}
Handクラス - IsTwoPair メソッド
// ツーペアであるか?
private bool IsTwoPair()
{
// ワンペアと似ているが、2つのペアは異なるカードじゃないとだめ
int match = 0; // 1つ目のペアの数値
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++)
{
// 2枚がある「かつ」前のペアと異なる
if (CountSameCard(i) == 2 && i != match)
return true;
}
return false;
}
スリー・オブ・ア・カインド
ワンペアと似ているが、2枚ではなく、3枚を確認すべき。
// スリー・オブ・ア・カインドであるか?
private bool IsThreeOfAKind()
{
}
Handクラス - IsThreeOfAKind メソッド
// スリー・オブ・ア・カインドであるか?
private bool IsThreeOfAKind()
{
// いずれかの3枚のカードは同じ数値だったら、 スリー・オブ・ア・カインドになる
for (int i = 1; i <= 13; i++)
{
if (CountSameCard(i) == 3)
return 3;
return false;
}
ストレート
これは新しいやつ!すべての数値が連続であることが確認すべき。
ヒント:
- まず、最も数値が小さいカードを求める
- ストレートであれば、以下は事実である:
- 最も小さいカード +0 → 1枚
- 最も小さいカード +1 → 1枚
- 最も小さいカード +2 → 1枚
- 最も小さいカード +3 → 1枚
- 最も小さいカード +4 → 1枚
// ストレートであるか?
private bool IsStraight()
{
}
Handクラス - IsStraight メソッド
// ストレートであるか?
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 は
// 必ず1枚ずつがあるはず
for (int i = smallCard; i < smallCard + 5; i++)
{
// 1つだけ!
if (CountSameNumber(i) != 1)
return false;
}
// 順番に1枚ずつがあった!
return true;
// メモ:A-K-Q-J-10 に対応していない
// 興味がある学生、ご自分でお試しください。
}
フラッシュ
すべてのカードが同じスートであるかを確認しましょう!
// フラッシュであるか?
private bool IsFlush()
{
}
Handクラス - IsFlush メソッド
// フラッシュであるか?
private bool IsFlush()
{
// すべて同じスートでであればOK
CardSuit suit = cards[0].Suit;
// 残りのを確認
foreach(Card c in cards)
{
if (c.Suit != suit)
return false;
}
return true;
}
フールハウス
これはツーペアと似ているが、2枚と2枚ではなく、2枚と3枚のパターンである
// フルハウスであるか?
private bool IsFullHouse()
{
}
Handクラス - IsFullHouse メソッド
// フルハウスであるか?
private bool IsFullHouse()
{
// ツーペアと似ているが、枚数は2枚と3枚になる
int match = 0; // 1つ目のペアの数値
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++)
{
// 3枚がある「かつ」前のペアと異なる
if (CountSameNumber(i) == 3 && i != match)
return true;
}
return false;
}
フォー・オブ・ア・カインド
もうわかるでしょう~
// フルハウスであるか?
private bool IsFullHouse()
{
}
Handクラス - IsFourOfAKind メソッド
// フォー・オブ・ア・カインドであるか?
private bool IsFourOfAKind()
{
// いずれかの4枚のカードは同じ数値だったら、 フォー・オブ・ア・カインドになる
for (int i = 1; i <= 13; i++)
{
if (CountSameNumber(i) == 4)
return true;
return false;
}
ストレートフラッシュ
ストレート「かつ」フラッシュである
// ストレートフラッシュであるか?
private bool IsStraightFlush()
{
}
Handクラス - IsStraightFlush メソッド
// ストレートフラッシュであるか?
private bool IsStraightFlush()
{
// ストレート「かつ」フラッシュなので…
return IsStraight() && IsFlush();
}
結果を表示
最後、強い→弱い順番で各判定を確認し、手札の中身とその結果をコンソールで表示しましょう:
// ハンドの判定し、表示する
public void CheckResult()
{
}
Handクラス - CheckResult メソッド
// ハンドの判定し、表示する
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);
}
確認用のスクリプト
いくつかのハンドを確認してみてください:いくつかのハンドを確認してみてください(ダウンロード)
// 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();
}
}
