今回は、どの役に近いか(リーチしたか)を認識できる機能を実装します。
概要
ポーカーでの役は、大きく分けるとフラッシュ、ストレート、そしてペア系になります。
リーチは、常に今の手持ちより強い役にかかるものとします。
ストレートとフラッシュは、条件を満たすカードが3枚あったらリーチとします。ペアは、2枚以上で常に役になるため、常に何らかのリーチがかかっています。
それぞれ、リーチの条件を見ていきます。
リーチの条件
フラッシュのリーチ
フラッシュはすべてのマークの個数を数え、一番多いマーク数が3以上であればリーチになります。
たとえば、A, 3, 5, 7, 8 を持っていたとして、それぞれのマークを調べると、ハートが1枚、ダイヤが3枚、スペードが1枚でした。この場合、全てのマークで最も枚数が多いのはダイヤで、3枚以上の条件を満たすのでリーチとなります。
これを処理にすると、以下のようになります。
手持ちカード枚数 = 5; マークの種類の数 = 4; リーチ条件数 = 3; void CheckFlush() { // マーク出現数を数える for( i = 0; i < 手持ちカード枚数; i++ ){ マーク出現数[ カード[i]/13 ]++; } // 最大マーク出現数の確認 for( i = 0; i < マークの種類の数; i++ ){ if( 最大マーク出現数 < マーク出現数[i] ){ 最大マーク出現数 = マーク出現数[i]; } } // リーチ条件を満たしているか? if( 最大マーク出現数 == 手持ちカード枚数 ){ // フラッシュ確定 } else if( 最大マーク出現数 >= リーチ条件数 ){ // フラッシュのリーチ } }
心持ち、前回よりスッキリした気がします。
ストレートのリーチ
第2回では、ストレートの判定条件を少し複雑に考えていたのですが、よく考えると、ストレートになる条件は10パターンしかないので、どのパターンに近いかで判定すればよいのでは、と考えました。
先ほどと同じく、A, 3, 5, 7, 8 を持っていたとします。A, 2, 3, 4, 5 のパターンから 10, J, Q, K, A までの 10パターンにどれくらいあてはまっているか調べると、{A, x, 3, x, 5} {3, x, 5, x, 7} {x, 5, x, 7, 8} {5, x, 7, 8, x} でそれぞれ3枚該当があり、ストレートのリーチであることが分かります。
これを処理にすると、以下のようになります。
void CheckStraight() { int i, j; // ナンバー出現数を数える for( i = 0; i < 手持ちカード枚数; i++ ){ ナンバー出現数[ カード[i]%13 ]++; } for( i = 0; i < ストレートパターン数; i++ ){ if( ナンバー出現数[i] != 0 ){ for( j = 0; j < 手持ちカード枚数; j++){ if( ナンバー出現数[(i+j)%13] != 0 ){ ストレート条件一致数[i]++; } } if( ストレート条件一致数[i] == 手持ちカード枚数 ){ // ストレート確定 } else if( ストレート条件一致数[i] >= リーチ条件数 ){ // ストレートのリーチ } } } }
前回の処理に比べると、非常にスッキリしました。
ペアのリーチ
ペアは常にどれかのリーチ状態になります。そのため、いつも次の1枚を待つ状態と考えます。
この記事の冒頭にあった図から、ペアのリーチだけを抜き出すと、以下のようになります。図の青線は、冒頭の図に足りないところを少し足しています。
それぞれの役から伸びている矢印を表にすると、以下のようになります。緑はペア数が1、青はペア数が2です。
ハイカード(役なし)を除いて、もっとも確率の高いワンペアから、フルハウスまで、ほぼ緑と青が点灯することになります。
void ペアのリーチ判定(現在の役) { // 1組系 switch( 現在の役 ){ case フルハウス: case スリーカード: case ツーペア: case ワンペア: // リーチ break; } // 2組系 switch( 現在の役 ){ case スリーカード: case ツーペア: case ワンペア: case ハイカード: // リーチ break; } // ワンペア if( 現在の役 == ハイカード ){ // ワンペアリーチ } }
リーチの表示
現在のリーチ状況を表示する画面を追加します。
どの役をリーチと見ているのか、各リーチの状況を、それぞれランプを点灯させて分かるようにします。
●(赤)フラッシュ
●(橙)ストレート
●(青)2組系
●(緑)1組系:スリーカード以上
●(紫)1組系:ワンペア
Hierarchy
前回追加した山札表示(Deck)の右に、野球のストライクボードのような表示を追加します。名前はReachSignalsとしておきます。
表示は、Panel と Text を Emtpyオブジェクト(Signal)で括った簡易的なものです。
表示用のクラス(ReachSignals.cs)を作って、リーチの情報が来たら点灯・消灯をするようにします(SetSignal)。
コード
// シグナルID public enum SIGNAL_ID { SIG_FLASH, SIG_STRAIGHT, SIG_OVER_2PAIR, SIG_OVER_3_OF_A_KIND, SIG_PAIR, SIG_OFF, }; public class ReachSignals : MonoBehaviour { List<Image> signals = new List<Image>(); List<Color> signalColor = new List<Color> { // 色指定 }; public void SetSignal( SIGNAL_ID id, bool on ) { if( on ){ signals[(int)id].color = signalColor[(int)id]; } else { signals[(int)id].color = signalColor[(int)SIGNAL_ID.SIG_OFF]; } } }
コード
カード管理(CardSuit.cs)
public class CardSuit : MonoBehaviour { /* 略 */ // フラッシュ判定(修正) int MAX_MARKS = 4; // マークの種類の数 int[] markCount = new int[4]; // マーク出現数 int maxMarkCount; // 最大マーク出現数 int maxMarkId; // 最大出現マーク int FLUSH_REACHING_THRESHOLD = 3; // フラッシュリーチしきい値 bool r_flush; // フラッシュのリーチ void CheckFlush() { int i; // マーク出現数クリア for( i = 0; i < MAX_MARKS; i++ ){ markCount[i] = 0; } maxMarkCount = 0; // マーク出現数を数える for( i = 0; i < MAX_CARDS; i++ ){ markCount[ cards[i]/13 ]++; } // 最大マーク出現数の確認 for( i = 0; i < MAX_MARKS; i++ ){ if( maxMarkCount < markCount[i] ){ maxMarkCount = markCount[i]; maxMarkId = i; } } // フラッシュ判定 flush = false; r_flush = false; if( maxMarkCount == MAX_CARDS ){ flush = true; } else if( maxMarkCount >= FLUSH_REACHING_THRESHOLD ){ r_flush = true; } } // ストレート判定(修正) int MAX_NUMBERS = 13; int[] numbersCount = new int[13]; // ナンバー出現数 int MAX_STRAIGHT_COMBINATION_NUMBER = 10; // ストレートパターン数 int[] straightPtnCount = new int[10]; // ストレート条件一致数 int STRAIGHT_REACHING_THRESHOLD = 3; // ストレートリーチしきい値 bool r_straight; // ストレートのリーチ void CheckStraight() { int i, j; // ナンバー出現数クリア for( i = 0; i < MAX_NUMBERS; i++ ){ numbersCount[i] = 0; } // ナンバー出現数を数える for( i = 0; i < MAX_CARDS; i++ ){ numbersCount[ cards[i]%MAX_NUMBERS ]++; } // ストレート判定 straight = false; r_straight = false; for( i = 0; i < MAX_STRAIGHT_COMBINATION_NUMBER; i++ ){ straightPtnCount[i] = 0; if( numbersCount[i] != 0 ){ for( j = 0; j < MAX_CARDS; j++){ if( numbersCount[(i+j)%MAX_NUMBERS] != 0 ){ straightPtnCount[i]++; } } if( straightPtnCount[i] == MAX_CARDS ){ straight = true; } else if( straightPtnCount[i] >= STRAIGHT_REACHING_THRESHOLD ){ r_straight = true; } } } } // リーチ判定(追加) void CheckWaitingHands(HAND_NAME hand) { if( ReferenceEquals( reachSignals, null ) ) { return; } // フラッシュ点灯 reachSignals.SetSignal( SIGNAL_ID.SIG_FLASH, r_flush ); // ストレート点灯 reachSignals.SetSignal( SIGNAL_ID.SIG_STRAIGHT, r_straight ); // ペア1組系点灯 switch( hand ){ case HAND_NAME.THREE_OF_A_KIND: // four card, fullhouse case HAND_NAME.PAIR: // three card, two pair case HAND_NAME.FULL_HOUSE: // four card case HAND_NAME.TWO_PAIR: // three card, fullhouse reachSignals.SetSignal( SIGNAL_ID.SIG_OVER_3_OF_A_KIND, true ); break; default: reachSignals.SetSignal( SIGNAL_ID.SIG_OVER_3_OF_A_KIND, false ); break; } // ペア2組系点灯 switch( hand ){ case HAND_NAME.PAIR: // three card, two pair case HAND_NAME.TWO_PAIR: // three card, fullhouse reachSignals.SetSignal( SIGNAL_ID.SIG_OVER_2PAIR, true ); break; default: reachSignals.SetSignal( SIGNAL_ID.SIG_OVER_2PAIR, false ); break; } // ワンペア点灯 if( hand == HAND_NAME.HIGH_CARD ){ reachSignals.SetSignal( SIGNAL_ID.SIG_PAIR, true ); } else { reachSignals.SetSignal( SIGNAL_ID.SIG_PAIR, false ); } } // トリガ(リーチ処理を追加しました) public void ChangeCards( bool force ) { // 第2回、CheckHands() の後に以下追加しました // リーチ表示 CheckWaitingHands( hand ); /* 以下 略 */ } }
リーチ表示(ReachSignals.cs)
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public enum SIGNAL_ID { SIG_FLASH, SIG_STRAIGHT, SIG_OVER_2PAIR, SIG_OVER_3_OF_A_KIND, SIG_PAIR, SIG_OFF, }; public class ReachSignals : MonoBehaviour { List<Image> signals = new List<Image>(); List<Color> signalColor = new List<Color> { Color.red, new Color(1.0f, 0.5f, 0.0f, 1.0f), // orange Color.blue, Color.green, new Color(0.8f, 0.0f, 0.8f, 1.0f), // purple Color.gray, }; public void SetSignal( SIGNAL_ID id, bool on ) { if( on ){ signals[(int)id].color = signalColor[(int)id]; } else { signals[(int)id].color = signalColor[(int)SIGNAL_ID.SIG_OFF]; } } // Start is called before the first frame update void Start() { Image image; foreach( Transform child in gameObject.transform ){ foreach( Transform gc in child ){ image = gc.gameObject.GetComponent<Image>(); if( !ReferenceEquals( image, null ) ){ signals.Add( image ); } } } int i = 0; foreach( Image im in signals ){ SetSignal( (SIGNAL_ID)i, false ); i++; } } }
プレイ画面
(前回までと同じURLです。最新の状態に更新されています)
tomo-mana.hatenablog.com
次回
●どのカードがリーチしているのか(表示)
●どのカードが来たら揃うのか(処理、表示)
本記事に使用しているトランプの絵柄は、いらすとやさんの素材を使用させていただいています。ありがとうございます。
トランプのイラスト(54枚まとめ) | かわいいフリー素材集 いらすとや