Skip to content
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

레디 step1 미션 제출 #6

Open
wants to merge 103 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 77 commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
e504ca0
docs: 기능 요구 사항 작성
reddevilmidzy Mar 5, 2024
3ed10c0
test: 카드 합 구하기
JINU-CHANG Mar 5, 2024
42a28a9
feat: 카드 합 구하기
reddevilmidzy Mar 5, 2024
8972ef5
chore: 패키지 변경
JINU-CHANG Mar 5, 2024
3a7d4d4
feat: 참여자 생성 기능 구현
JINU-CHANG Mar 5, 2024
d719d08
feat: null 이나 공백인 경우 예외가 발생 기능 구현
JINU-CHANG Mar 5, 2024
a83c06d
feat: 참여자 이름이 중복시 예외 발생 기능 구현
reddevilmidzy Mar 5, 2024
3a1cd48
feat: 총 참여자의 수는 2이상 8이하가 아닐시 예외 발생 기능 구현
JINU-CHANG Mar 5, 2024
acf6680
feat: 카드를 추가하는 기능 구현
reddevilmidzy Mar 5, 2024
68fa548
feat: Player가 Packet을 필드로 갖도록 구현
JINU-CHANG Mar 5, 2024
fbf514f
feat: CardDeck 생성 기능 구현
JINU-CHANG Mar 5, 2024
6017614
test: 플레이어에게 카드 2장을 나눠주는 기능 구현
reddevilmidzy Mar 5, 2024
ac2aa22
refactor: 리스트에서 스택으로 변경
JINU-CHANG Mar 5, 2024
aa6549c
feat: 참여자에게 카드를 2장씩 분배하는 기능 구현
JINU-CHANG Mar 5, 2024
9450f3b
feat: 딜러가 카드 2장을 분해하다.
reddevilmidzy Mar 5, 2024
b533c94
refactor: 스택에서 덱으로 변경
JINU-CHANG Mar 5, 2024
b458bdf
fix: CardNumber ONE 제거, TEN으로 추가
reddevilmidzy Mar 6, 2024
63ec5fb
feat: 카드 내의 ACE 가 포함된 경우 합이 21이하이면 에이스는 11로 계산 기능 구현
reddevilmidzy Mar 6, 2024
50e902d
feat: 카드 합 계산시 ACE 고려하는 로직 추가
JINU-CHANG Mar 6, 2024
52dcb84
refactor: Packet 클래스 이름을 Hands로 변경
reddevilmidzy Mar 6, 2024
ba0dd83
feat: 참여자의 대답이 y, n 가 아닐시 예외가 발생한다.
reddevilmidzy Mar 6, 2024
56eaa06
feat: 참여자 이름 입력받는 기능 구현
JINU-CHANG Mar 6, 2024
ee77777
feat: 플레이어의 시작 카드 출력 기능 구현
reddevilmidzy Mar 6, 2024
a5255e3
feat: 딜러 카드 출력하는 기능 구현
JINU-CHANG Mar 6, 2024
9575be9
docs: 완료한 기능 체크
JINU-CHANG Mar 6, 2024
e4d4369
feat: answer에 따라 카드를 추가 기능 구현
reddevilmidzy Mar 6, 2024
51f5aea
refactor: Dealer 필드에서 Players 제거
JINU-CHANG Mar 6, 2024
3a0ddb4
feat: 추가된 카드 출력하는 기능 구현
JINU-CHANG Mar 6, 2024
9b1e7a4
style: 사용하지 않는 메서드 삭제
reddevilmidzy Mar 6, 2024
546c4e9
feat: 참여자의 대답이 y일시 한번 더 deal 기능 구현
reddevilmidzy Mar 6, 2024
9fd7e9c
feat: 변경된 카드가 있을 경우 출력하는 기능 구현
JINU-CHANG Mar 6, 2024
3c2ce48
feat: 딜러의 카드가 17 이상이 될때까지 카드 받는 기능 구현
reddevilmidzy Mar 6, 2024
eb08c75
feat: 딜러가 카드를 받을 때 메시지 출력기능 구현
JINU-CHANG Mar 6, 2024
a1f0915
feat: 모든 참여자의 카드의 합을 출력하는 기능 구현
JINU-CHANG Mar 7, 2024
3595593
feat: hands의 승패 판단 기능 구현
reddevilmidzy Mar 7, 2024
e26d988
feat: 참여자의 승패 확인 기능 구현
reddevilmidzy Mar 7, 2024
bbd54df
style: '플레이어'를 '참여자'로 변경
reddevilmidzy Mar 7, 2024
57572c0
feat: 모든 참여자의 카드의 합을 출력하는 기능 구현
reddevilmidzy Mar 7, 2024
ef45b28
fix: 컴파일 에러 수정
JINU-CHANG Mar 7, 2024
8f1e9b8
feat: 딜러의 승패를 확인 기능 구현
JINU-CHANG Mar 7, 2024
755f99a
docs: 구현해야 할 기능 목록 추가
JINU-CHANG Mar 7, 2024
d8f2ad4
feat: 최종 결과 출력하는 기능 구현
JINU-CHANG Mar 7, 2024
4f080a1
feat: 참가자의 카드의 합이 21을 초과하면 BUST 메시지 출력 기능 구현
reddevilmidzy Mar 7, 2024
f6438d3
feat: 모든 참여자가 버스트라면 딜러는 딜하지 않는 기능 구현
reddevilmidzy Mar 7, 2024
649014a
feat: 무승부 기능 구현
JINU-CHANG Mar 7, 2024
7593784
fix: 딜러가 버스트일때 참여자가 버스트가 아니면 WIN
reddevilmidzy Mar 7, 2024
cbee98a
feat: 블랙잭 시 메시지 출력후 카드 더 받지 않는 기능 구현
JINU-CHANG Mar 7, 2024
0909ae6
docs: 블랙잭 규칙 추가
reddevilmidzy Mar 7, 2024
c96930e
Merge branch 'step1' of https://github.com/reddevilmidzy/java-blackja…
reddevilmidzy Mar 7, 2024
b188bdf
refactor: 'CardNumber'를 'Rank'로 변경, 'CardShape'를 'Shape'로 변경 및 card 패…
JINU-CHANG Mar 7, 2024
62493d6
refactor: players 관련 메서드의 파라미터 및 변수명 변경
reddevilmidzy Mar 7, 2024
4df9b26
refactor: 메서드 삭제 및 이름 변경
JINU-CHANG Mar 7, 2024
7b0d3d7
refactor: Player 클래스 메서드명 및 순서 변경
JINU-CHANG Mar 7, 2024
75d216b
refactor: CardDeck의 generate메서드 스트림 활용
reddevilmidzy Mar 7, 2024
72b3f45
refactor: Dealer 초기 카드 넘버 상수명 변경
reddevilmidzy Mar 7, 2024
6238248
refactor: Dealer가 Participant 상속받도록 변경
JINU-CHANG Mar 7, 2024
5a87d7c
docs: 기능 요구 사항 수정
reddevilmidzy Mar 8, 2024
72a3a20
fix: 카드의 사이즈가 같을 때 21에 가까운 카드 합이 이기는 기능 수정
reddevilmidzy Mar 8, 2024
9414fb4
refactor: Participant 추상클래스 생성
JINU-CHANG Mar 8, 2024
2d2154e
refactor: Dto의 이름 변경 및 정적 팩터리 메서드 명 변경
reddevilmidzy Mar 8, 2024
356674b
refactor: getCardNames 메서드 동작 로직을 hands 안으로 이동
reddevilmidzy Mar 8, 2024
da599d2
refactor: Participant 생성자 접근제한자 변경 및 메서드 정렬
reddevilmidzy Mar 8, 2024
0198084
refactor: Dealer 의 deal을 Dealer 클래스로 이동
reddevilmidzy Mar 8, 2024
781f8e8
refactor: controller 인덴트 줄이기 및 메서드 분리
JINU-CHANG Mar 8, 2024
162927e
refactor: cardDeckTest 클래스 접근제한자 변경
reddevilmidzy Mar 8, 2024
02211ea
refactor: null과 blank 테스트 코드 메서드 분리
JINU-CHANG Mar 8, 2024
2d96eec
refactor: 중복되는 테스트코드 변수 분리
JINU-CHANG Mar 8, 2024
32b1496
style: static 변수 import
JINU-CHANG Mar 8, 2024
d0c0451
refactor: 테스트 코드내에 중복되는 변수를 필드로 선언
reddevilmidzy Mar 8, 2024
23bdf69
refactor: outputView FORM 상수static 으로 변경
reddevilmidzy Mar 8, 2024
4e2a246
refactor: 사용하지 않는 필드 및 메서드 삭제
reddevilmidzy Mar 8, 2024
4ab6363
style: 코드 정렬
reddevilmidzy Mar 8, 2024
987be12
refactor: HandsTest 중복되는 테스트코드 분리
JINU-CHANG Mar 8, 2024
8c64ab9
Merge branch 'step1' of https://github.com/reddevilmidzy/java-blackja…
reddevilmidzy Mar 8, 2024
88665a8
refactor: ParticipantDto 레코드로 변경
reddevilmidzy Mar 8, 2024
7977057
refactor: Hands 메서드 분리
JINU-CHANG Mar 8, 2024
995d864
refactor: calculateResult 메서드 인덴트 줄이기
reddevilmidzy Mar 8, 2024
2d822e9
docs: 리팩터링 목록 추가
reddevilmidzy Mar 11, 2024
405ac6b
refactor: createEmptyPacket 메서드명 createEmptyHands로 수정
reddevilmidzy Mar 11, 2024
dfaae33
feat: 카드덱이 비었을때 꺼낸 경우 예외 발생
reddevilmidzy Mar 11, 2024
aaf03ab
refactor: Name 객체 포장
JINU-CHANG Mar 11, 2024
017e6a0
refactor: 승패무 판단을 Result의 책임으로 이동 (재미있게 BiPredicate 활용)
reddevilmidzy Mar 11, 2024
36094c8
refactor: Game 클래스 삭제 및 책임 분리
JINU-CHANG Mar 11, 2024
ad35da3
refactor: TestFixture 생성
JINU-CHANG Mar 11, 2024
5e142b0
style: import 문 순서 변경
reddevilmidzy Mar 11, 2024
cc6a91f
refactor: 테스트코드 변수명 변경 및 테스트 추가
reddevilmidzy Mar 11, 2024
d5564b4
fix: LinkedHashMap으로 변경하여 참여자 순서 유지
reddevilmidzy Mar 11, 2024
2b02cdb
refactor: System.out.println 대신 %n과 System.lineSeparator 사용
reddevilmidzy Mar 11, 2024
b23548c
docs: 리팩터링 목록 수정
reddevilmidzy Mar 11, 2024
8e95a70
feat: 참가자의 이름에 딜러가 사용될 시 예외 발생 기능 구현
reddevilmidzy Mar 11, 2024
2a8dfdc
refactor: isHit 메서드 생성
JINU-CHANG Mar 11, 2024
3de3d0b
docs: 리팩터링 목록 수정
JINU-CHANG Mar 11, 2024
21ec566
style: 메서드 위치 변경
JINU-CHANG Mar 11, 2024
f2d25ec
refactor: controller 메서드 추출
reddevilmidzy Mar 11, 2024
96cdc3b
chore: 패키지 정리
reddevilmidzy Mar 11, 2024
fec95ee
docs: 리팩터링 목록 추가
reddevilmidzy Mar 12, 2024
41b1df7
feat: 예외 시 재입력 기능 구현
reddevilmidzy Mar 12, 2024
4347c8c
feat: 커스텀 예외 구현
reddevilmidzy Mar 12, 2024
1713750
refactor: Participant 추상 메서드 사용
reddevilmidzy Mar 12, 2024
167957a
refactor: InvalidPlayerName 예외 클래스 이름 변경
reddevilmidzy Mar 12, 2024
e9374bc
refactor: 카드 생성 책임을 Card로 변경 및 카드를 캐시하여 가져오는 기능 구현
reddevilmidzy Mar 12, 2024
19b71c6
docs: 리팩터링 목록 수정
reddevilmidzy Mar 12, 2024
80e13e4
refactor: cardDeck 생성 책임 위치 변경
reddevilmidzy Mar 12, 2024
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
63 changes: 63 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# 블랙잭 규칙

## 참여자

**승**

* (딜러 카드 합 < 참여자 카드 합) && 참여자가 버스트가 아닌 경우
* (딜러 카드 합 == 참여자 카드 합) && (딜러 카드 수 > 참여자 카드 수) && 참여자가 버스트가 아닌 경우
* 딜러가 버스트인 경우 && 참여자가 버스트가 아닌 경우

**무**

* (딜러 카드 합 == 참여자 카드 합) && (딜러 카드 수 == 참여자 카드 수) && 참여자가 버스트가 아닌 경우

**패**

* 참여자가 버스트인 경우
* (딜러 카드 합 > 참여자 카드 합)
Comment on lines +5 to +18
Copy link
Member

Choose a reason for hiding this comment

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

개인적으로 참여자가 버스트가 아닌 경우가 맨 앞에 가있거나, 다른 방식으로 언급이 되면 더 좋을 것 같아요~
맨 뒤에 같은 말이 반복되어 가독성이 조금 떨어진다고 느껴집니다.

그리고 딜러 카드 합 == 참여자 카드 합 인데 딜러 카드 수 < 참여자 카드 수인 경우는 어떻게 되나요?
언급이 되어 있지 않아 여쭤봅니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

딜러 카드 합 == 참여자 카드 합 && 딜러 카드 수 < 참여자 카드 수 라면 "패"조건이네요 작성을 놓쳤네요😅


<br>

# 기능 요구 사항

* [x] 참여자 이름 입력 받는다.
* [x] 양 끝 공백을 제거한다.
* [x] 참여자는 쉼표(,)로 구분한다.
* [x] null 이나 공백인 경우 예외가 발생한다.
* [x] 쉼표로 시작시 예외가 발생한다. ex) ,pobi,jason
* [x] 쉼표로 끝날시 예외가 발생한다. ex) pobi,jason,
* [x] 쉼표가 연속으로 올시 예외가 발생한다. ex) pobi,,jason
* [x] 참여자 이름이 중복시 예외가 발생한다.
* [x] 총 참여자의 수는 2이상 8이하여야 한다.

<br>

* [x] 딜러가 카드 2장을 분배하다.
* [x] 카드는 총 6벌을 둔다. (52 * 6)
* [x] 딜러의 카드를 출력한다. (1장)
* [x] 참여자의 카드를 출력한다. (2장)

<br>

* [x] 참여자는 hit(y) / stay(n)를 선택한다.
* [x] y, n 가 아닐시 예외가 발생한다.
* [x] 참여자가 hit을 하는 경우 현재 가지고 있는 카드를 출력한다.
* [x] 참여자가 hit을 한 적 없이 stay를 하는 경우 현재 가지고 있는 카드를 출력한다.
* [x] 카드 합이 블랙잭인 경우 블랙잭 메시지를 출력한다.
* [x] 카드 합이 21 초과시 버스트 메시지를 출력한다.

<br>

* [x] 딜러의 카드의 합을 계산한다.
* [x] 카드 내의 ACE 가 포함된 경우
* ACE: 11
* 11로 했을 때 카드의 합이 21을 초과한 경우 1로 계산
* [x] 17 이상이 될때까지 카드를 받는다.

<br>

* [x] 모든 참여자의 카드의 합을 계산한다.
* [x] 딜러의 승패무를 확인한다.
* [x] 참여자의 승패무를 확인한다.
* [x] 게임 결과를 출력한다.
16 changes: 16 additions & 0 deletions src/main/java/application/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package application;

import controller.BlackJackController;
import java.util.Scanner;
import view.InputView;
import view.OutputView;

public class Application {
public static void main(String[] args) {
final Scanner scanner = new Scanner(System.in);
final InputView inputView = new InputView(scanner);
final OutputView outputView = new OutputView();
final BlackJackController blackJackController = new BlackJackController(inputView, outputView);
blackJackController.run();
}
}
98 changes: 98 additions & 0 deletions src/main/java/controller/BlackJackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package controller;

import domain.Answer;
import domain.CardDeck;
import domain.Game;
import domain.participant.Dealer;
import domain.participant.Player;
import domain.participant.Players;
import dto.DealerHandsDto;
import dto.ParticipantDto;
import dto.ParticipantsDto;
import view.InputView;
import view.OutputView;

public class BlackJackController {
private final InputView inputView;
private final OutputView outputView;

public BlackJackController(final InputView inputView, final OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public void run() {
Copy link
Member

Choose a reason for hiding this comment

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

메서드 길이가 10라인을 넘었네요

final Players players = Players.from(inputView.readNames());
final CardDeck cardDeck = CardDeck.generate();
final Dealer dealer = new Dealer(cardDeck);
final Game game = new Game(dealer, players);

initHands(players, dealer);
dealWithPlayers(players, dealer);

if (!players.isAllBust()) {
dealer.deal();
printDealerTurnMessage(dealer.countAddedHands());
}
printFinalResult(players, dealer, game);
}

private void printDealerTurnMessage(final int turn) {
for (int i = 0; i < turn; i++) {
outputView.printDealerTurnMessage();
}
}

private void dealWithPlayers(final Players players, final Dealer dealer) {
for (Player player : players.getPlayers()) {
deal(player, dealer);
}
}

private void initHands(final Players players, final Dealer dealer) {
dealer.initHands(players);
outputView.printStartDeal(DealerHandsDto.from(dealer), ParticipantsDto.of(players));
}

private void printFinalResult(final Players players, final Dealer dealer, final Game game) {
outputView.printHandsResult(ParticipantsDto.of(dealer, players));
outputView.printGameResult(game.getDealerResult(), game.getPlayersResult());
}

private void deal(final Player player, final Dealer dealer) {
boolean handsChanged = false;
boolean turnEnded = false;

while (!turnEnded) {
Answer answer = Answer.from(inputView.readAnswer(player.getName()));
dealer.deal(player, answer);

printHandsIfRequired(player, handsChanged, answer);
Copy link
Member

Choose a reason for hiding this comment

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

21 이상이면 카드를 받을 수 없는 것은 예제와 다르게 구현하셨는데,
보여준 적 없으면 보여준다는 것은 예제대로 구현하셨네요!

저는 이 부분이 생기면서 로직이 좀 복잡해진 듯 합니다.
혹시 이 기능이 꼭 필요하다고 생각하신 이유가 있나요?

Copy link
Member Author

Choose a reason for hiding this comment

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

오오 잘 캐치해주셨어요!

어떤건 예제대로 하고 어떤건 또 멋대로 구현하여 혼란을 줄 수도 있을거 같은데 이유를 설명해보자면;;;
21이상 일때 카드를 더 받을 수 없게 한것은 플레이어가 최적의 스코어를 얻을 수 있게하기 위함이었습니다

그리고 변경된 경우에만 출력하는 기능은 예제 보고 규칙을 파악하여 그냥 신나서 구현했던 거 같아요 ㅎㅎㅎ
꼭 필요하다고 생각해서 구현하진 않았습니다😅


handsChanged = true;
turnEnded = isTurnEnded(player, answer);
}
}

private void printHandsIfRequired(final Player player, final boolean handsChanged, final Answer answer) {
if (shouldShowHands(handsChanged, answer)) {
outputView.printHands(ParticipantDto.from(player));
}
}
Comment on lines +75 to +79
Copy link
Member

Choose a reason for hiding this comment

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

printHandsIfRequired 라는 네이밍을 봤을 때 한 번에 이해하기는 좀 힘든 것 같아요.

직역하면 "필요한 경우 핸드를 출력한다" 라는 의미 같은데,
왜 필요한지 알기 위해 메서드를 열어봤더니 또 if 조건 문 안에
shouldShowHands "보여줘야한다" 라는 의미의 네이밍 조건이 있어서.. 이해하기 조금 힘드네용 😊

메서드 이름에 행위를 드러내더라도 의도가 조금 섞이도록 하는게 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

네이밍 어려워요ㅜㅜ
사실 저 메서드명은 채찍피티가 짜줬던 거 같네요
좀 더 고민해보겠습니다!

Copy link
Member

Choose a reason for hiding this comment

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

ㅋㅋㅋㅋㅋㅋㅋ 저는 지쌤이라 부르는데 채찍피티도 재밌네요


private boolean isTurnEnded(final Player player, final Answer answer) {
if (player.isBust()) {
outputView.printBust();
return true;
}
if (player.isBlackJack()) {
outputView.printBlackJack();
return true;
}
return Answer.STAY.equals(answer);
}

private boolean shouldShowHands(final boolean handsChanged, final Answer answer) {
return (Answer.STAY.equals(answer) && !handsChanged) || Answer.HIT.equals(answer);
Copy link
Member

Choose a reason for hiding this comment

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

[의견] enum 비교는 == 을 이용하는 것이 일반적이라고 알고 있어요!

Copy link
Member

Choose a reason for hiding this comment

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

printHandsIfRequired 리뷰의 연장입니다!
이 부분이 컨트롤러에서 중요한 비중을 차지하고 있는 것 같아서 추가로 리뷰를 남겨드려요

shouldShowHands 네이밍이 일단 좀 아쉬운 것 같아용
메서드 네이밍으로 의도를 잘 드러내거나 혹은 내부 조건 로직을 더 가독성 있게 변경하면 어떨까 싶습니다.

Answer 에 메서드를 하나 만들어서 메서드 이름으로 의도를 드러내는 것도 하나의 방법이 될 것 같아요.
(Answer.isStay, Asnwer.isHit 등등)

그리고 부정연산자가 기본적으로 조건문 안에 들어가면 가독성에 안 좋다 생각합니다.
다른 조건과 결합되어 있을 때 더 심한 경향이 있는 것 같아서 따로 분리하는 것도 방법이 될 수 있을 것 같습니다.

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

import java.util.Arrays;

public enum Answer {
HIT("y"),
STAY("n");

private final String value;

Answer(final String value) {
this.value = value;
}

public static Answer from(final String value) {
return Arrays.stream(Answer.values())
.filter(answer -> answer.value.equals(value))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("[ERROR] 유효하지 않은 대답입니다."));
}
}
53 changes: 53 additions & 0 deletions src/main/java/domain/CardDeck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package domain;

import domain.card.Card;
import domain.card.Rank;
import domain.card.Shape;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.List;

public class CardDeck {
private static final int DECK_SIZE = 6;

private final Deque<Card> cards;
Copy link
Member

Choose a reason for hiding this comment

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

자료구조 중 deque를 사용하신 이유가 있나요?

Copy link
Member Author

Choose a reason for hiding this comment

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

요것도 시행착오가 있었는데,,

우선 처음에는 List 였습니다. 하지만 블랙잭 게임에선 계속 위의 값만 꺼내서 사용하기 때문에 상위원소 삭제가 간편한 Stack 을 사용하였다가 동기화 이슈로 LinkedList 를 사용하였다가 꺼내는 로직이 마음에 안들어 Deque를 사용하게 되었습니다!

정리하면 제일 위의 값을 삭제하기 위한 자료구조로 덱이 효율적이라고 판단하여 사용하였습니다!


public CardDeck(final Deque<Card> cards) {
this.cards = cards;
}

public static CardDeck generate() {
final List<Card> deck = new ArrayList<>();

for (int i = 0; i < DECK_SIZE; i++) {
deck.addAll(generateOneCardDeck());
}

Collections.shuffle(deck);
return new CardDeck(new ArrayDeque<>(deck));
}

public Card pop() {
return cards.pop();
}

public int size() {
return cards.size();
}

private static List<Card> generateOneCardDeck() {
return Arrays.stream(Shape.values())
.flatMap(shape -> matching(shape).stream())
.toList();
}

private static List<Card> matching(final Shape shape) {
return Arrays.stream(Rank.values())
.map(rank -> new Card(rank, shape))
.toList();
}
Copy link
Member

Choose a reason for hiding this comment

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

저도 고민했던 부분인데...
개인적으로 stream보다는 for문을 그냥 사용하는 것이 가독성 측면에서 더 좋은 것 같아요.
그냥 의견입니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

맞아요 저도 2중 포문이 더 직관적인거 같아요 ㅎㅎ


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

import domain.participant.Dealer;
import domain.participant.Player;
import domain.participant.Players;
import java.util.EnumMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class Game {

private final Dealer dealer;
private final Players players;

public Game(final Dealer dealer, final Players players) {
this.dealer = dealer;
this.players = players;
}

public Map<Player, Result> getPlayersResult() {
final Map<Player, Result> playerResult = new LinkedHashMap<>();

for (Player player : players.getPlayers()) {
playerResult.put(player, player.calculateResult(dealer));
}
return playerResult;
}

public Map<Result, Integer> getDealerResult() {
Map<Result, Integer> dealerResult = new EnumMap<>(Result.class);

for (Result value : getPlayersResult().values()) {
Result reversed = value.reverse();
Integer orDefault = dealerResult.getOrDefault(reversed, 0);
dealerResult.put(reversed, orDefault + 1);
}
return dealerResult;
}
}
96 changes: 96 additions & 0 deletions src/main/java/domain/Hands.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package domain;

import domain.card.Card;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class Hands {

private static final int BLACK_JACK = 21;
public static final int EXTRA_ACE_VALUE = 10;

private final List<Card> cards;

public Hands(final List<Card> cards) {
this.cards = new ArrayList<>(cards);
}

public static Hands createEmptyPacket() {
return new Hands(new ArrayList<>());
}

public int sum() {
int total = cards.stream()
.mapToInt(Card::getCardNumber)
.sum();

return calculateTotalByAce(total);
}

public void add(final Card card) {
cards.add(card);
}

public boolean isBust() {
return sum() > BLACK_JACK;
}

public boolean isBlackJack() {
return sum() == BLACK_JACK;
}

private boolean hasAce() {
return cards.stream()
.anyMatch(Card::isAce);
}

public int size() {
return cards.size();
}

public List<String> getCards() {
return cards.stream()
.map(Card::toString)
.toList();
}
Copy link
Member

Choose a reason for hiding this comment

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

[의견] 이 메서드를 호출하는 코드를 작성할 때, 메서드 이름이 getCards 이기 때문에 반환 타입이 List 인 것으로 예상할 것 같아요. 메서드 이름을 바꿔보는건 어떤가요?

Copy link
Member Author

Choose a reason for hiding this comment

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

동의합니다! 메서드 네이밍 고민 많이하여도 마땅한 이름이 나오지 않는거 같아 일단 다른거 부터 구현하자! 하고 넘어갔다가 다시 와도 좋은 이름이 떠오르진 않는 거 같네요ㅜ


public Result calculateResult(final Hands target) {
if (!this.isBust() && target.isBust()) {
return Result.WIN;
}
if (this.sum() == target.sum() && this.size() == target.size() && !this.isBust()) {
return Result.TIE;
}
if (this.sum() >= target.sum() && this.sum() <= BLACK_JACK) {
return Result.WIN;
}
return Result.LOSE;
}
Copy link
Member

Choose a reason for hiding this comment

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

hands는 딜러와 플레이어 모두에게 있는데 이 결과 계산 메서드는 플레이어에게만 적용되는 메서드네요.

그리고 세 번째 if문에 첫 번째 조건... this.sum() > target.sum() 여야 하지 않을까요?!
그리고 두 번째 조건 this.sum() <= BLACK_JACK!this.isBust()으로 대체될 수 있어 보입니다.
일관성이 있으면 더 좋을 듯 해요!

Copy link
Member Author

Choose a reason for hiding this comment

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

this.sum() > target.sum() 오 맞네요!

감사합니다!


private int calculateTotalByAce(final int total) {
if (hasAce() && total + EXTRA_ACE_VALUE <= BLACK_JACK) {
Copy link
Member

Choose a reason for hiding this comment

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

에이스가 두 장 이상 있어도 한 개만 10점이 더해질 수 있다는 점을 이용해 구현하셨군요!
저도 이렇게 구현했는데, 어차피 한 번만 더해진다는 것을 알아야 코드를 이해하기 쉬울 것 같더라구요.
그래서 설명에 필요한 비용이 든다는 것이 좀 고민이었습니다.
혹시 관련 의견 있으신가요?

그것과 별개로... 비슷한 로직인데 결과물은 제 메서드보다 더 깔끔한 것 같네요 ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

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

후후후 어렵네요... EXTRA_ACE_VALUE를 Hands 가 알고 있는게 맞는지에 대한 고민도 있고;;
ACE는 1 점이 될 수 도 있고 11점이 될 수 도 있다는 이니까 메서드명에 calculateByAceRule 이런식으로 지어주면 그나마 좀 의미를 전달할 수 있지 않을까 싶네요!

return total + EXTRA_ACE_VALUE;
}

return total;
}

@Override
public boolean equals(final Object target) {
if (this == target) {
return true;
}

if (!(target instanceof Hands hands)) {
return false;
}

return Objects.equals(cards, hands.cards);
}
Copy link
Member

Choose a reason for hiding this comment

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

[질문] 동일한 카드를 들고 있다면, 내부의 순서에 상관 없이 동일해야 할 것으로 생각되는데, 맞나요? 맞다면 지금 그렇게 구현되어있는건가요?

Copy link
Member Author

Choose a reason for hiding this comment

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

내부의 순서 상관없이 동일하게 생각되어야 맞는거 같네요! 그냥 습관적으로 equals 를 구현하는 것이 문제였.. 감사합니다1!


@Override
public int hashCode() {
return Objects.hash(cards);
}
}
Loading