2025. 10. 13. 20:46ㆍ개발

namespace BlackJackGame;
class Card
{
public string Pattern; //카드 무늬 ♠, ♥, ♦, ♣
public string Rank; //숫자, 등급 2~10, J, Q, K, A
public Card(string pattern, string rank)
{
Pattern = pattern;
Rank = rank; //필드 초기화. 카드객체를 유효하게 만들어줌
}
public int GetValue() //카드 점수 반환 (A는 11, J/Q/K는 10, 나머지는 숫자)
{
if (int.TryParse(Rank, out int value))
return value;
else if (Rank == "A")
return 11;
else
return 10;
}
}
class Deck
{
private List<Card> cards = new List<Card>();
private Random random = new Random();
public Deck()
{
string[] Pattern = {"♠", "♥", "♦", "♣" };
string[] Rank = {"J", "Q", "K", "A","2", "3", "4", "5", "6", "7", "8", "9","10"};
foreach (string pattern in Pattern)
{
foreach (string rank in Rank)
{
cards.Add(new Card(pattern, rank));
}
}
}
public void Shuffle() //게임 시작시 카드 더미(덱)을 섞음.
{
for (int i=0; i<cards.Count; i++)
{
int j = random.Next(cards.Count);
(cards[j], cards[i]) = (cards[j], cards[i]); //구조분해로 스왑
}
}
public Card Draw()
{
Card drawnCard = cards[0];
cards.RemoveAt(0);
return drawnCard;
}
}
class Hand //플레이어나 딜러의 손에 어떤 카드가 들어가느냐
{
public List<Card> Cards = new List<Card>();
public void AddCard(Card card)
{
Cards.Add(card);
}
public int GetScore()
{
int score = 0;
int aceCount = 0;
foreach (Card card in Cards)
{
if (card.Rank == "J"|| card.Rank == "Q"|| card.Rank == "K")
{
score += 10;
}
else if (card.Rank == "A")
{
score += 11;
aceCount++;
}
else
{
score += int.Parse(card.Rank);
}
}
while (score > 21 && aceCount > 0)
{
score -= 10;
aceCount--;
}
return score;
}
//현재 손패를 출력하기
public void ShowHand(string name)
{
Console.WriteLine($"{name}의 현재 손패:");
foreach (Card card in Cards)
{
Console.Write(card.Pattern + card.Rank);
}
Console.WriteLine($"->점수:{GetScore()}");
}
}
class GameManager //게임 한판을 순서대로 호출하면 됨 + 게임진행제어
{
private Deck deck;
private Hand player;
private Hand dealer;
public void StartGame()
{
Console.WriteLine("----블랙잭 게임 시작----");
Console.WriteLine("플레이어는 'hit'를 입력하면 카드를 한 장 더 받고, 'stand'를 입력하면 턴을 종료합니다.");
Console.WriteLine("게임이 시작됩니다 \n");
//덱(카드 더미)초기화,
deck = new Deck();
deck.Shuffle();
//카드 나누고,
player = new Hand();
dealer = new Hand();
//초기 카드 분배(각각 2장)
player.AddCard(deck.Draw());
dealer.AddCard(deck.Draw());
player.AddCard(deck.Draw());
dealer.AddCard(deck.Draw());
//게임 루프 실행!
PlayerTurn();
DealerTurn();
ShowResult();
}
private void PlayerTurn()
{
while (true)
{
Console.WriteLine("hit or stand?");
string choice = Console.ReadLine();
if (choice == "hit")
{
player.AddCard(deck.Draw());
player.ShowHand("플레이어");
if (player.GetScore() > 21)
{
Console.WriteLine("Burst! 점수가 초과되었습니다.");
break;
}
}
else if (choice == "stand")
{
break;
}
}
}
private void DealerTurn()
{
//딜러 점수가 17보다 작으면 자동으로 카드 받기
while (dealer.GetScore() <17)
{
dealer.AddCard(deck.Draw());
dealer.ShowHand("딜러");
}
}
private void ShowResult()
{
//플레이어 점수 계산
int playerScore = player.GetScore();
//딜러 점수 계산
int dealerScore = dealer.GetScore();
//burst 체크 (21초과 -> 패배)
if (playerScore > 21)
{
Console.WriteLine("Player is Burst!");
}
else if (dealerScore > 21)
{
Console.WriteLine("Dealer is Burst!");
}
else
{
if (playerScore > dealerScore)
Console.WriteLine("Player Wins!");
else if (playerScore < dealerScore)
Console.WriteLine("Dealer Wins!");
else
Console.WriteLine("Draw!");
}
}
}
class Program
{
static void Main(string[] args)
{
GameManager game = new GameManager();
game.StartGame();
}
}
전체 코드



스네이크 게임보다는 구현이 조금 더 쉬웠다.
규칙이 명확해서 그런 듯! 응용해야 할 것도 그리 많지 않고... 스네이크 게임보다는 확실히 술술 풀리고 머리도 덜 아팠음
블랙잭 기본 규칙
- 목표:
카드의 합이 21에 가장 가깝게 만드는 사람이 승리. - 카드 규칙:
- 숫자 2~10은 그대로 점수
- J, Q, K는 10점
- A(에이스)는 1 또는 11점 중 유리한 쪽으로 계산
- 진행 순서:
- 플레이어와 딜러에게 각각 2장씩 나눠줌
- 플레이어는 Hit(한 장 더) 또는 Stand(그만) 선택
- 플레이어가 21을 넘으면 → 버스트(패배)
- 딜러는 17점 이상이 될 때까지 카드를 자동으로 받음
- 둘 다 끝나면 점수 비교 → 21에 더 가까운 쪽 승리
게임 흐름 : 덱 생성 → 카드 섞기 → 플레이어/딜러 카드 나누기 → 플레이어 턴 → 딜러 턴 → 승패 판정
클래스 구조와 역할:
| 클래스 | 역할 |
| Card | 한 장의 카드 정보 (무늬, 숫자, 점수 계산) |
| Deck | 52장 카드 묶음, 섞기·뽑기 담당 |
| Hand | 플레이어나 딜러의 카드 보유 & 점수 계산 |
| GameManager | 게임 한 판을 순서대로 호출함 / 게임 진행 제어 |
전체 구조 틀
using System;
using System.Collections.Generic;
using System.Linq;
namespace BlackjackGame
{
// 카드 한 장
class Card
{
public string Suit; // 무늬: ♠, ♥, ♦, ♣
public string Rank; // 숫자: 2~10, J, Q, K, A
// 생성자
public Card(string suit, string rank)
{
// TODO: 필드 초기화
}
// 카드의 점수 반환 (A는 11, J/Q/K는 10, 나머지는 숫자)
public int GetValue()
{
// TODO: 점수 계산 로직 작성
return 0;
}
}
// 카드 더미 (덱)
class Deck
{
private List<Card> cards = new List<Card>();
private Random rand = new Random();
public Deck()
{
// TODO: 52장 카드 생성 (4무늬 × 13숫자)
}
// 카드 섞기
public void Shuffle()
{
// TODO: 리스트 섞기 (Random 이용)
}
// 카드 한 장 뽑기
public Card Draw()
{
// TODO: 한 장 반환 후 리스트에서 제거
return null;
}
}
// 손패 (플레이어나 딜러)
class Hand
{
public List<Card> Cards = new List<Card>();
// 카드 추가
public void AddCard(Card card)
{
// TODO: 손패에 카드 추가
}
// 점수 계산
public int GetScore()
{
// TODO: 카드 점수 합 + A 처리 로직
return 0;
}
// 현재 손패 출력
public void ShowHand(string name)
{
// TODO: 콘솔에 카드와 점수 출력
}
}
// 메인 게임 흐름
class GameManager
{
private Deck deck;
private Hand player;
private Hand dealer;
public void StartGame()
{
// TODO: 덱/손패 초기화, 카드 나누기, 게임 루프 실행
}
private void PlayerTurn()
{
// TODO: Hit / Stand 선택, 점수 체크
}
private void DealerTurn()
{
// TODO: 딜러 점수 < 17 → 자동으로 카드 받기
}
private void ShowResult()
{
// TODO: 최종 점수 비교 후 승패 출력
}
}
//실행 부분
class Program
{
static void Main()
{
GameManager game = new GameManager();
game.StartGame();
}
}
}
트러블 슈팅
1. 블랙잭 게임 필드 초기화 어떻게 해
public Card(string suit, string rank)
{
Suit suit = new Suit();
Rank rank = new Rank();
}
문제점:
-새로운 지역 변수 Suit을 만듬 x
- 필드가 아니라 지역변수 초기화를 함 x
생성자 매개변수 이름과 필드 이름 대소문자 구분이 비슷해서 헷갈렸다.
C#에서는 이렇게 하면 필드에 값이 들어가는게 아니라 새로운 지역변수가 생겨버린다.
이 코드에서 문제는 왼쪽 Suit,Rank가 클래스의 필드가 아니라는 점이다!
왜냐면 이 구문은 새로운 지역 변수를 선언하는 문법이기 때문이다.
Suit suit = new Suit();
C#에서 **“타입이 Suit인 지역 변수 suit를 새로 만든다”**라는 뜻.
그렇다면 왜 필드를 초기화해줘야할까?
필드 초기화는 "객체를 진짜로 유효하게 만드는 단계"이다.
위에 필드에서 내가 카드 객체를 만들었다면,
public string Pattern; //카드 무늬 ♠, ♥, ♦, ♣
public string Rank; //숫자, 등급 2~10, J, Q, K, A
밑은 필드 초기화를 통해 카드 객체를 유효하게 만듬!
Pattern = pattern;
Rank = rank; //필드 초기화. 카드객체를 유효하게 만들어줌
트러블 슈팅 2
연산자 기호 헷갈려..
또는|| 과 그리고&&가 제일 헷갈려 ..
언제 어느 상황에서 구분해서 써야 하는지도 헷갈린다..
문제점)
else if (Rank == "J" && "Q" && "K") return 10;
=>x 이건 문법적으로 틀렸다.. Q와 K는 그냥 문자열이라서 && 뒤에 쓰면 논리 연산이 안됨.
컴파일 오류남.
시도한 점)
😀(ME): 그럼 보간으로 바꿔도 틀려?
$"{Rank}" == "J" && "Q" && "K"
👾(GPT): 어 틀려~
- $"{Rank}"는 Rank 값 문자열 그대로 가져오는 거고
- 여전히 "Q"와 "K"는 그냥 문자열 → 논리 연산 불가
- 보간(interpolation)은 문자열 합치거나 출력할 때 편리한 것이지, 여러 값 비교를 한 번에 처리해주진 않음 !!
내가 여기서 원하는 것은 J 또는 Q또는 K카드일 때 10을 반환해줘
이기 때문에, 또는 ||을 써야함. &&는 그리고임!
else if (Rank == "J" || Rank == "Q" || Rank == "K")
return 10;
😀: 아 ㅇㅋㅇㅋ
알게 된 점:
보간은 문자열을 합치거나 출력할 때 편리한 것이지, 여러 값의 비교를 한 번에 처리해주진 않는다.
또는 ||과 그리고 &&를 구분하자... 또는|| 그리고 && 또는|| 그리고 && 또는|| 그리고 &&
트러블 슈팅 3)
Q. private 필드여도 생성자에서 접근이 가능해...?
class Deck //카드 더미 (덱)
{
private List<Card> cards = new List<Card>();
private Random random = new Random();
public Deck()
{
// 52장의 카드 생성 (4무늬 x 13숫자)
}
여기서 public Deck에 랜덤한 카드 52장이 섞여야 하는데 필드가 private이어도 상관 없어?
A. 엉 상관 없음. private필드여도 생성자에서 접근 가능함!
private 필드여도 생성자에서 접근 가능
- private List<Card> cards → Deck 클래스 내부에서만 접근 가능
- public Deck() 생성자는 Deck 클래스 내부이므로 자유롭게 사용 가능
- 따라서 cards.Add(...) 같은 코드는 문제 없이 가능
즉, private라고 해서 같은 클래스 내부에서 못 쓰는 게 아님.
알게 된 점: 필드가 private이어도 같은 클래스 내부에서 사용할 수 있다...다만 외부 클래스에서는 접근 못함!
트러블슈팅4)
cards.Add(new Card(pattern, rank)); 이렇게 쓰면 4무늬 곱하기 13숫자가 나와?
ChatGPT의 말:
맞아 😄 네가 쓴 그대로라면 4무늬 × 13랭크 = 52장 카드가 나와.
예를 들어 이렇게 하면:
string[] Pattern = { "♠", "♥", "♦", "♣" };
string[] Rank = { "J", "Q", "K", "A","2","3","4","5","6","7","8","9","10" };
foreach (string pattern in Pattern)
{
foreach (string rank in Rank)
{
cards.Add(new Card(pattern, rank));
}
}
- Pattern 루프 4번
- Rank 루프 13번
- 총 반복 횟수 = 4 × 13 = 52
- 따라서 cards 리스트에 52장의 카드가 들어감
즉, cards.Add(new Card(pattern, rank)); 한 줄로 4무늬 × 13숫자 카드 완성 가능 😎
원하면 내가 카드 섞기(Shuffle)까지 이어서 그림으로 보여줄 수도 있어.
나의 말:
cards.Add(new Card(pattern, rank)); 이게 무슨 뜻이야 코드 설명 좀
ChatGPT의 말:
좋아 😄 차근차근 설명할게.
코드
cards.Add(new Card(pattern, rank));
1️⃣ new Card(pattern, rank)
- Card 클래스의 새 객체를 만든다는 뜻
- 생성자에 pattern과 rank를 전달
- 예를 들어:
pattern = "♠"
rank = "A"
- 그러면 스페이드 A 카드 객체가 만들어짐
2️⃣ cards.Add(...)
- cards는 List<Card>
- Add() 메서드는 리스트에 새 요소를 추가
- 즉, 새로 만든 Card 객체를 cards 리스트에 넣는 것
3️⃣ 전체 의미
cards.Add(new Card(pattern, rank));
- “pattern과 rank로 카드 객체를 만들고, 그 카드를 덱(cards) 리스트에 추가해라”
- foreach 루프 안에서 이걸 반복하면 모든 무늬 × 모든 숫자 카드가 리스트에 쌓임
- 결과적으로 52장의 카드 완성
💡 그림으로 생각하면
Pattern: ♠, ♥, ♦, ♣
Rank: 2,3,...,K,A
foreach pattern
foreach rank
cards.Add(new Card(pattern, rank))
→ cards 리스트: [♠2, ♠3, ..., ♣K, ♣A] 총 52장
원하면 내가 이 52장 카드 섞고 Draw해서 플레이어/딜러에게 나눠주는 흐름까지 이어서 보여줄 수도 있어.
나의 말:
1️⃣ new Card(pattern, rank)
Card 클래스의 새 객체를 만든다는 뜻
생성자에 pattern과 rank를 전달
예를 들어:
pattern = "♠" rank = "A"
그러면 스페이드 A 카드 객체가 만들어짐 이거 그럼 나중에 입력 받아야해?
ChatGPT의 말:
아니 😄 지금 구조에서는 나중에 입력 받을 필요 없어.
이유
- new Card(pattern, rank) → 이미 코드 안에서 패턴과 랭크 배열을 반복문으로 넣어줌
- 예시:
string[] Pattern = { "♠", "♥", "♦", "♣" };
string[] Rank = { "2", "3", ..., "K", "A" };
foreach (string pattern in Pattern)
{
foreach (string rank in Rank)
{
cards.Add(new Card(pattern, rank));
}
}
- 여기서 모든 패턴 × 모든 랭크가 자동으로 Card 객체로 생성됨
- 즉, 스페이드 A도, 하트 2도, 다 리스트에 들어감
🔹 결론
- 사용자 입력 필요 없음
- Deck 생성자에서 자동으로 52장 카드 만들어서 cards에 저장됨
- 나중에 플레이어가 카드를 뽑을 때는 **Deck에서 Draw()**만 호출하면 됨
원하면 내가 Deck에서 Draw()로 카드 뽑고 플레이어/딜러에 나눠주는 흐름까지 그림으로 보여줄 수도 있어.
나의 말:
아 ㅇㅋ 나 이해함
ChatGPT의 말:
좋아 😄 완전히 이해했구나!
- new Card(pattern, rank) → 자동으로 카드 객체 생성
- cards.Add(...) → 리스트에 쌓아서 52장 덱 완성
- 사용자 입력 따로 받을 필요 없음 → 나중에 Draw()로 카드만 뽑으면 됨
잡담: 연휴 끝나고 오랜만에 공부하니까 신체리듬이 와장창 깨져버림....분명 놀기만 한 건 아니었던 것 같은데...ㅎㅎㅎ
그래서 오늘은 그냥 신체리듬 되돌리는 날이라고 맘 먹고 좀 내려놓고 공부했다....
'개발' 카테고리의 다른 글
| 두 번째 팀프로젝트 [TextRPG/프로그래밍기초주차] -2 (0) | 2025.10.15 |
|---|---|
| 두 번째 팀프로젝트 [TextRPG/프로그래밍기초주차] (0) | 2025.10.14 |
| C# 스네이크게임 만들기 (0) | 2025.10.09 |
| Text RPG 개인과제 완료 (0) | 2025.10.01 |
| Text RPG 개발-2 (0) | 2025.09.30 |