ぬるーむ

Unity初心者が誰もが知っているゲームの模倣をしています。個人的な備忘録ですが、入門書を読み終えたばかりの初心者の方は「こんなへなちょこでもいいのか!」「俺の方がうまく作れる」と作成意欲がわいたりするかもしれません。

Unityによる大富豪の作り方 5 ~場に出す役の選択~


スポンサードリンク

場に出す役の選択

  1. 選択候補の選出

    場に出してもよさそうな役の候補を選出します。候補がない場合パスします。
  2. 場に出す役の決定

    候補に挙がった役の中から、出した後の手札評価値が最も低くなる(同値の場合、役ランクが低い)役を出します。

選択候補の選出

親のとき

nullsuke.com で書いた方法で役を取得。単純に優先評価値が最も高い役を選択候補とします。優先評価値が同じ場合はランクの弱い役を選択候補とします。

子のとき

場の役が階段の場合

対象を出した後の手札があがれそうであるかまたは、出した後の手札評価値 < 出す前の手札評価値 + 1であるものを選択候補とします。

場の役がグループの場合

すべての合法役について以下の条件を順に判定していきます。一度候補の可否が決定されたらそれ以降の判定はしません。

  1. 場を流せそうな役でかつ、上がれそうな手札の場合
    選択候補とする。
  2. 以下のいずれかの条件を満たした場合、選択候補とする。
    • 場を流せそうな役でかつ、手札にある役が4未満
    • 場を流せそうな役でかつ、出した後の手札にも場を流せそうな役がある
  3. 出した後の手札にある役が1でかつ、その役評価値が50以下の場合
    選択候補としない。
  4. 出した後の手札が上がれそうな手札の場合
    選択候補とする。
  5. 1やK(革命時は4や5)の単体やグループでありかつ、出した後の手札にある役が4以上でかつ、出した後の手札評価値が0より大きい場合
    選択候補としない。
  6. 出した後の手札評価値 < 出す前の手札評価値 である場合
    選択候補とする。

例1
場が♠️7 ♦️7でどのカードも使われておらず、誰も上がっていなく
手札が♠️3 ♦️10 ♣️10 ♠️2 ♦️2の場合
合法役は[ ♦️10 ♣️10 ] [ ♠️2 ♦️2 ]となる。
[ ♦️10 ♣️10 ]は上記条件の4に該当するので候補となる。
[ ♠️2 ♦️2 ]は上記条件の2に該当するので候補となる。
[ ♦️10 ♣️10 ]を出した後の手札[ ♠️3 ♠️2 ♦️2 ]の手札評価値は0
[ ♠️2 ♦️2 ]を出した後の手札[ ♠️3 ♦️10 ♣️10 ]の手札評価値は3
よって[ ♦️10 ♣️10 ]が選択されます。

例2
場が♠️7でどのカードも使われておらず、誰も上がっていなく
手札が♠️8 ♦️1の場合
合法役は[ ♠️8] [ ♦️1 ]となる。
[ ♠️8 ]は上記条件の4に該当するので候補となる。
[ ♦️1 ]は上記条件の3に該当するので候補とならない。
よって[ ♠️8 ]が選択されます。

例3
場が♠️7でどのカードも使われておらず、誰も上がっていなく
手札が♠️3 ♦️4 ♣️8 ♦️1 ❤️2の場合
合法役は[ ♣️8] [ ♦️1 ] [ ❤️1 ]となる。
[ ♠️8 ]は上記条件の6に該当するので候補となる。
[ ♦️1 ]は上記条件の5に該当するので候補とならない。
[ ❤️2 ]はどの条件にも該当しないので候補とならない。
よって[ ♠️8 ]が選択されます。

実装

public class PlayerLogic
{
    private static BitUtility bit;
    private static ulong[] bitPlayerCards;

    //親のときに役を選択
    private ulong SelectBitHandOnDealer(int id, GameData gameData)
    {
        var bitPlayerCard = bitPlayerCards[id];
        var playerCard = new PlayerCard(bitPlayerCard, gameData);
        var hands = playerCard.Hands;
    
        var ordered = hands.OrderByDescending(h => h.Priority).ThenBy(h => h.Rank)
            .ToList<Hand>();

        return ordered[0].BitHand;
    }

    //子のときに役を選択
    private ulong SelectBitHandOnNoneDealer(int id, GameData gameData)
    {
        var bitPlayerCard = bitPlayerCards[id];
        var playerCard = new PlayerCard(bitPlayerCard, gameData);
        var legalHands = playerCard.GetLegalHands(gameData);
        
        if (bit.IsSequence(gameData.BitFieldCard))
        {
            legalHands.ForEach(h =>
            {
                var next = playerCard.CreateNextPlayerCard(h.BitHand, gameData);

                if (next.CanEndGame || playerCard.Score <= next.Score + 1)
                {
                    h.Selected = true;
                }
            });
        }
        else
        {
            for (int i = 0; i < legalHands.Count; i++)
            {
                var h = legalHands[i];
                var next = playerCard.CreateNextPlayerCard(h.BitHand, gameData);

                if (h.CanEndTurn && playerCard.CanEndGame)
                {
                    h.Selected = true;
                }
                else if (h.CanEndTurn)
                {
                    if (playerCard.Hands.Count < 4 ||
                        next.Hands.Exists(h2 => h2.CanEndTurn))
                    {
                        h.Selected = true;
                    }
                }
                else if (next.Hands.Count == 1 &&
                    next.Hands[0].Score <= 50)
                {
                    continue;
                }
                else if (next.CanEndGame)
                {
                    h.Selected = true;
                }
                else if (IsPrettyHigh(h.BitHand, gameData.IsRevolutionalizing) &&
                    next.Hands.Count >= 4 && next.Score > 0)
                {
                    continue;
                }
                else if (next.Score <= playerCard.Score)
                {
                    h.Selected = true;
                }
            }
        }

        var selected = legalHands.Where(h => h.Selected).ToList<Hand>();

        if (selected.Count == 0) return 0;

        var min = Mathf.Infinity;
        int m = 0;

        //選択候補の中から、出した後の手札評価値が最も低い役を選択
        for (int i = 0; i < selected.Count; i++)
        {
            var next = playerCard.CreateNextPlayerCard(selected[i].BitHand, gameData);
            
            if (next.Score <= min)
            {
                if (next.Score == min)
                {
                    if (selected[i].Rank < selected[m].Rank) m = i;
                }
                else
                {
                    m = i;
                }

                min = next.Score;
            }
        }

        return selected[m].BitHand;
    }

    //Kや1(革命時は4や5)の単体やグループかどうか判定する
    private bool IsPrettyHigh(ulong bitHand, bool isRevolutionalizing)
    {
        var qpCard = bit.ToQPCard(bitHand);

        if (!isRevolutionalizing)
        {
            return (qpCard & 0x0000110000000000ul) > 0;
        }
        else
        {
            return (qpCard & 0x0000000000000110ul) > 0;
        }
    }
}