티스토리 뷰
들어가기 전 설명
현재 구현한 코드들은 GitHub 에 있습니다. 글을 보다가 헷갈리시면 소스코드로 참고 해주시기 바랍니다.
또한, JAVA console으로 구현하였습니다.
게임 규칙
- 게임 딜러와 플레이어 1대1로 진행합니다.
- 딜러를 포함한 플레이어에게 카드 두 장을 나누어주고, 딜러의 카드 한 장은 게이머에게 보이지 않습니다.
- 딜러는 카드의 합이 16 이하면 무조건 한 장을 더 받아야 하고, 17 이상의 경우에는 멈추어야 합니다.
- 딜러의 카드와 합이 같으면 비깁니다.
- 게이머는 먼저 받은 카드 두 장의 합이 21에 못 미치면 한 장씩 더 받을 수 있고, 멈출 수도 있습니다.
- 게이머는 카드의 합이 딜러보다 먼저 21이 되거나 딜러보다 21에 가깝게 되면 이기고, 카드를 더 받았는데 21을 초과하면 집니다.
- 카드 중에 에이스카드(A) 는 1이나 11으로 취급할 수 있고, 10, J, Q, K는 모두 10으로 계산합니다.
- 카드 오픈을 했을 때 게이머의 카드 숫자합이 21미만이면 딜러와 게이머 카드 숫자합을 측정합니다.
이 때, 딜러도 21을 넘으면 딜러가 게임에서 집니다. - 카드 합이 21이 되면 블랙잭(Blackjack)이 되고 즉시 승리합니다
https://ko.wikipedia.org/wiki/%EB%B8%94%EB%9E%99%EC%9E%AD
코드 구현하기
- Card
- CardDeck
- Dealer
- Gamer
- Rule
순서대로 구현하려고 합니다.
Card.java
카드 객체는 카드덱, 딜러, 게이머, 규칙에도 어디서든 의존하며 사용합니다.
왜냐하면 카드덱이 카드로 이루어져 있고 딜러와 게이머도 카드를 소지하고 있어야 하며 그 카드 점수로 룰을 따지기 때문입니다.
카드의 패턴과 숫자를 필드로 가지고 있으며 Enum으로 구현하고자 하였습니다.
근데 구현하면서 에이스 카드는 1 또는 11의 점수를 얻을 수 있다는 규칙 때문에 Denomination Enum 객체에서 Enum Value를 조회할 때 배열로 리턴을 하는 것을 볼 수 있습니다.
이것 때문에 뭔가 되게 짜치는(?) 로직이 탄생되어 버린 것 같습니다.
우선은 구현은 하고 개선, 보완할 점에서 개선이 된다면 보완 해보도록 하겠습니다.
public class Card {
private Pattern pattern;
private Denomination denomination;
@Override
public String toString() {
return pattern + "이며, " + denomination + "이며 " +
"보이는 카드 숫자는 " + denomination.getValues()[0] + " 입니다";
}
public Card(Pattern pattern, Denomination denomination) {
this.pattern = pattern;
this.denomination = denomination;
}
enum Denomination { // 카드 숫자
TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6),
SEVEN(7), EIGHT(8), NINE(9), TEN(10),
JACK(10), QUEEN(10), KING(10), ACE(1, 11);
public final int[] values;
Denomination(int ... value) {
this.values = value;
}
public int[] getValues() {
return values;
}
}
enum Pattern { // 카드 패턴
DIAMOND, HEART, CLUB, SPADE
}
public Denomination getDenomination() {
return denomination;
}
}
CardDeck.java
카드를 뽑을 때 맨 위에에서 뽑기 때문에 List 컬렉션이 아닌 Stack을 이용하였습니다.
밑장빼기 하고 싶으면 Deque를 이용하면 몰래 빼낼 수 있는...?
저희는 정정당당히 게임을 임하여 카드덱에서 맨 위에 부터 카드를 뽑도록 하겠습니다.
객체지향적으로 구현을 하다보면 해당객체를 의인화를 해야될 때가 많은 것 같습니다.
일상 생활에는 "카드를 사람이 섞는다" 라고 한다면 코드 객체지향 프로그래밍 세계해서는 "카드가 스스로 섞는다"가 되는 것이죠.
그래서 카드덱 객체서 스스로 셔플 될 수 있어서 private로 셔플 메서드를 넣고 카드들을 캡슐화를 진행해서 외부에서 카드 1장씩 가져가도록 구현하게 되었습니다.
public class CardDeck {
private Stack<Card> cardDeck;
// Getter
public Stack<Card> getCardDeck() {
return cardDeck;
}
public CardDeck() {
cardDeck = new Stack<>();
init();
shuffle();
}
// 트럼트 카드 세팅
private void init(){
// 카드 덱에 52장의 카드 추가
for (Card.Pattern pattern : Card.Pattern.values()) {
for (Card.Denomination denomination : Card.Denomination.values()) {
cardDeck.add(new Card(pattern, denomination));
}
}
}
// 카드 섞기
private void shuffle(){
Collections.shuffle(cardDeck);
}
// 카드 얻기
public Card getCard(){
if(cardDeck.isEmpty()){
throw new DeckEmptyException("카드를 다 뽑았습니다.");
} else {
return cardDeck.pop();
}
}
}
Dealer.java
이제 이렇게 카드를 나눠주는 사람인 것이고 딜러도 2장의 카드가 필요하므로 Gamer와 역할 차이가 별로 안납니다.
인터페이스에 카드 드로우 메서드를 추상화하고 딜러에게 구현하였습니다.
근데 왜 딜러가 멋있어 보이져?
public class Dealer implements Player{
private List<Card> haveCards = new ArrayList<>();
private String name = "딜러";
private int score = 0;
public String getName() {
return name;
}
public List<Card> getHaveCards() {
return haveCards;
}
// 카드 뽑기
@Override
public void draw(CardDeck cardDeck) {
Card card = cardDeck.getCard();
haveCards.add(card);
}
}
Gamer.java
이 게임은 카지노 게임이므로 도박의 중독이 있으므로 주의해주시기 바랍니다.
도박 중독 안되게 그냥 게임은 게임일 뿐 돈걸고 하지 맙시다!!!!!!!
잘 못하다간 아래 오렌지 아저씨처럼 호구 아저씨 되는겁니다.
게이머는 딜러와 같은 역할을 하며 사용자 닉네임을 설정할 수 있게 하였습니다.
인터페이스에 카드 드로우 메서드를 추상화하고 게이머에게 구현하였습니다.
public class Gamer implements Player {
private List<Card> haveCards = new ArrayList<>();
private String name;
private int score = 0;
public Gamer(String name) {
this.name = name;
}
public List<Card> getHaveCards() {
return haveCards;
}
public String getName() {
return name;
}
// 카드 뽑기
@Override
public void draw(CardDeck cardDeck) {
Card card = cardDeck.getCard();
haveCards.add(card);
}
}
Rule.java
심판 같은 객체라고 생각하시면 됩니다.
카드를 통해서 점수를 계산하고, 그 점수에 따라서 누가 승리자 인지 판별해 줍니다.
인생에서 퇴장안당하고 싶으면 도박중복은 절대 안됩니다.!!
승리자를 판별 할 때 여러번의 분기처리를 하기 위해 if-else 문을 많이 사용한 것을 볼 수 있습니다.
만약 블랙잭 규칙이 자주 바뀌거나 좀 세세한 승리 판별 규칙이 달라지면 오류가 많이 발생할 확률이 큰 로직인 것 같습니다.
개선 및 보완할 때 이것을 보완 해보도록 하겠습니다.
public class Rule {
private int VICTORY_DEALER = 0;
private int VICTORY_GAMER = 0;
private final int BLACKJACK_SCORE = 21;
public int getVICTORY_DEALER() {
return VICTORY_DEALER;
}
public int getVICTORY_GAMER() {
return VICTORY_GAMER;
}
public int getBLACKJACK_SCORE() {
return BLACKJACK_SCORE;
}
// 점수 계산
public int calculateScore(List<Card> cards){
int sum = 0;
int aceCount = 0;
// 첫 번째 합계 계산 및 에이스 카운트
for (Card card : cards) {
int value = card.getDenomination().getValues()[0];
if (value == 1) { // 에이스 카드인 경우
value = 11; // 먼저 11로 계산
aceCount++;
}
sum += value;
}
// 점수가 21을 초과하면, 에이스를 1로 변환
while (sum > 21 && aceCount > 0) {
sum -= 10; // 에이스 하나를 11에서 1로 변경 (11 - 10 = 1)
aceCount--;
}
return sum;
}
// 승리자 판별
public String getWinner(Dealer dealer, Gamer gamer) {
int dealerScore = calculateScore(dealer.getHaveCards()); // 딜러 점수
int gamerScore = calculateScore(gamer.getHaveCards()); // 게이머 점수
String winner = null;
if (gamerScore == BLACKJACK_SCORE) { // 게이머가 블랙잭일 경우
winner = gamer.getName();
VICTORY_GAMER++;
} else if (gamerScore < BLACKJACK_SCORE && dealerScore < gamerScore) { // 블랙잭 점수보다 낮으면서 딜러보다 점수 높을 떄
winner = gamer.getName();
VICTORY_GAMER++;
} else if (BLACKJACK_SCORE < dealerScore && gamerScore < BLACKJACK_SCORE) { // 딜러도 블랙잭 점수 넘었을 떄
winner = gamer.getName();
VICTORY_GAMER++;
} else if (gamerScore < BLACKJACK_SCORE && (gamerScore == dealerScore)){ // 무승부
winner = "무승부";
} else {
winner = dealer.getName();
VICTORY_DEALER++;
}
return winner;
}
}
다음 이야기
다음 글에서는 이 도메인 로직을 가지고 서비스를 구현한다면 어떻게 할지를 구현해보도록 하겠습니다.
감사합니다.
'프로그래밍 언어 > 💫JAVA' 카테고리의 다른 글
[JAVA] 객체지향 프로그래밍을 배우는 블랙잭 게임 (4/4) : 개선 사항 및 후기 (1) | 2024.06.24 |
---|---|
[JAVA] 객체지향 프로그래밍을 배우는 블랙잭 게임 (3/4) : 비즈니스 로직 개발 및 실행 (2) | 2024.06.18 |
[JAVA] 객체지향 프로그래밍을 배우는 블랙잭 게임 (1/4) : 설계 단계 (0) | 2024.06.06 |
[JAVA] 멀티쓰레드와 쓰레드 풀 : 동시 프로그래밍의 핵심 (0) | 2024.02.14 |
[JAVA] URL : 네트워크 통신과 리소스 접근을 위한 핵심 클래스 (0) | 2023.12.28 |
- Total
- Today
- Yesterday
- Cors
- AJAX
- Mac
- spring
- 오라클
- 데이터 베이스
- 디자인패턴
- Spring Security
- 네트워크
- 개발블로그
- git
- 자바스크립트
- 개발환경
- Front
- 프론트
- java
- Fetch
- aws
- DBeaver
- 비동기
- 개발자
- JavaScript
- 프로세스
- 코딩테스트
- 개발
- 템플릿
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |