Skip to content

[블랙잭 게임 1단계] 리뷰 요청 드립니다. #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 55 commits into
base: chunghyeon-kim
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
cd61a5b
docs: 구현 기능 목록 작성
chunghyeon-kimm Jul 25, 2021
9a8ad41
feat: Type, Denomination Enum으로 카드 객체 생성
chunghyeon-kimm Jul 25, 2021
49385ff
feat: Deck 객체, 테스트 작성
chunghyeon-kimm Jul 25, 2021
5850dd0
feat: Player 클래스 작성
chunghyeon-kimm Jul 25, 2021
f5e6914
feat: Player가 카드 하나를 받는 기능 구현
chunghyeon-kimm Jul 25, 2021
c43c521
refactor: 롬복 추가, 오타 수정
chunghyeon-kimm Jul 25, 2021
55f99ee
feat: 덱에서 카드를 하나 뽑는 메서드 작성
chunghyeon-kimm Jul 25, 2021
0d0b13c
feat: 카드의 숫자값을 Score 객체로 생성하고 Enum 클래스들에 롬복 적용
Ariel429 Jul 26, 2021
e9ed25c
refactor: 한장의 카드를 Deck에서 뽑아주는 기능 리팩토링
Ariel429 Jul 26, 2021
2b3f5c5
feat: 딜러 객체 생성 및 카드합이 16이하일 때까지 카드를 한 장씩 뽑을 수 있는 로직 구현
Ariel429 Jul 26, 2021
a6c12c5
docs: 객체 설계와 로직 구현에 대한 구상
Ariel429 Jul 26, 2021
b2198e8
Implements missing components to a commit
Ariel429 Jul 26, 2021
5ec0118
feat: 게임종료 후 카드값의 합을 구하는 로직을 util성 클래스로 구현
Ariel429 Jul 27, 2021
f0cbf79
feat: Participant 인터페이스 추가 및 게임 참여자가 가진 카드 덱 값의 합을 구하는 로직 구현
Ariel429 Jul 27, 2021
28c5a8d
refactor: Participant 추상클래스를 생성하여 Player와 Dealer가 각각 상속받도록 리팩토링
Ariel429 Jul 27, 2021
ac18db0
feat: 입력받은 이름을 ,를 기준으로 잘라 Player 목록을 생성하는 로직 구현
Ariel429 Jul 27, 2021
b1e1a25
feat: InputView를 생성하여 사용자 이름을 입력받는 기능 구현
Ariel429 Jul 27, 2021
7b7ed47
feat: Player에게 카드 한장을 더 받을지 묻는 기능 구현
Ariel429 Jul 27, 2021
8dab5e0
refactor: 누락된 사항 commit
Ariel429 Jul 27, 2021
f5829e0
feat: Controller와 Application을 생성, 카드를 두 개씩 분배하는 기능 구현
Ariel429 Jul 27, 2021
f0ec5f4
docs: 설계, 로직 구현에 대한 메모
Ariel429 Jul 27, 2021
2cc96f3
feat: OutputView 객체 생성, 게임에서 처음 카드를 두 개씩 분배하는 문구 출력 기능 구현
Ariel429 Jul 28, 2021
e99f18b
feat: 딜러와 플레이어가 처음 분배받은 카드 목록의 출력 기능 구현
Ariel429 Jul 28, 2021
9bfe228
feat: 플레이어가 21을 넘지 않는 선에서 카드를 한장씩 받는 로직 구현
Ariel429 Jul 28, 2021
02ba420
feat: 딜러 카드목록의 합이 17미만이면 카드를 추가로 한 장 받는 로직 구현
Ariel429 Jul 28, 2021
02b8932
refactor: 카드, 게임참가자, 결과로 package 분리
Ariel429 Jul 28, 2021
6765478
feat: winningResult, Rule을 Enum으로 선언하여 TotalResult 객체를 생성해 저장하도록 구현
Ariel429 Jul 28, 2021
d7e50f4
feat: 플레이어와 딜러의 승패를 판가름하여 플레이어 기준으로 승패를 발표하는 기능 구현
Ariel429 Jul 28, 2021
33dc3e5
feat: 플레이어와 딜러의 승패를 판가름하여, 딜러기준으로 승패를 발표하는 로직 구현 및 누락test 커밋
Ariel429 Jul 28, 2021
56880bb
feat: 게임 종료 후 모든 플레이어의 카드 목록, 카드값의 합 출력하는 로직 구현
Ariel429 Jul 28, 2021
f3f0d12
feat: 게임 종료 후 최종 결과를 출력하는 로직 구현
Ariel429 Jul 28, 2021
3260c5d
docs: 누락된 커밋 추가
Ariel429 Jul 28, 2021
b71d341
refactor: 카드 덱을 생성하는 클래스를 DeckFactory로 분리, 이중 depth 제거
Ariel429 Jul 29, 2021
6f547a3
refactor: 이중 depth 제거 (do-while문 사용)
Ariel429 Jul 29, 2021
1177903
refactor: 사용자의 이름 입력에 대한 예외처리
Ariel429 Jul 29, 2021
74e1443
refactor: 카드 덱에서 52장을 초과하는 카드를 뽑는 경우에 대한 예외처리
Ariel429 Jul 29, 2021
3aa80e6
feat: 사용자 이름에 대한 예외 처리
Ariel429 Jul 29, 2021
22f26dd
feat: 사용자의 이름 입력(중복 이름이 입력된 경우)에 대한 예외처리
Ariel429 Jul 29, 2021
68db2ce
refactor: 플레이어가 카드를 한장 더 뽑겠다는 대답의 유효성 검사 로직 추가
Ariel429 Jul 29, 2021
aa1ab86
refactor: 불필요한 주석 및 쓰이지 않은 import정리
Ariel429 Jul 29, 2021
e5a2991
refactor: Controller의 drawCardToPlayer 메서드 수정
chunghyeon-kimm Jul 29, 2021
4eed0a8
refactor: 문자열 상수처리
chunghyeon-kimm Jul 29, 2021
b21caf0
refactor: 테스트 정리
chunghyeon-kimm Jul 29, 2021
61696cb
refactor: Dto 클래스 이름 변경, DrawCard 입력이 yes인지 확인하는 메서드를 DrawCardRequest…
chunghyeon-kimm Aug 9, 2021
b00b293
refactor: DrawCard 응답의 유효성 검사 메서드를 DrawCardRequestDto로 이동 및 테스트메서드 이동
chunghyeon-kimm Aug 9, 2021
dc99787
refactor: CardTest printCardTwoDiamond 메서드 수정
chunghyeon-kimm Aug 9, 2021
8cd41f3
refactor: Player클래스 toString 오버라이딩 삭제, outputView에 플레이어 게임결과 출력메서드 추가
chunghyeon-kimm Aug 9, 2021
634acab
refactor: 메서드 이름 수정 (win -> resultVersusDealer)
chunghyeon-kimm Aug 9, 2021
3818a06
refactor: Rule 메서드 이름 변경 (compare -> findMatchingRule)
chunghyeon-kimm Aug 9, 2021
49d48e7
refactor: static block 추가
chunghyeon-kimm Aug 12, 2021
080bc70
feat: Rule 비교 로직 추가
chunghyeon-kimm Aug 14, 2021
4b3f64b
refactor: 카드 뽑을 때 RandomNumber 생성하는 부분을 분리
chunghyeon-kimm Aug 14, 2021
0aebc8c
refactor: Controller 클래스변수를 지역변수로 변경
chunghyeon-kimm Aug 14, 2021
bfe22bb
refactor: get -> orElseThrow
chunghyeon-kimm Aug 15, 2021
13b9421
refactor: player와 dealer를 비교해서 게임 결과를 반환하는 로직을 Player에서 Rule로 옮기고 메서드…
chunghyeon-kimm Aug 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
# java-blackjack

# 구현 기능 목록

-[x] 게임
- [x] 플레이어들의 이름을 입력받는다.
- [x] 최소 플레이어는 1명으로, 딜러와 게임을 진행한다.
- [x] ,를 기준으로 분리

- [x] 시작시 각 플레이어가 두 장의 카드를 지급받는다.
- [x] 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 뽑는다.
- [x] 딜러는 처음에 받은 2장의 합계가 17이상이면 추가로 받을 수 없다.
- [x] 플레이어는 카드 숫자의 합이 21이 초과하지 않는 동안 원하는 만큼 카드를 뽑을 수 있다.
- [x] 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다.
- [x] Ace를 가진 플레이어는 에이스의 숫자값으로 1 혹은 11을 선택할 수 있다.
- [x] King, Queen, Jack의 숫자값은 각각 10으로 취급한다.

-[x] 출력
- [x] 게임 시작 시 각 플레이어가 받은 2장의 카드를 출력한다.
- [x] 각 플레이어에게 추가로 카드를 받을 것인지 묻는다.
- [x] 카드를 추가로 받으면 해당 플레이어가 가진 카드 목록을 출력한다.
- [x] 딜러는 카드값의 합이 16이하면 카드를 추가로 받았다는 메시지를 출력한다.

- [x] 모든 플레이어가 더이상 카드를 뽑지 않으면 게임이 종료된다.
- [x] 플레이어별 카드의 목록을 출력한다.
- [x] 플레이어별 카드 숫자의 합을 계산하여 결과로 출력한다.

- [x] 게임을 완료한 후 각 플레이어별로 승패를 출력한다.
- [x] 딜러는 각 플레이어와의 승패를 누적해서 출력한다.
- [x] 플레이어는 딜러와의 승패를 출력한다.


- [x] 예외
- 이름
- [x] 플레이어의 이름은 공백, null 값이 될 수 없다.
- [x] 플레이어의 이름은 특수문자를 포함할 수 없다.
- [x] 플레이어의 이름은 "딜러"가 될 수 없다.
- [x] 플레이어는 서로 중복된 이름을 가질 수 없다.

- 카드 덱
- [x] 하나의 카드덱에서는 카드를 52장 초과하여 뽑을 수 없다.

- 플레이어에게 카드를 더 받을 것인지 질문
- [x] y, n가 아닌 형태로 대답할 수 없다.
- [x] 대문자 Y, N도 올바른 답으로 인식한다.
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ repositories {
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.0')
testImplementation('org.assertj:assertj-core:3.15.0')

compileOnly 'org.projectlombok:lombok:1.18.20'
annotationProcessor 'org.projectlombok:lombok:1.18.20'

testCompileOnly 'org.projectlombok:lombok:1.18.20'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.20'

implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.0'
}

test {
Expand Down
113 changes: 113 additions & 0 deletions memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
domain

- Card, Deck, Player, Dealer, Game, Statistics
- Game : 카드 분배 역할 (카드 분배 조건도 이 객체에서 판단)
- Statistics : 카드분배가 완료된 후 점수계산 및 승패 결과 도출

View

- 플레이어 이름을 입력 받고 결과를 출력

Controller

- 게임 전체 흐름 제어 (입력, 게임실행, 출력)

### Enum

1. 게임컨트롤러 (카드분배- 질문)
- 답(YES, NO)

2. 게임결과 (결과 승패를 판단)
- 승(플레이어 점수, 딜러점수, 승(플), 패(딜러))
- 패(플레이어 점수, 딜러점수, 패(플), 승(딜러))
- 무승부(플레이어 점수, 딜러점수, 무(플,딜러))

domain

- Card, Deck, Player, Dealer, Score(계산), GameResult(승패도출)
- Answer(질문답), Results(승패무)

View

- inputview: 입력받는 것(질문에 대한 답을 받는 부분)
- outputview: 출력하는 것

Controller

- 카드를 분배(drawCard), 더 받을지 질문, 하나카드를 분배...
- 흐름제어

# Ace 카드 처리 로직

Player -> 21 한계치 Dealer -> 17 한계치

모든 게임스코어합은 -> 21이 게임의 종료 지점

Score 계산 && 카드를 더 뽑게할지말지

- 카드의 Score가 계산로직으로 21(Player) 17(Dealer) 되기전까지 카드를 더 뽑을지 질문

## 계산로직

1. 카드 목록에 포함된 에이스 카드의 숫자를 추출한다. -> n
2. n+1의 길이를 가지는 배열을 생성하고, 에이스 카드를 1로 계산한 카드합과 거기에 차례대로 10을 더한 값을 배열에 입력한다.
3. 배열의 숫자들 중 21을 초과하지 않으면서 가장 큰 값을 취한다.

ex) Ace, Ace, 8일 경우 int[3] = {10, 20, 30} -> 20을 숫자합으로 계산

### 게임종료

카드를 한 장 뽑을 때마다 모든 경우의 수를 계산. Ace를 1로, 혹은 11로 계산하는 경우의 수를 다 생각해서 모든 경우의 수 중 카드합의 최소값이 한계치를 넘는 경우에 카드를 그만 뽑도록.

Ace 7, 9

27을 취한다. 카드를 더 못뽑음.

17

Ace, 7, 9, 4

-> 21

## 카드 뽑기

<딜러>
처음에 2장의 카드를 받고 카드합이 16 이하이면 한 장을 더 뽑는다. 에이스 1장은 11로 계산하고 나머지 에이스는 1로 계산한다. -> 에이스가 포함되어 있을 경우 카드합에 10을 더해준다

<에이스가 포함되어 있지 않을 경우>

카드합이 16 이하이면 카드를 뽑음.

<에이스가 포함되어 있는 경우>

카드합에 10을 더한 값이 16 이하이면 카드를 뽑음.

## 카드 뽑기 및 에이스가 여러개 포함될 가능성이 있는 카드덱의 합 계산

// 카드값의 합(ace가 11)
int scoreOfAceAsEleven; // 에이스의 개수 int aceCount; // Ace카드값의 차이 int DiFFERENCE_OF_ACE_VALUE = 10;

calcaluateResult(){
while (canCountAceAsOne) {
카드값의 합 scoreOfAceAsEleven = scoreOfAceAsOne(scoreOfAsAsEleven);
aceCount--;
}

return scoreOfAceAsEleven;
}

boolean canCountAceAsOne(int 카드값의 합, 에이스의 개수){
에이스가 하나라도 있음 > 0 && 카드 합이 21(상수선언)을 넘음(ace를 1로 취급가능)}
}

int scoreOfAceAsOne(int 카드값의 합){
(ace를 11로 취급한) scoreOfAceAsEleven 카드값의 합 - 10
}

- 17 dealer = threshould

컨트롤러에서 딜러에게 boolean Drawble(){ return calculateResult() < 17 } drawCard()

- 21 player 컨트롤러에서 플레이어에게 더 받을건지 묻는 질문 boolean 플레이어의 답 Yes && Drawable() { return calculateResult() < 21 } drawCard()


14 changes: 14 additions & 0 deletions src/main/java/blackjack/BlackjackApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package blackjack;

import blackjack.controller.BlackJackController;

public class BlackjackApplication {

public static void main(String[] args) {
try {
new BlackJackController().run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
66 changes: 66 additions & 0 deletions src/main/java/blackjack/controller/BlackJackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package blackjack.controller;

import blackjack.domain.card.Deck;
import blackjack.domain.participant.Dealer;
import blackjack.domain.participant.Player;
import blackjack.domain.participant.PlayersFactory;
import blackjack.domain.result.GameResult;
import blackjack.dto.DrawCardRequestDto;
import blackjack.dto.PlayersNameInputDto;
import blackjack.view.InputView;
import blackjack.view.OutputView;

import java.util.List;

public class BlackJackController {
private final InputView inputView = new InputView();
private final OutputView outputView = new OutputView();

public void run() {
PlayersNameInputDto namesInput = inputView.getPlayersName();
Dealer dealer = new Dealer();
Deck deck = new Deck();
List<Player> players = PlayersFactory.createPlayers(namesInput.getPlayersName());

drawTowCards(dealer, players, deck);
drawCardToPlayers(players, deck);
drawCardToDealer(dealer, deck);
outputView.printCardsResult(dealer, players);
outputView.printGameResult(GameResult.of(dealer, players));
}

private void drawTowCards(Dealer dealer, List<Player> players, Deck deck) {
for (Player player : players) {
player.receiveCard(deck.drawCard());
player.receiveCard(deck.drawCard());
}
dealer.receiveCard(deck.drawCard());
dealer.receiveCard(deck.drawCard());
outputView.printFirstCardsGiven(players, dealer);
outputView.printDealerCard(dealer);
outputView.printPlayersCard(players);
}

private void drawCardToPlayers(List<Player> players, Deck deck) {
for (Player player : players) {
drawCardToPlayer(player, deck);
}
}

private void drawCardToPlayer(Player player, Deck deck) {
DrawCardRequestDto drawCardRequest = inputView.getPlayersResponse(player);
while (player.drawable() && drawCardRequest.isYes()) {
player.receiveCard(deck.drawCard());

outputView.printCards(player);
drawCardRequest = inputView.getPlayersResponse(player);
}
}

private void drawCardToDealer(Dealer dealer, Deck deck) {
while (dealer.drawable()) {
dealer.receiveCard(deck.drawCard());
outputView.printDealerCardGiven();
}
}
}
46 changes: 46 additions & 0 deletions src/main/java/blackjack/domain/card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package blackjack.domain.card;

import lombok.Getter;

import java.util.Objects;

@Getter
public class Card {

private final Denomination denomination;
private final Type type;

public Card(Denomination denomination, Type type) {
this.denomination = denomination;
this.type = type;
}

public boolean isAce() {
return denomination.equals(Denomination.ACE);
}


@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Card)) {
return false;
}
Card card = (Card) o;
return denomination == card.denomination &&
type == card.type;
}

@Override
public int hashCode() {
return Objects.hash(denomination, type);
}

@Override
public String toString() {
return denomination.getDenominationName() + type.getType();

}
}
32 changes: 32 additions & 0 deletions src/main/java/blackjack/domain/card/Deck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package blackjack.domain.card;

import blackjack.domain.card.strategy.DrawStrategy;
import blackjack.domain.card.strategy.RandomDrawStrategy;
import lombok.Getter;

import java.util.LinkedList;
import java.util.List;

public class Deck {

private static final String ALERT_NO_CARD_LEFT = "사용 가능한 카드를 모두 소진하였습니다.";

@Getter
private final List<Card> deck;

public Deck() {
this.deck = new LinkedList<>(DeckFactory.createDeck());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LinkedList로 선언하신 이유는 카드를 한장씩 삭제할 때 ArrayList가 비효율적이라서 그렇게 하신걸까요? 👏

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 그렇습니다.

}

public Card drawCard() {
if (deck.isEmpty()) {
throw new RuntimeException(ALERT_NO_CARD_LEFT);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재는 경기도중에 카드가 모두 소진되면, 프로그램이 종료되는 구조인데요. 일반적으로 경기를 하다, 카드가 모두 소진된다고 그 게임이 무효가 되지 않듯이 조금 더 구체적인 방법/구현을 고민해보면 좋을 것 같아요. (구현은 하지 않으셔도 됩니다)

도메인에 대해서 조금 더 능동적으로 이해하면 어떨까 라는 의미에서 드린 피드백입니다. (참고로 블랙잭에서는 참가 인원을 제한하고 있더라구요🙂)

}
return deck.remove(generateNextDrawCardIndex(new RandomDrawStrategy()));
}

private int generateNextDrawCardIndex(DrawStrategy drawStrategy) {
return drawStrategy.getNextIndex(deck);
}

}
39 changes: 39 additions & 0 deletions src/main/java/blackjack/domain/card/DeckFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package blackjack.domain.card;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class DeckFactory {

private static final List<Card> cardsDeck;

static {
cardsDeck = createCardsDeck();
}

private static List<Card> createCardsDeck() {
List<Card> cards = new ArrayList<>();

for (Denomination denomination : Denomination.values()) {
cards.addAll(createCards(denomination));
}
return cards;
}

private static List<Card> createCards(Denomination denomination) {
List<Card> cards = new ArrayList<>();

for (Type type : Type.values()) {
cards.add(new Card(denomination, type));
}

return cards;
}

public static List<Card> createDeck() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메소드의 네이밍으로 봤을 때 Deck 을 만들 것 같은데, List 를 반환하고 있는 부분이 직관적으로 이해되지 않는 것 같습니다. 추가로 어플리케이션이 뜸과 동시에 cardDecks를 만들고 있어서, 사용한다면 getter 형태로 사용해야하지 않을까 싶네요.

혹시 이 메소드를 사용하는 사람 입장에서는 어플리케이션에서 해당 값(List<Card>)은 항상 고정되어 있다라는걸 몰라도 되고 필요할 때 달라하면 된다 느낌으로 작성하신걸까요? > 요 경우에도 사용자 입장에선 항상 덱을 만들거라 예상하고 로직을 진행할 수 있어서 위험할 것 같아요!

return Collections.unmodifiableList(cardsDeck);
}
}


Loading