I'm FanJae.
[C#] 콘솔 월남뽕 게임 만들기 본문
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에 하나씩 올리면서 정리를 좀 해야겠다.
그리고 다음엔.. 클래스 설계좀 제대로 해야 할 것 같다.
'Unity > Unity 초격차캠프' 카테고리의 다른 글
| [20260504] C# (프로퍼티, 생성자, 메서드 II) (0) | 2026.05.04 |
|---|---|
| [C# 리마인드] 콘솔 스네이크 게임 만들기 (0) | 2026.05.03 |
| [20260430] C# 얕은 복사(Shallow Copy) vs 깊은 복사(Deep Copy) (0) | 2026.04.30 |
| [20260430] 클래스(Class) (0) | 2026.04.30 |
| [20260429] 구조체(struct)와 클래스(class)의 차이점 (C#) (0) | 2026.04.29 |