20231108_기록_블랙잭 게임

2023. 11. 8. 20:24IT/TIL

오늘 한 것들

 

학습법 특강 참가

C# 문법 종합반 3주차 과제 - 2. 블랙잭 게임

C# 문법 종합반 4주차

C# 문법 종합반 5주차

 

오늘의 주제는 블랙잭 게임인데 우선 아래에 완성된 코드를 입력하고 시작하면,

더보기
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Linq;

// 블랙잭 게임 만들기
// 1 vs 1의 대결 게임 (턴 방식)
// 게임 시작 시에 각각 2장의 카드를 받고 자신의 카드를 확인
// 플레이어는 자신의 카드를 확인한 후에 더 받을 지 선택할 수 있음
// 딜러는 카드의 합이 17점이 되거나 넘을 때가지 계속해서 카드를 받아야 됨
// 카드는 각각 한 장씩 받아야됨(순서대로 받아야됨)
// 21점을 초과하면 패배하는데, 21점에 최대한 가까워야 승리
// 2 ~ 10 -> 각 숫자가 점수 J, Q, K -> 10점, A 11점(A는 1점도 사용 가능하게 구현해보기)


// 구현의 순서

// 1. class card 만들기 (4종류의 모양, 13개의 숫자)
// 2. class deck 만들기 위의 card를 가지고 총 덱 만들기
// 3. class hand 만들기 각 플레이어의 손 패 만들기
// 4. class player 만들기
// 5. class dealer 만들기
// 6. Black Jack 게임 만들기 (위의 것들로 게임에 필요한 함수, UI 만들기)


public enum Suit { 하트, 다이아몬드, 클로버, 스페이드 }
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 $"{Suit} {Rank}";
    }
}

// 덱을 표현하는 클래스
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 List<Card> GetCard()
    {
        return cards;
    }


}

// 플레이어를 표현하는 클래스
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
{
    public bool ShouldHit()
    {
        return Hand.GetTotalValue() < 17;
    }

    public void PlayTurn(Deck deck)
    {
        while (ShouldHit())
        {
            DrawCardFromDeck(deck);
        }
    }
}

public class Blackjack
{
    private Dealer dealer;
    private Player player;
    private Deck deck;
    private bool WaitEnd;
    private int dealerScore;
    private int playerScore;

    public Blackjack()
    {
        dealer = new Dealer();
        player = new Player();
        deck = new Deck();
        WaitEnd = false;
    }

    public void RunGame()
    {
        Console.WriteLine("블랙잭 게임을 시작합니다!");

        // 딜러와 플레이어에게 카드를 배분
        dealer.DrawCardFromDeck(deck);
        dealer.DrawCardFromDeck(deck);

        player.DrawCardFromDeck(deck);
        player.DrawCardFromDeck(deck);
    }

    public void CheckCard()
    {
        dealerScore = dealer.Hand.GetTotalValue();
        playerScore = player.Hand.GetTotalValue();

        Console.WriteLine("\n딜러의 손패 :");
        foreach (Card card in dealer.Hand.GetCard())
        {
            Console.WriteLine(card.ToString());
        }
        Console.WriteLine($"딜러의 점수 : {dealerScore}");

        Console.WriteLine("\n플레이어의 손패 :");
        foreach (Card card in player.Hand.GetCard())
        {
            Console.WriteLine(card.ToString());
        }
        Console.WriteLine($"플레이어의 점수 : {playerScore}");

    }
    public void CheckScore()
    {
        if (playerScore == 21 && dealerScore != 21)
        {
            Console.WriteLine("\n축하합니다. 당신의 승리입니다");
            Console.ReadLine();
            Environment.Exit(0);
        }
        else if (playerScore == 21 && dealerScore == 21)
        {
            Console.WriteLine("\n무승부입니다");
            Console.WriteLine("게임을 다시 시작합니다.");
            // 아무키를 눌러도 다시 시작되게
        }
        else if (dealerScore == 21)
        {
            Console.WriteLine("\n딜러의 승리입니다");
            Console.ReadLine();
            Environment.Exit(0);
        }
        else if (playerScore > 21)
        {
            Console.WriteLine("\n당신의 패배입니다");
            Console.ReadLine();
            Environment.Exit(0);
        }
        else if (dealerScore > 21)
        {
            Console.WriteLine("\n축하합니다. 당신의 승리입니다");
            Console.ReadLine();
            Environment.Exit(0);
        }

    }

    public void EndGame()
    {
        System.Threading.Thread.Sleep(300);

        if ((dealerScore > 16) && (dealerScore > playerScore) && (dealerScore < 22))
        {
            Console.WriteLine("\n딜러의 승리입니다");
            Console.ReadLine();
            Environment.Exit(0);
        }
        else if ((dealerScore > 16) && (playerScore > dealerScore))
        {
            Console.WriteLine("\n축하합니다. 당신의 승리입니다");
            Console.ReadLine();
            Environment.Exit(0);
        }
        else if ((dealerScore > 16) && (playerScore == dealerScore))
        {
            Console.WriteLine("\n무승부입니다");
            Console.ReadLine();
            Environment.Exit(0);
        }
        else if ((dealerScore) > 21)
        {
            Console.WriteLine("\n축하합니다. 당신의 승리입니다");
            Console.ReadLine();
            Environment.Exit(0);
        }

    }

    public void DrawCardPlayer(ConsoleKeyInfo key)
    {
        if (key.Key == ConsoleKey.Spacebar || key.Key == ConsoleKey.Z)
        {
            Console.WriteLine("카드를 드로우 합니다");
            player.DrawCardFromDeck(deck);
            CheckCard();
            CheckScore();
            return;
        }
        else if (key.Key == ConsoleKey.X)
        {
            WaitEnd = true;

            while (WaitEnd == true)
            {
                Console.WriteLine("\n딜러의 행동을 기다립니다"); 
                System.Threading.Thread.Sleep(300);

                while (dealerScore < 17)
                {
                    Console.WriteLine("딜러가 카드를 뽑습니다");
                    dealer.DrawCardFromDeck(deck);
                    CheckCard();
                    System.Threading.Thread.Sleep(500);
                }

                Console.WriteLine("딜러가 카드를 뽑지 않습니다");
                Console.WriteLine("\n점수를 비교합니다");
                EndGame();
                
            }
        }

        else if (key.Key == ConsoleKey.S)
        {
            CheckCard();
        }

        else if (key.Key == ConsoleKey.R)
        {
            ReStart();
        }

        else if (key.Key == ConsoleKey.Escape)
        {
            Console.WriteLine("게임을 종료합니다");
            Console.ReadLine();
            Environment.Exit(0);
        }
        else
        {
            Console.WriteLine("\n올바른 키를 입력해주세요");
            Console.WriteLine("카드를 드로우하려면 Z나 스페이스를");
            Console.WriteLine("딜러와 점수를 비교하려면 X를");
            Console.WriteLine("카드를 확인하려면 S를");
            Console.WriteLine("게임을 재시작하려면 R을");
            Console.WriteLine("게임을 종료하려면 Esc키를 눌러주세요");
        }
    }

    public void DealerWord()
    {
        Console.WriteLine("\n카드를 드로우 하시겠습니까?");
        Console.WriteLine("\n카드를 드로우하려면 Z키를 눌러주세요");
        Console.WriteLine("점수를 비교하려면 X를 눌러주세요");
    }

    public void ReStart()
    {
        dealer = new Dealer();
        player = new Player();
        deck = new Deck();
        WaitEnd = false;

        Console.WriteLine("\n게임을 초기화하고 다시 시작합니다.");
        RunGame();
        CheckCard();
        CheckScore();
    }

}

class Program
{
    static void Main(string[] args)
    {

        Blackjack blackjack = new Blackjack();
        blackjack.RunGame();
        blackjack.CheckCard();
        blackjack.CheckScore();
        blackjack.DealerWord();


        while (true)
        {
            ConsoleKeyInfo key = Console.ReadKey(true);
            blackjack.DrawCardPlayer(key);
            blackjack.DealerWord();

        }
    }
}

 

위와 같다.

 

블랙잭 게임은 트럼프가 있다면 할 수 있는 게임 중에 가장 쉬운 게임으로,

 

먼저 점수 계산법을 설명하면,

각 카드가 점수를 갖는 점수 카드로 사용하는데 2 ~ 10까지의 숫자는 각 숫자가 점수,

Jack, Queen, King은 각 10점, Ace의 경우 11점으로 사용하지만 21점을 넘기게되는 경우에는 1점으로 처리한다.

 

위의 룰에 따라서 점수를 책정하면서 21점을 넘기지 않는 상태로 21점에 최대한 가깝게 점수를 모으는 게임이다.

카드는 초기에 플레이어마다 각 2장씩 받은 후에

자신의 턴에 카드를 추가로 받을지 받지 않을지 정할 수 있다.

카드를 받기로 했다면 1장을 추가로 받은 후에

다시 자신의 턴에 카드를 받을지 말지 정할 수 있는 행동을 할 수 있다.

받지 않기로 정했다면 다른 플레이어들의 턴이 모두 종료된 후에 카드를 공개해 점수를 비교하여 승패를 낸다.

 

이번에 만들 게임은 플레이어와 딜러의 1대1 게임으로, 딜러는 컴퓨터이다.

따라서 다른 플레이어는 상관 없이 나와 컴퓨터의 대결로,

딜러에게는 추가로 룰이 붙는데,

딜러는 자신의 점수가 17점 이상이 되기 전까지는 카드를 받아야된다.

17점 이상인 경우에는 플레이어의 행동이 끝난 후에 점수를 비교해서 승패를 결정한다.

 

위의 룰을 따라서 게임을 만드려고 계획하면서 아래의 순서로 게임을 만들기로 결정했다.

 

1. 게임 시작 시에 플레이어(나)와 딜러(컴퓨터)가 각각 2장의 카드를 받고

2. 카드를 추가로 받을지 말지 플레이어가 정하고

3. 그에 따라 카드를 받거나 받지 않는 행동을 반복한다.

4. 그 후에 딜러의 룰에 따라 카드를 추가로 받거나 받지않고

5. 점수를 비교해서 승자를 결정한다.

 

기본적으로 주어진

Card, Deck, Hand, Player의 코드를 사용해서 작업을 진행했는데,

처음에는 Player의 클래스를 이용하여 dealer의 클래스를 만드려고 했으나

작업 과정에서 dealer 클래스를 계속 불러오는 것보다

blackjack 클래스에 딜러의 작업을 같이 붙여 넣는 것이 좀 더 부드러워

blackjack 게임 안에 딜러를 넣어서 작업을 진행했다.

 

 

덱에 있는 카드를 드로우할 수 있게 코딩하고

더보기
public void RunGame()
{
    Console.WriteLine("블랙잭 게임을 시작합니다!");

    // 딜러와 플레이어에게 카드를 배분
    dealer.DrawCardFromDeck(deck);
    dealer.DrawCardFromDeck(deck);

    player.DrawCardFromDeck(deck);
    player.DrawCardFromDeck(deck);
}

드로우한 카드를 펼쳐서 보여주고 점수를 비교하여 점수로 승자를 표시할 수 있게 코딩하고

더보기
public void CheckCard()
{
    dealerScore = dealer.Hand.GetTotalValue();
    playerScore = player.Hand.GetTotalValue();

    Console.WriteLine("\n딜러의 손패 :");
    foreach (Card card in dealer.Hand.GetCard())
    {
        Console.WriteLine(card.ToString());
    }
    Console.WriteLine($"딜러의 점수 : {dealerScore}");

    Console.WriteLine("\n플레이어의 손패 :");
    foreach (Card card in player.Hand.GetCard())
    {
        Console.WriteLine(card.ToString());
    }
    Console.WriteLine($"플레이어의 점수 : {playerScore}");

}

public void CheckScore()
{
    if (playerScore == 21 && dealerScore != 21)
    {
        Console.WriteLine("\n축하합니다. 당신의 승리입니다");
        Console.ReadLine();
        Environment.Exit(0);
    }
    else if (playerScore == 21 && dealerScore == 21)
    {
        Console.WriteLine("\n무승부입니다");
        Console.WriteLine("게임을 다시 시작합니다.");
        // 아무키를 눌러도 다시 시작되게
    }
    else if (dealerScore == 21)
    {
        Console.WriteLine("\n딜러의 승리입니다");
        Console.ReadLine();
        Environment.Exit(0);
    }
    else if (playerScore > 21)
    {
        Console.WriteLine("\n당신의 패배입니다");
        Console.ReadLine();
        Environment.Exit(0);
    }
    else if (dealerScore > 21)
    {
        Console.WriteLine("\n축하합니다. 당신의 승리입니다");
        Console.ReadLine();
        Environment.Exit(0);
    }

}

public void EndGame()
{
    System.Threading.Thread.Sleep(300);

    if ((dealerScore > 16) && (dealerScore > playerScore) && (dealerScore < 22))
    {
        Console.WriteLine("\n딜러의 승리입니다");
        Console.ReadLine();
        Environment.Exit(0);
    }
    else if ((dealerScore > 16) && (playerScore > dealerScore))
    {
        Console.WriteLine("\n축하합니다. 당신의 승리입니다");
        Console.ReadLine();
        Environment.Exit(0);
    }
    else if ((dealerScore > 16) && (playerScore == dealerScore))
    {
        Console.WriteLine("\n무승부입니다");
        Console.ReadLine();
        Environment.Exit(0);
    }
    else if ((dealerScore) > 21)
    {
        Console.WriteLine("\n축하합니다. 당신의 승리입니다");
        Console.ReadLine();
        Environment.Exit(0);
    }

}

플레이어의 선택에 따라 카드를 추가로 드로우 하거나,

드로우하지 않고 딜러의 행동을 기다리게 코딩하였다.

더보기
public void DrawCardPlayer(ConsoleKeyInfo key)
{
    if (key.Key == ConsoleKey.Spacebar || key.Key == ConsoleKey.Z)
    {
        Console.WriteLine("카드를 드로우 합니다");
        player.DrawCardFromDeck(deck);
        CheckCard();
        CheckScore();
        return;
    }
    else if (key.Key == ConsoleKey.X)
    {
        WaitEnd = true;

        while (WaitEnd == true)
        {
            Console.WriteLine("\n딜러의 행동을 기다립니다"); 
            System.Threading.Thread.Sleep(300);

            while (dealerScore < 17)
            {
                Console.WriteLine("딜러가 카드를 뽑습니다");
                dealer.DrawCardFromDeck(deck);
                CheckCard();
                System.Threading.Thread.Sleep(500);
            }

            Console.WriteLine("딜러가 카드를 뽑지 않습니다");
            Console.WriteLine("\n점수를 비교합니다");
            EndGame();
            
        }
    }

    else if (key.Key == ConsoleKey.S)
    {
        CheckCard();
    }

    else if (key.Key == ConsoleKey.R)
    {
        ReStart();
    }

    else if (key.Key == ConsoleKey.Escape)
    {
        Console.WriteLine("게임을 종료합니다");
        Console.ReadLine();
        Environment.Exit(0);
    }
    else
    {
        Console.WriteLine("\n올바른 키를 입력해주세요");
        Console.WriteLine("카드를 드로우하려면 Z나 스페이스를");
        Console.WriteLine("딜러와 점수를 비교하려면 X를");
        Console.WriteLine("카드를 확인하려면 S를");
        Console.WriteLine("게임을 재시작하려면 R을");
        Console.WriteLine("게임을 종료하려면 Esc키를 눌러주세요");
    }
}

 

이를 종합해서 Main에 구현을 완료하였다.

더보기
class Program
{
    static void Main(string[] args)
    {

        Blackjack blackjack = new Blackjack();
        blackjack.RunGame();
        blackjack.CheckCard();
        blackjack.CheckScore();
        blackjack.DealerWord();


        while (true)
        {
            ConsoleKeyInfo key = Console.ReadKey(true);
            blackjack.DrawCardPlayer(key);
            blackjack.DealerWord();

        }
    }
}


public void DealerWord()
{
    Console.WriteLine("\n카드를 드로우 하시겠습니까?");
    Console.WriteLine("\n카드를 드로우하려면 Z키를 눌러주세요");
    Console.WriteLine("점수를 비교하려면 X를 눌러주세요");
}

 

이 과정에서 가장 문제가 됬던 파트는 플레이어가 카드를 추가로 받지 않게 선택했을 때

딜러의 상황에 맞게 추가로 행동을 계속해야되는 파트로

처음에는 if 문과 for 문을 이용하여 구현하려고 했으나

if 문의 경우 플레이어의 행동이 입력되야 다음 행동이 진행되기에 원하는 구현이 안됬고,

for 문의 경우 변수를 만드는 것이 잘 되지 않아 구현이 안됬다.

-> bool 값과 while 문을 사용하여 딜러의 점수가 17점 미만인 경우 bool 값을 true로 하여

17점을 넘는 경우 false가 되게 설정하여 while 문을 반복하게 하는 방식으로 구현했다.

 

 

위의 방식으로 과제를 진행하였는데,

과제에서 구현하려는 것의 개념을 정리한 후에

구현할 것들의 순서를 정하고, 순서대로 구현함으로써 작업을 진행하였다.

 

다시금 문제를 해결하는 순서를 잘 정리하여 과제를 수행하였는데,

이 과정을 몸에 체득하는 것을 우선적으로 하여 과제를 수행할 것이다.