Consoleアプリでブラックジャックを作成する

2024年7月11日

C#を使用してオブジェクト指向でブラックジャックを作成するためのサンプルコードです。このコードは、Consoleで実行されるコードです。Windows Formsアプリケーションで実行したい場合、必要に応じてデザインとUIコントロールをカスタマイズしてください。

概要

このサンプルコードは、Deck、Card、Player、Gameの4つのクラスで構成されています。Deckクラスはトランプの山札を表し、Cardクラスはトランプのカードを表しています。Playerクラスはプレイヤーを表し、手札の管理やカードのドローなどの操作を行います。Gameクラスはブラックジャックのゲームの進行を管理します。プレイヤーがゲームに参加し、掛け金を決めてカードを配るという流れを表現しています。

このサンプルコードでは、オブジェクト指向の特徴であるカプセル化、継承、ポリモーフィズムを活用しています。例えば、PlayerクラスのGetHandValueメソッドでは、手札の合計値を計算するために、

手札に含まれるカードのランクをすべて取得しています。このように、オブジェクト指向では、関連するデータと操作を1つのオブジェクトにまとめることで、コードの再利用性や拡張性を高めることができます。

また、このサンプルコードでは、プログラム全体をGameクラスでラップすることで、ブラックジャックのゲームを1つのオブジェクトとして扱っています。これにより、ゲームの進行を1つの場所で管理できるため、コードの見通しが良くなり、バグの発生を防ぐことができます。

以上が、オブジェクト指向でブラックジャックを作成するためのサンプルコードの解説となります。ただし、このサンプルコードはあくまで一例であり、実際のアプリケーションに合わせて適宜改良する必要があります。

クラス別説明

Cardクラス

// Cardクラス
public class Card
{
    public string Suit { get; set; }  // スート
    public string Rank { get; set; }  // ランク
    public int Value { get; set; }    // 値

    public Card(string suit, string rank, int value)
    {
        Suit = suit;
        Rank = rank;
        Value = value;
    }
}

このコードは、Cardというクラスを定義しています。

このCardクラスは、トランプのカードを表すオブジェクトです。このクラスには、カードのスート(Suit)、ランク(Rank)、値(Value)の3つのプロパティが定義されています。スートは、カードのマーク(スペード、ハート、ダイヤ、クラブ)を表し、ランクは、カードの数字やジャック、クイーン、キング、エースを表します。値は、ブラックジャックでのカードの点数を表します。

また、Cardクラスには、引数としてスート、ランク、値を取るコンストラクタが定義されています。このコンストラクタを使用することで、新しいカードを作成することができます。

例えば、次のようにして、スペードの2を表すカードを作成することができます。

Card card = new Card("スペード", "2", 2);

このように、Cardクラスは、ブラックジャックのゲームを実装する上で、非常に重要な役割を担っています。このクラスを使用することで、ブラックジャックのカードをオブジェクトとして表現し、簡単かつ効率的なコードを書くことができます。

Deckクラス

// Deckクラス
public class Deck
{
    private List<Card> _cards = new List<Card>();

    public Deck()
    {
        string[] suits = { "Spades", "Hearts", "Clubs", "Diamonds" };
        string[] ranks = { "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King" };
        int[] values = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10 };

        // カードを生成してデッキに追加
        for (int i = 0; i < suits.Length; i++)
        {
            for (int j = 0; j < ranks.Length; j++)
            {
                Card card = new Card(suits[i], ranks[j], values[j]);
                _cards.Add(card);
            }
        }
    }

    // カードをシャッフルする
    public void Shuffle()
    {
        Random random = new Random();
        for (int i = _cards.Count - 1; i > 0; i--)
        {
            int j = random.Next(i + 1);
            Card temp = _cards[i];
            _cards[i] = _cards[j];
            _cards[j] = temp;
        }
    }

    // カードを引く
    public Card DrawCard()
    {
        Card card = _cards[0];
        _cards.RemoveAt(0);
        return card;
    }

    // デッキに残っているカード数を返す
    public int GetRemainingCards()
    {
        return _cards.Count;
    }
}

このコードは、Deck(トランプの山札)を表すDeckクラスを定義しています。

Deckクラスは、_cardsという名前のList<Card>型のフィールドを持っており、このフィールドにはデッキに含まれるカードが格納されます。コンストラクタでは、トランプの各スートとランク、点数を格納した配列を定義し、それらを元に全てのカードを生成して、_cardsフィールドに追加しています。つまり、Deckクラスは、トランプの山札を生成し、カードをシャッフルしたり、引いたりするためのメソッドを提供しています。

Shuffleメソッドは、ランダムにカードをシャッフルするためのメソッドです。このメソッドは、List<Card>型のフィールドである_cardsをシャッフルしています。具体的には、ランダムな数値を生成し、それに対応する位置のカードと、シャッフル対象のカードを入れ替えることで、カードをシャッフルしています。

DrawCardメソッドは、山札からカードを1枚引くためのメソッドです。_deckフィールドから先頭のカードを取得して、それを削除しています。そして、取得したカードを返しています。

GetRemainingCardsメソッドは、デッキに残っているカード数を取得するためのメソッドです。_cardsフィールドのCountプロパティを呼び出して、デッキに残っているカードの数を返しています。

このように、Deckクラスは、トランプの山札を生成して、シャッフルしたり、カードを引いたりするためのメソッドを提供しています。このDeckクラスを利用することで、ブラックジャックのゲームを作成することができます。

Playerクラス

// Playerクラス
public class Player
{
    public string Name { get; private set; }
    public int Chips { get; set; }
    public List<Card> Hand { get; private set; }

    public Player(string name, int chips)
    {
        Name = name;
        Chips = chips;
        Hand = new List<Card>();
    }

    // 手札にカードを加える
    public void AddCardToHand(Card card)
    {
        Hand.Add(card);
    }

    // 手札の合計値を計算する
    public int GetHandValue()
    {
        int value = 0;
        int aceCount = 0;

        foreach (Card card in Hand)
        {
            value += card.Value;
            if (card.Rank == "Ace")
            {
                aceCount++;
            }
        }

        while (aceCount > 0 && value > 21)
        {
            value -= 10;
            aceCount--;
        }

        return value;
    }

    // 手札をリセットする
    public void ResetHand()
    {
        Hand.Clear();
    }
}

Playerクラスは、ブラックジャックゲームにおけるプレイヤーの状態と行動を管理するために設計されています。このクラスは、プレイヤーの名前、所持しているチップの数、および現在の手札を保持します。プレイヤーの名前は、クラスのインスタンス生成時に指定され、その後変更することはできません。チップの数は、プレイヤーが賭けを行ったり、ゲームに勝ったりするたびに増減します。手札はCardオブジェクトのリストとして管理され、プレイヤーがカードを引くたびに手札に追加されます。

Playerクラスには、手札にカードを追加するためのメソッド(AddCardToHand)、手札の合計値を計算するメソッド(GetHandValue)、そして手札をリセットするメソッド(ResetHand)が含まれています。手札の合計値を計算する際、エースのカードはゲームのルールに従って状況に応じて1または11として扱われます。プレイヤーの手札がリセットされると、手札のリストがクリアされ、次のゲームの準備が整います。

このクラスは、ブラックジャックゲームのロジックを実装するために不可欠であり、プレイヤーの状態を管理する中心的な役割を果たします。例えば、プレイヤーがカードを引くたびに手札にカードが追加され、手札の合計値が動的に計算されます。また、ゲームが終了すると手札がリセットされ、次のゲームに備えます。このようにして、Playerクラスはブラックジャックゲームのプレイヤーの状態を効果的に管理し、ゲームの進行をサポートします。

Gameクラス

// Gameクラス
public class Game
{
    private Deck _deck;
    private Player _dealer;
    private Player _player;

    public Game(string playerName, int startingChips)
    {
        _deck = new Deck();
        _deck.Shuffle();

        _dealer = new Player("Dealer", 0);
        _player = new Player(playerName, startingChips);
    }

    // ゲームを開始する
    public void StartGame()
    {
        Console.WriteLine("Welcome to Blackjack!");

        // プレイヤーが掛け金を決める
        Console.Write("Enter your bet: ");
        int bet = int.Parse(Console.ReadLine());
        _player.Chips -= bet;

        // プレイヤーとディーラーに2枚ずつカードを配る
        _player.AddCardToHand(_deck.DrawCard());
        _dealer.AddCardToHand(_deck.DrawCard());
        _player.AddCardToHand(_deck.DrawCard());
        _dealer.AddCardToHand(_deck.DrawCard());

        // プレイヤーの手札を表示する
        Console.WriteLine("Your hand:");
        foreach (Card card in _player.Hand)
        {
            Console.WriteLine("{0} of {1}", card.Rank, card.Suit);
        }

        // ディーラーの手札の1枚目を表示する
        Console.WriteLine("Dealer's hand:");
        Console.WriteLine("{0} of {1}", _dealer.Hand[0].Rank, _dealer.Hand[0].Suit);

        // プレイヤーのターン
        while (true)
        {
            // プレイヤーがヒットするかスタンドするかを選択する
            Console.Write("Do you want to hit or stand? ");
            string choice = Console.ReadLine().ToLower();

            if (choice == "hit")
            {
                // カードを引いて手札に加える
                Card card = _deck.DrawCard();
                _player.AddCardToHand(card);

                // 手札を表示する
                Console.WriteLine("Your hand:");
                foreach (Card c in _player.Hand)
                {
                    Console.WriteLine("{0} of {1}", c.Rank, c.Suit);
                }

                // 手札の合計値が21以上ならバスト
                if (_player.GetHandValue() >= 21)
                {
                    break;
                }
            }
            else if (choice == "stand")
            {
                break;
            }
            else
            {
                Console.WriteLine("Invalid choice. Please enter 'hit' or 'stand'.");
            }
        }

        // ディーラーのターン
        while (_dealer.GetHandValue() < 17)
        {
            // カードを引いて手札に加える
            Card card = _deck.DrawCard();
            _dealer.AddCardToHand(card);
        }

        // プレイヤーとディーラーの手札を表示する
        Console.WriteLine("Your hand:");
        foreach (Card card in _player.Hand)
        {
            Console.WriteLine("{0} of {1}", card.Rank, card.Suit);
        }
        Console.WriteLine("Your hand value: {0}", _player.GetHandValue());
        Console.WriteLine("Dealer's hand:");
        foreach (Card card in _dealer.Hand)
        {
            Console.WriteLine("{0} of {1}", card.Rank, card.Suit);
        }
        Console.WriteLine("Dealer's hand value: {0}", _dealer.GetHandValue());

        // 勝敗を判定する
        if (_player.GetHandValue() > 21)
        {
            Console.WriteLine("You busted! You lose {0} chips.", bet);
        }
        else if (_dealer.GetHandValue() > 21)
        {
            Console.WriteLine("Dealer busted! You win {0} chips.", bet * 2);
            _player.Chips += bet * 2;
        }
        else if (_player.GetHandValue() > _dealer.GetHandValue())
        {
            Console.WriteLine("You win {0} chips!", bet * 2);
            _player.Chips += bet * 2;
        }
        else if (_player.GetHandValue() == _dealer.GetHandValue())
        {
            Console.WriteLine("Push! You get your bet back.");
            _player.Chips += bet;
        }
        else
        {
            Console.WriteLine("You lose {0} chips.", bet);
        }

        // 手札を初期化する
        _player.ResetHand();
        _dealer.ResetHand();

        // もう1度プレイするかどうかを選択する
        Console.Write("Do you want to play again? ");
        string answer = Console.ReadLine().ToLower();

        if (answer == "yes")
        {
            StartGame();
        }
        else
        {
            Console.WriteLine("Thanks for playing!");
        }
    }
}

このコードは、カジノの人気ゲームであるブラックジャックをシミュレートするために使用されるオブジェクト指向のコードです。

Gameクラスは、ブラックジャックのゲームの各要素を表します。_deck変数は、Deckクラスのインスタンスであり、_dealer_playerはそれぞれPlayerクラスのインスタンスであるため、カジノのブラックジャックテーブル上の人物を表します。

StartGame()メソッドは、実際のブラックジャックゲームの流れを反映します。プレイヤーが掛け金を決め、プレイヤーとディーラーに2枚ずつカードを配り、プレイヤーの手札とディーラーの1枚目のカードを表示します。その後、プレイヤーはカードを引いたり、スタンドしたりすることができます。ディーラーは、手札の合計値が17未満になるまでカードを引き続けます。最終的に、勝敗を判定し、プレイヤーの手札を初期化し、プレイヤーがもう一度プレイするかどうかを尋ねます。

Playerクラスは、プレイヤーとディーラーの両方に使用されます。AddCardToHand()メソッドは、プレイヤーまたはディーラーの手札にカードを追加します。GetHandValue()メソッドは、手札のカードの合計値を計算します。ResetHand()メソッドは、手札をリセットして、新しいゲームを始めるために使用されます。

Deckクラスは、トランプのデッキを表します。Shuffle()メソッドは、デッキのカードをシャッフルします。DrawCard()メソッドは、デッキの上から1枚のカードを引き、それを削除します。

このコードは、オブジェクト指向の設計原則を使用しています。各クラスは、一意の責任を持ち、それぞれが独立して機能し、コードのメンテナンスと拡張を容易にします。また、このコードは、リーダブルで理解しやすい構造を持ち、プログラマーが簡単に機能を追加したり、バグを修正したりできるようになっています。

Programクラス

// Programクラス
class Program
{
    static void Main(string[] args)
    {
        Console.Write("Enter your name: ");
        string playerName = Console.ReadLine();
        Console.Write("Enter starting chips: ");
        int startingChips = int.Parse(Console.ReadLine());

        Game game = new Game(playerName, startingChips);
        game.StartGame();

        Console.ReadLine();
    }
}

このコードは、ブラックジャックのゲームを実行するためのエントリーポイントであるMainメソッドを含むProgramクラスです。

Mainメソッドでは、まず、ユーザーに名前と開始チップを入力するように促すために、Console.Writeメソッドを使用して、それぞれのメッセージを表示しています。ユーザーがそれぞれの情報を入力すると、それらの情報を変数playerNameとstartingChipsに代入しています。

その後、Gameクラスのインスタンスを生成しています。このGameクラスのインスタンスを使用して、ゲームの進行を制御します。Gameクラスのコンストラクタには、ユーザーが入力した名前と開始チップの値を渡しています。

次に、game.StartGame()メソッドを呼び出して、ブラックジャックのゲームを開始します。このメソッドは、ゲームのループを実行し、ユーザーがゲームを続けるかどうかを尋ね、適切なアクションを実行します。

最後に、Console.ReadLine()メソッドを使用して、ユーザーがキーを押すまでプログラムを待機しています。これは、プログラムが瞬時に終了するのを防ぐためです。

このように、Programクラスは、ブラックジャックのゲームを実行するために必要な入力を受け取り、Gameクラスのインスタンスを作成してゲームを開始する役割を果たしています。

C#,クラス,学習,設計

Posted by hidepon