C#

C# BlackJack

하길 2024. 1. 8. 13:01

최근 진행하고 있는 스파르타 부트캠프 과제로 블랙잭 만들기가 주어졌습니다.

따라서 오늘은 C# 콘솔 환경에서 블랙잭 게임을 구현 해보겠습니다.

 

1. 블랙잭 규칙

한게임: 포커

 

블랙잭 게임은 카지노에서 흔히 볼 수 있는 게임으로 카드의 합이 21에 가까운 사람이 승리하는 간단한 규칙의 게임입니다.

게임이 시작되면 딜러와 플레이어는 카드를 2장씩 받게 됩니다.

만약 이때 플레이어의 카드의 합이 21이면 그 즉시 블랙잭으로 승리하게 됩니다. 

단, 딜러 카드의 합 또한 21이면 무승부 처리됩니다.

 

딜러는 무조건 카드의 합이 17이상 되도록 카드를 뽑아야하기 때문에 어떻게 보면 플레이어가 조금 유리한 게임이라고

할 수 있습니다. 하지만, 딜러와 플레이어 모두 21이 넘었을 경우에는 무조건 플레이어의 패배이기 때문에 플레이어는 이점을 유의하면서 플레이해야합니다.

 

이외의 여러가지 룰이 존재하지만 간단하게 만들어보는 것이 목표이기 때문에 따로 구현하지 않겠습니다.

 

 - 과제의 요구사항

1. 블랙잭 게임은 1명의 플레이어와 1명의 딜러가 참여합니다.

2. 게임 시작 시, 플레이와 딜러는 각각 두 장의 카드를 받습니다.

3. 플레이어는 21점이 넘지 않는 한 계속해서 카드를 받을 수 있습니다.

4. 딜러는 카드의 합이 17점이 되거나 넘을 때까지 계속해서 카드를 받아야 합니다.

5. 카드를 더 이상 받지 않는 플레이어와 딜러 중 카드 합이 21점에 더 가까운 쪽이 승리합니다.

6. 21점을 초과하면 패배합니다.

 

2. 구현 내용

이번 과제는 제공된 코드가 굉장히 잘 되어있고 규칙도 간단해서 어렵지 않게 구현했던 것 같습니다.

따라서 굳이 풀어서 설명하지 않고 코드만 보여줘도 구현 의도가 보이기 때문에 아래 구현 코드로 대체하겠습니다.

 

-구현 코드

더보기
namespace BlackJack
{
    using System;
    using System.Collections.Generic;
    using System.Security.Cryptography.X509Certificates;

    public enum Suit { Hearts, Diamonds, Clubs, Spades }
    public enum Rank { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace }

    // 카드 한 장을 표현하는 클래스
    public class Card
    {
        public Suit Suit { get; private set; }
        public Rank Rank { get; private set; }

        public Card(Suit s, Rank r)
        {
            Suit = s;
            Rank = r;
        }

        public int GetValue()
        {
            if ((int)Rank <= 10)
            {
                return (int)Rank;
            }
            else if ((int)Rank <= 13)
            {
                return 10;
            }
            else
            {
                return 11;
            }
        }

        public override string ToString()
        {
            return $"{Rank} of {Suit}";
        }
    }

    // 덱을 표현하는 클래스
    public class Deck
    {
        private List<Card> cards;

        public Deck()
        {
            cards = new List<Card>();

            foreach (Suit s in Enum.GetValues(typeof(Suit)))
            {
                foreach (Rank r in Enum.GetValues(typeof(Rank)))
                {
                    cards.Add(new Card(s, r));
                }
            }

            Shuffle();
        }

        public void Shuffle()
        {
            Random rand = new Random();

            for (int i = 0; i < cards.Count; i++)
            {
                int j = rand.Next(i, cards.Count);
                Card temp = cards[i];
                cards[i] = cards[j];
                cards[j] = temp;
            }
        }

        public Card DrawCard()
        {
            Card card = cards[0];
            cards.RemoveAt(0);
            return card;
        }
    }

    // 패를 표현하는 클래스
    public class Hand
    {
        private List<Card> cards;

        public Hand()
        {
            cards = new List<Card>();
        }

        public void AddCard(Card card)
        {
            cards.Add(card);
        }

        public int GetTotalValue()
        {
            int total = 0;
            int aceCount = 0;

            foreach (Card card in cards)
            {
                if (card.Rank == Rank.Ace)
                {
                    aceCount++;
                }
                total += card.GetValue();
            }

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

            return total;
        }

        //보유중인 카드 이름 얻기
        public string GetHaveCradsName()
        {
            string names = "";

            foreach(Card card in cards)
            {
                names += ", " + card.ToString();
            }

            return names;
        }
    }

    // 플레이어를 표현하는 클래스
    public class Player
    {
        public Hand Hand { get; private set; }

        public Player()
        {
            Hand = new Hand();
        }

        //덱에서 카드를 뽑아서 핸드에 추가
        public Card DrawCardFromDeck(Deck deck)
        {
            Card drawnCard = deck.DrawCard();
            Hand.AddCard(drawnCard);
            return drawnCard;
        }
    }

    // 딜러 클래스를 작성하고, 딜러의 행동 로직을 구현하세요.
    public class Dealer : Player
    {
        //핸드의 합이 17 이상이 될 때까지 카드 뽑기
        public void DrawRemainCard(Deck _deck)
        {
            while(Hand.GetTotalValue() < 17)
            {
                DrawCardFromDeck(_deck);
            }
        }
    }

    // 블랙잭 게임을 구현하세요. 
    public class Blackjack
    {
        //참가자
        public Player player = new Player();
        public Dealer dealer = new Dealer();

        //덱
        public Deck deck = new Deck();

        //승리자
        public bool isPlayerWin = false;
        public bool isDealerWin = false;

        // 코드를 여기에 작성하세요

        //게임시작
        public void StartGame()
        {
            //카드 그만 받기
            bool stopDraw = false;

            //플레이어와 딜러가 두장 씩 카드를 뽑음
            player.DrawCardFromDeck(deck);
            dealer.DrawCardFromDeck(deck);
            player.DrawCardFromDeck(deck);
            dealer.DrawCardFromDeck(deck);

            //블랙잭이 존재한다면 즉시 게임 종료
            if (player.Hand.GetTotalValue() == 21 || dealer.Hand.GetTotalValue() == 21)
                GameEnd(player, dealer);

            //플레이어는 21점이 넘지 않는 한 카드를 계속 받을 수 있음
            while (player.Hand.GetTotalValue() <= 21 && !stopDraw)
            {
                Console.Clear();
                Console.WriteLine("블랙잭");
                Console.WriteLine();
                Console.WriteLine("현재 보유중인 카드 : {0}", player.Hand.GetHaveCradsName());
                Console.WriteLine("현재 패의 합계 : {0}", player.Hand.GetTotalValue());
                Console.WriteLine("카드를 더 받으시겠습니까?");
                Console.WriteLine();
                Console.WriteLine("1. 예");
                Console.WriteLine("2. 아니오");
                string input = Console.ReadLine();

                switch (input)
                {
                    case "1":
                        player.DrawCardFromDeck(deck);
                        break;

                    case "2":
                        stopDraw = true;
                        break;
                }

                dealer.DrawRemainCard(deck);
            }

            //플레이어의 카드 합이 21이 넘었을 경우
            if(player.Hand.GetTotalValue() > 21)
            {
                Console.Clear();
                Console.WriteLine("블랙잭");
                Console.WriteLine();
                Console.WriteLine("현재 보유중인 카드 : {0}", player.Hand.GetHaveCradsName());
                Console.WriteLine("현재 패의 합계 : {0}", player.Hand.GetTotalValue());
                Console.WriteLine();
                Console.WriteLine("보유중인 카드합이 21이 넘어 더 이상 카드를 뽑으실 수 없습니다.");
                Console.WriteLine();
                Console.WriteLine("게임 결과를 확인하시려면 아무키나 입력하세요.");
                Console.ReadLine();
            }

            //게임 끝
            GameEnd(player, dealer);
        }

        //게임 결과 보여주기
        public void GameEnd(Player _player, Dealer _dealer)
        {
            //게임 결과 확인
            GameReferee(_player, _dealer);
            Console.Clear();
            Console.WriteLine("블랙잭");
            Console.WriteLine();
            Console.WriteLine("게임결과");
            Console.WriteLine("---------------------------------------------------------------------------");
            Console.WriteLine();

            Console.WriteLine("플레이어 보유카드 : {0}", _player.Hand.GetHaveCradsName());
            Console.WriteLine("플레이어 합 : {0}", _player.Hand.GetTotalValue());
            Console.WriteLine();
            Console.WriteLine("딜러 보유카드 : {0}", _dealer.Hand.GetHaveCradsName());
            Console.WriteLine("딜러 합 : {0}", _dealer.Hand.GetTotalValue());
            Console.WriteLine();
            if (isPlayerWin)
                Console.WriteLine("플레이어의 승리!!");
            else if (isDealerWin)
                Console.WriteLine("딜러의 승리!!");
            else { Console.WriteLine("무승부!!"); }
        }

        //게임 심판
        public void GameReferee(Player _player, Dealer _dealer)
        {
            int playerTotal = _player.Hand.GetTotalValue();
            int dealerTotal = dealer.Hand.GetTotalValue();

            //둘다 21점 이하일 경우 점수가 더 적은 사람이 승리
            if ((playerTotal <= 21 && dealerTotal <= 21))
            {
                int playerScore = Math.Abs(21 - playerTotal);
                int dealerScore = Math.Abs(21 - dealerTotal);

                if (playerScore < dealerScore)
                    isPlayerWin = true;
                else if (dealerScore < playerScore)
                    isDealerWin = true;
            }

            //딜러의 승리 조건
            else if ((playerTotal > 21 && dealerTotal <= 21) || playerTotal > 21)
                isDealerWin = true;

            //플래이어의 승리 조건
            else if (dealerTotal > 21 && playerTotal <= 21 && !isDealerWin)
                isPlayerWin = true;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Blackjack blackjack = new Blackjack();

            Console.WriteLine("블랙잭");
            Console.WriteLine();
            Console.WriteLine("게임을 시작하려면 아무키나 입력하세요.");
            Console.ReadLine();

            // 블랙잭 게임을 실행하세요
            blackjack.StartGame();
        }
    }
}

 

3. 시연영상

 

4. 후기

이번 과제를 하면서 많이 배웠던 점은 Card, Deck, Hand, Player, Dealer, BlackJack와 같이 생각보다 정말 세세하게

Class를 나눠서 작업을 한다는 점을 배웠던 것 같습니다. 또한 나눈 객체들 마다 필요한 것들이 전부 담겨있으니

이후 Blackjack이라는 하나의 게임을 만들 때 훨씬 수월하게 게임을 만들 수 있었던 것 같습니다.

 

앞으로 어떤 게임을 만들려고 할 때, 게임에 필요한 것들을 생각한 뒤, 여러가지의 객체로 쪼개서 필요한 것들을 구현하고

이후 만든 객체들을 필요에 따라 조립해 하나의 게임을 만드는 것이 좋겠다는 생각을 했습니다.