Notice
Recent Posts
Recent Comments
Link
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

I'm FanJae.

[C#] 콘솔 월남뽕 게임 만들기 본문

Unity/Unity 초격차캠프

[C#] 콘솔 월남뽕 게임 만들기

FanJae 2026. 5. 1. 23:51

1. 시작에 앞서

아예 처음 접해본 게임이었다.

게임 규칙을 요약해 보면, 2개의 카드를 오픈. 내 카드가 그 2개의 카드 사이에 해당 하면 승리하는 게임이다.

 

2. 구현 방법

1. GameManager 클래스를 만들고, 안에는 다음과 같은 필드를 넣는다.
    - 52개의 카드 정보
    - 사용한 카드의 개수
    - 유저의 시드머니 (설계에 따라 다소 애매할 수 있지만 여기에 넣었다.)
    - 마크 표 - 숫자 매칭표
2. 초기 카드 값을 설정하는 것과 카드 셔플을 하는 메서드를 구현한다.
    - 카드 셔플은 Fisher-Yates Shuffle 알고리즘을 사용한다.)
    - 카드 값을 설정시에는 0~51까지 값을 담는다.
3. 포커의 마크 값을 변환하는 메서드와 숫자 값을 변환하는 메서드를 구현한다.
    - 포커의 마크 값은 0~51의 값에 대해서 13을 나눈 값으로 변환할 수 있다.
    - 숫자 값은 13으로 나눈 나머지의 1을 더하여 변환 가능하다.
    - 값 표기를 할때 0이 없는 것을 고려하기 위해서 1을 더한다.
4. 카드 값을 프린트하는 메서드를 구현한다.
    - 마크 표와 숫자 매칭표를 이용하여, 조건에 따라 처리가 가능하다.
5. 정수 값이 올바른지 파싱하는 메서드를 구현한다.
    - 문자열에 값을 받아 파싱이 가능한지 확인한다.
    - 소지금액보다 높은 금액을 적었는지 확인한다.
    - 최소 베팅 금액 이하를 적었는지 검사한다.
6. 게임 로직에 따라서 체크를 진행한다.
    - 승리 조건에서 C가 A ~ B 사이인지 아래와 같이 확인하면 알 수 있다.
        - A - C > 0 : A가 C보다 크다. (양수)
        - A - C == 0 : A랑 C가 같다. (0)
        - A - C < 0 : A가 C보다 작다. (음수)
    - A,B가 C에 각각 (양수,음수) 또는 (음수,양수) 형태로 만족하게 만든다.
    - 곱셈을 이용하여, 조건을 간단하게 만들 수 있다.

 

3. 구현

※ C# Source Code

더보기

1. GameManager 클래스 - 전체

class GameManager
{
    private int[] card = new int[52];
    private int used_card_count = 0;
    private int money = 0;
    enum Mark { Spade, Clover, Diamond, Heart }; // 마크표
    enum Value { A = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, J, Q, K }; // 숫자 매칭표

    public void GameReady() // 게임 전 카드 셋팅민 셔플 처리
    {
        CardSetting();
        CardShuffle();
    }
    public void CardSetting() // 카드 값 설정
    {
        for(int i=0; i<52; i++)
        {

            card[i] = i;
        }
    }
    public void CardShuffle() // 카드 셔플 (Fisher-Yates Shuffle 알고리즘)
    {
        Random rand = new Random();
        for(int i=51; i>=0; i--)
        {
            int j = rand.Next(0, i + 1); // 아직 미확정 구간인 0~i 중 무작위 하나

            int temp = card[i];
            card[i] = card[j];
            card[j] = temp;
        }            
    }

    int ParsePokerMark(int card_value) // 포커의 마크 값 처리
    {
        return card_value / 13;
    }
    int ParsePokerValue(int card_value) // 포커의 숫자 값 처리
    {
        return card_value % 13 + 1;
    }
    public void CardPrint(int card_value) // 카드 프린트
    {
        Mark mark = (Mark)ParsePokerMark(card_value);
        Value value = (Value) ParsePokerValue(card_value);

        switch (mark) // mark에서 얻어온 숫자에 따라 처리
        {
            case Mark.Spade:
                Console.Write("♠");
                break;
            case Mark.Clover:
                Console.Write("♣");
                break;
            case Mark.Diamond:
                Console.Write("◆");
                break;
            case Mark.Heart:
                Console.Write("♥");
                break;
            default:
                break;
        }

        switch (value) // A,J,Q,K에 대해서만 알파벳 나머지는 숫자로 출력
        {
            case Value.A:
                Console.Write("A\\t");
                break;
            case Value.J:
                Console.Write("J\\t");
                break;
            case Value.Q:
                Console.Write("Q\\t");
                break;
            case Value.K:
                Console.Write("K\\t");
                break;
            default:
                Console.Write($"{(int)value}\\t");
                break;
        }
    }

    public int MyTryParse(string input) // 정수값이 올바른지 파싱 처리
    {
        int myMoney;
        if(int.TryParse(input,out myMoney) == false || myMoney > money || myMoney < 1000)
        {
            myMoney = -1;
        }
        return myMoney;
    }
    public void GamePlay()
    {
        while (true)
        {
            string input;
            int betMoney;

            if (used_card_count + 3 > 52) // 카드 모두 소진
            {
                Console.WriteLine("다음 라운드에 사용할 카드가 부족하므로, 게임을 종료합니다.");
                break;
            }
            if (money < 1000) // 소지금이 1000원 미만인 경우
            {
                Console.WriteLine("소지금이 1000 미만이므로, 게임을 종료합니다.");
                break;
            }

            for (int i = used_card_count; i < used_card_count + 3; i++) // 사용한 현재 카드의 수를 인덱스로 하여 3장을 출력한다.
            {
                CardPrint(card[i]);
            }
            Console.WriteLine();

            Console.WriteLine($"내가 가진 시드머니  : {money}");

            //bool test = GameLogicCheck(used_card_count); 카드 다쓸 때 확인하기 위해 넣어놓은 코드
            //Console.WriteLine($"Cheat : {test}");
            while (true)
            {
                Console.Write("베팅액(최소 베팅 금액 1000원)을 입력하시오! ");
                input = Console.ReadLine();

                betMoney = MyTryParse(input);
                if (betMoney == -1)
                {
                    Console.WriteLine("올바른 베팅액을 입력해 주시길 바랍니다.");
                }
                else
                {
                    break;
                }
            }

            if (GameLogicCheck(used_card_count) == true) // 승리한 경우
            {
                money += betMoney;
                Console.WriteLine($"{betMoney} 원을 획득했다");

            }
            else
            {
                money -= betMoney;
                Console.WriteLine($"{betMoney} 원을 잃었다~");
            }

            used_card_count += 3;
            Console.WriteLine($"현재 사용한 카드 수 : {used_card_count}");
            Console.WriteLine();
        }
    }

    public bool GameLogicCheck(int used_card_count) // 카드 로직 체크
		{
		    
		    int A = ParsePokerValue(card[used_card_count]); // 첫번째 카드 값 변환
		    int B = ParsePokerValue(card[used_card_count + 1]); // 두번째 카드 값 변환
		    int C = ParsePokerValue(card[used_card_count + 2]); // 세번째 카드 값 변환
		
		    bool Win = (A - C) * (B - C) < 0; // C가 A~B 사이 값인지 확인
		
		    return Win;
		}
    public void SetMoney(int myMoney)
    {
        money = myMoney;
    }
}

- GameManager 하나가 이 게임의 핵심 로직이다.

- 다소 복잡한 관계로 각각의 주요 필드와 로직을 나누어 설명한다.

 

2. GameManager - 주요 필드

private int[] card = new int[52];
private int used_card_count = 0;
private int money = 0;
enum Mark { Spade, Clover, Diamond, Heart }; // 마크표
enum Value { A = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, J, Q, K }; // 숫자 매칭표

52장의 카드 정보, 사용한 카드 개수, 유저의 돈, 마크표, 실제 값 등의 정보를 담아 준다.

- 열거형에서는 특정 값을 1만 입력하면 다음 값은 자동으로 +1씩 증가하여, 이를 명시하지 않았다.

 

3. GameReady() - 게임 전 카드 셋팅 및 셔플 처리하는 함수

public void GameReady() // 게임 전 카드 셋팅 및 셔플 처리
{
    CardSetting();
    CardShuffle();
}

 - 게임 시작 전 카드를 셋팅하고 카드를 셔플해주는 메서드를 호출한다.

 

4. CardSetting() - 카드의 초기값을 설정하는 함수

public void CardSetting() // 카드 값 설정
{
    for(int i=0; i<52; i++)
    {

        card[i] = i;
    }
}

 - 카드의 초기 값을 설정하는 것으로 각각을 0~51의 값으로 설정하여 넣어준다.

 

5. CardShuffle() - 카드 셔플을 진행하는 함수

public void CardShuffle() // 카드 셔플 (Fisher-Yates Shuffle 알고리즘)
{
    Random rand = new Random();
    for(int i=51; i>=0; i--)
    {
        int j = rand.Next(0, i + 1); // 아직 미확정 구간인 0~i 중 무작위 하나

        int temp = card[i];
        card[i] = card[j];
        card[j] = temp;
    }            
}

- 셔플은 Fisher-Yates Shuffle 알고리즘을 사용하였다.

- 확정되지 않은 구간을 랜덤하게 뽑아서 맨 뒤부터 차곡차곡 넣어주는 셔플 기법이다.

 

6. ParsePokerMark() - 포커의 마크값을 추출해주는 함수

int ParsePokerMark(int card_value) // 카드의 마크 값 처리
{
    return card_value / 13;
}

- 나누기 연산을 이용하여 카드의 마크 값을 가져오는 함수다.

 

7. ParsePokerValue() - 카드의 숫자 값을 추출하는 함수다.

int ParsePokerValue(int card_value) // 카드의 숫자 값 처리
{
    return card_value % 13 + 1;
}

 - 나머지 연산을 이용하여 카드의 숫자 값만 추출하는 함수이다.

 

 8. CardPrint() - 카드의 마크와 카드 값을 출력하는 함수

public void CardPrint(int card_value) // 카드 프린트
{
    Mark mark = (Mark)ParsePokerMark(card_value);
    Value value = (Value) ParsePokerValue(card_value);

    switch (mark) // mark에서 얻어온 숫자에 따라 처리
    {
        case Mark.Spade:
            Console.Write("♠");
            break;
        case Mark.Clover:
            Console.Write("♣");
            break;
        case Mark.Diamond:
            Console.Write("◆");
            break;
        case Mark.Heart:
            Console.Write("♥");
            break;
        default:
            break;
    }

    switch (value) // A,J,Q,K에 대해서만 알파벳 나머지는 숫자로 출력
    {
        case Value.A:
            Console.Write("A\\t");
            break;
        case Value.J:
            Console.Write("J\\t");
            break;
        case Value.Q:
            Console.Write("Q\\t");
            break;
        case Value.K:
            Console.Write("K\\t");
            break;
        default:
            Console.Write($"{(int)value}\\t");
            break;
    }
}

 - 카드의 마크와 숫자 값을 출력해준다.

 

9. MyTryParse() - 정수값이 올바른지 파싱 처리를 진행하는 함수

public int MyTryParse(string input) // 정수값이 올바른지 파싱 처리
{
    int myMoney;
    if(int.TryParse(input,out myMoney) == false || myMoney > money || myMoney < 1000)
    {
        myMoney = -1;
    }
    return myMoney;
}

 - 입력받은 문자가 사용자가 입력할 수 있는 정수값인지 검증해준다.

 

10. GameLogicCheck() - 카드 게임의 승리 조건을 확인하는 함수

public bool GameLogicCheck(int used_card_count) // 카드 로직 체크
{
    
    int A = ParsePokerValue(card[used_card_count]); // 첫번째 카드 값 변환
    int B = ParsePokerValue(card[used_card_count + 1]); // 두번째 카드 값 변환
    int C = ParsePokerValue(card[used_card_count + 2]); // 세번째 카드 값 변환

    bool Win = (A - C) * (B - C) < 0; // C가 A~B 사이 값인지 확인

    return Win;
}

- 처음 단순 비교로 처리했다. 하지만, 사이 값을 확인하기 위한 로직을 개선하였다.

- 위 연산이 음수인지 확인하여 반환하면 사이 값 판별이 가능하다.

 

11.  GamePlay() - 게임 플레이 함수

public void GamePlay()
{
    while (true)
    {
        string input;
        int betMoney;

        if (used_card_count + 3 > 52) // 카드 모두 소진
        {
            Console.WriteLine("다음 라운드에 사용할 카드가 부족하므로, 게임을 종료합니다.");
            break;
        }
        if (money < 1000) // 소지금이 1000원 미만인 경우
        {
            Console.WriteLine("소지금이 1000 미만이므로, 게임을 종료합니다.");
            break;
        }

        for (int i = used_card_count; i < used_card_count + 3; i++) // 사용한 현재 카드의 수를 인덱스로 하여 3장을 출력한다.
        {
            CardPrint(card[i]);
        }
        Console.WriteLine();

        Console.WriteLine($"내가 가진 시드머니  : {money}");

        //bool test = GameLogicCheck(used_card_count); 카드 다쓸 때 확인하기 위해 넣어놓은 코드
        //Console.WriteLine($"Cheat : {test}");
        while (true)
        {
            Console.Write("베팅액(최소 베팅 금액 1000원)을 입력하시오! ");
            input = Console.ReadLine();

            betMoney = MyTryParse(input);
            if (betMoney == -1)
            {
                Console.WriteLine("올바른 베팅액을 입력해 주시길 바랍니다.");
            }
            else
            {
                break;
            }
        }

        if (GameLogicCheck(used_card_count) == true) // 승리한 경우
        {
            money += betMoney;
            Console.WriteLine($"{betMoney} 원을 획득했다");

        }
        else
        {
            money -= betMoney;
            Console.WriteLine($"{betMoney} 원을 잃었다~");
        }

        used_card_count += 3;
        Console.WriteLine($"현재 사용한 카드 수 : {used_card_count}");
        Console.WriteLine();
    }
}

- 현재 사용한 카드 수로 부터 다음 카드 수를 구하여, 52개를 넘었으면 카드를 모두 소진했기 때문에 게임을 종료한다.

- 소지금이 1000원 미만인 경우에도 파산으로 게임을 종료한다.

- 이후, 현재 사용한 카드 수를 인덱스로 활용하여 카드를 출력한다.

- 베팅액을 입력 받고 올바른지 검증을 진행한 이후, GameLogicCheck() 함수에서 승리 조건이 맞는지 확인한다.

- 승리했으면 betMoney 만큼 소지 금액을 증가. 패배했으면 betMoney만큼 소지 금액을 감소 시킨다.

- 이후, 현재 사용된 카드의 수를 3개 증가 시킨 뒤 출력해준다.

 

 12. 전체 코드

namespace Day11
{
    internal class VietnamHwatu
    {
        class GameManager
        {
            private int[] card = new int[52];
            private int used_card_count = 0;
            private int money = 0;
            enum Mark { Spade, Clover, Diamond, Heart }; // 마크표
            enum Value { A = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, J, Q, K }; // 숫자 매칭표

            public void GameReady() // 게임 전 카드 셋팅 및 셔플 처리
            {
                CardSetting();
                CardShuffle();
            }
            public void CardSetting() // 카드 값 설정
            {
                for(int i=0; i<52; i++)
                {

                    card[i] = i;
                }
            }
            public void CardShuffle() // 카드 셔플 (Fisher-Yates Shuffle 알고리즘)
            {
                Random rand = new Random();
                for(int i=51; i>=0; i--)
                {
                    int j = rand.Next(0, i + 1); // 아직 미확정 구간인 0~i 중 무작위 하나

                    int temp = card[i];
                    card[i] = card[j];
                    card[j] = temp;
                }            
            }

            int ParsePokerMark(int card_value) // 카드의 마크 값 처리
            {
                return card_value / 13;
            }
            int ParsePokerValue(int card_value) // 카드의 숫자 값 처리
            {
                return card_value % 13 + 1;
            }
            public void CardPrint(int card_value) // 카드 프린트
            {
                Mark mark = (Mark)ParsePokerMark(card_value);
                Value value = (Value) ParsePokerValue(card_value);

                switch (mark) // mark에서 얻어온 숫자에 따라 처리
                {
                    case Mark.Spade:
                        Console.Write("♠");
                        break;
                    case Mark.Clover:
                        Console.Write("♣");
                        break;
                    case Mark.Diamond:
                        Console.Write("◆");
                        break;
                    case Mark.Heart:
                        Console.Write("♥");
                        break;
                    default:
                        break;
                }

                switch (value) // A,J,Q,K에 대해서만 알파벳 나머지는 숫자로 출력
                {
                    case Value.A:
                        Console.Write("A\t");
                        break;
                    case Value.J:
                        Console.Write("J\t");
                        break;
                    case Value.Q:
                        Console.Write("Q\t");
                        break;
                    case Value.K:
                        Console.Write("K\t");
                        break;
                    default:
                        Console.Write($"{(int)value}\t");
                        break;
                }
            }

            public int MyTryParse(string input) // 정수값이 올바른지 파싱 처리
            {
                int myMoney;
                if(int.TryParse(input,out myMoney) == false || myMoney > money || myMoney < 1000)
                {
                    myMoney = -1;
                }
                return myMoney;
            }
            public void GamePlay()
            {
                while (true)
                {
                    string input;
                    int betMoney;

                    if (used_card_count + 3 > 52) // 카드 모두 소진
                    {
                        Console.WriteLine("다음 라운드에 사용할 카드가 부족하므로, 게임을 종료합니다.");
                        break;
                    }
                    if (money < 1000) // 소지금이 1000원 미만인 경우
                    {
                        Console.WriteLine("소지금이 1000 미만이므로, 게임을 종료합니다.");
                        break;
                    }

                    for (int i = used_card_count; i < used_card_count + 3; i++) // 사용한 현재 카드의 수를 인덱스로 하여 3장을 출력한다.
                    {
                        CardPrint(card[i]);
                    }
                    Console.WriteLine();

                    Console.WriteLine($"내가 가진 시드머니  : {money}");

                    //bool test = GameLogicCheck(used_card_count); // 카드 다쓸 때 확인하기 위해 넣어놓은 코드
                    //Console.WriteLine($"Cheat : {test}");
                    while (true)
                    {
                        Console.Write("베팅액(최소 베팅 금액 1000원)을 입력하시오! ");
                        input = Console.ReadLine();

                        betMoney = MyTryParse(input);
                        if (betMoney == -1)
                        {
                            Console.WriteLine("올바른 베팅액을 입력해 주시길 바랍니다.");
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (GameLogicCheck(used_card_count) == true) // 승리한 경우
                    {
                        money += betMoney;
                        Console.WriteLine($"{betMoney} 원을 획득했다");

                    }
                    else
                    {
                        money -= betMoney;
                        Console.WriteLine($"{betMoney} 원을 잃었다~");
                    }

                    used_card_count += 3;
                    Console.WriteLine($"현재 사용한 카드 수 : {used_card_count}");
                    Console.WriteLine();
                }
            }

            public bool GameLogicCheck(int used_card_count) // 카드 로직 체크
            {
                
                int A = ParsePokerValue(card[used_card_count]); // 첫번째 카드 값 변환
                int B = ParsePokerValue(card[used_card_count + 1]); // 두번째 카드 값 변환
                int C = ParsePokerValue(card[used_card_count + 2]); // 세번째 카드 값 변환

                bool Win = (A - C) * (B - C) < 0; // C가 A~B 사이 값인지 확인

                return Win;
            }
            public void SetMoney(int myMoney)
            {
                money = myMoney;
            }
        }
        static void Main(string[] args)
        {
            
            GameManager gm = new GameManager();
            gm.GameReady();
            gm.SetMoney(10000);
            gm.GamePlay();
        }
    }
}

4. 스크린샷

1) 플레이 스크린샷

 

5. 회고

슬슬 Git에 하나씩 올리면서 정리를 좀 해야겠다.
그리고 다음엔.. 클래스 설계좀 제대로 해야 할 것 같다.
Comments