-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: main
Are you sure you want to change the base?
Changes from 77 commits
e504ca0
3ed10c0
42a28a9
8972ef5
3a7d4d4
d719d08
a83c06d
3a1cd48
acf6680
68fa548
fbf514f
6017614
ac2aa22
aa6549c
9450f3b
b533c94
b458bdf
63ec5fb
50e902d
52dcb84
ba0dd83
56eaa06
ee77777
a5255e3
9575be9
e4d4369
51f5aea
3a0ddb4
9b1e7a4
546c4e9
9fd7e9c
3c2ce48
eb08c75
a1f0915
3595593
e26d988
bbd54df
57572c0
ef45b28
8f1e9b8
755f99a
d8f2ad4
4f080a1
f6438d3
649014a
7593784
cbee98a
0909ae6
c96930e
b188bdf
62493d6
4df9b26
7b0d3d7
75d216b
72b3f45
6238248
5a87d7c
72a3a20
9414fb4
2d2154e
356674b
da599d2
0198084
781f8e8
162927e
02211ea
2d96eec
32b1496
d0c0451
23bdf69
4e2a246
4ab6363
987be12
8c64ab9
88665a8
7977057
995d864
2d822e9
405ac6b
dfaae33
aaf03ab
017e6a0
36094c8
ad35da3
5e142b0
cc6a91f
d5564b4
2b02cdb
b23548c
8e95a70
2a8dfdc
3de3d0b
21ec566
f2d25ec
96cdc3b
fec95ee
41b1df7
4347c8c
1713750
167957a
e9374bc
19b71c6
80e13e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# 블랙잭 규칙 | ||
|
||
## 참여자 | ||
|
||
**승** | ||
|
||
* (딜러 카드 합 < 참여자 카드 합) && 참여자가 버스트가 아닌 경우 | ||
* (딜러 카드 합 == 참여자 카드 합) && (딜러 카드 수 > 참여자 카드 수) && 참여자가 버스트가 아닌 경우 | ||
* 딜러가 버스트인 경우 && 참여자가 버스트가 아닌 경우 | ||
|
||
**무** | ||
|
||
* (딜러 카드 합 == 참여자 카드 합) && (딜러 카드 수 == 참여자 카드 수) && 참여자가 버스트가 아닌 경우 | ||
|
||
**패** | ||
|
||
* 참여자가 버스트인 경우 | ||
* (딜러 카드 합 > 참여자 카드 합) | ||
|
||
<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] 게임 결과를 출력한다. |
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(); | ||
} | ||
} |
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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 21 이상이면 카드를 받을 수 없는 것은 예제와 다르게 구현하셨는데, 저는 이 부분이 생기면서 로직이 좀 복잡해진 듯 합니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오오 잘 캐치해주셨어요! 어떤건 예제대로 하고 어떤건 또 멋대로 구현하여 혼란을 줄 수도 있을거 같은데 이유를 설명해보자면;;; 그리고 변경된 경우에만 출력하는 기능은 예제 보고 규칙을 파악하여 그냥 신나서 구현했던 거 같아요 ㅎㅎㅎ |
||
|
||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
직역하면 메서드 이름에 행위를 드러내더라도 의도가 조금 섞이도록 하는게 좋을 것 같아요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네이밍 어려워요ㅜㅜ There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [의견] enum 비교는 == 을 이용하는 것이 일반적이라고 알고 있어요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위
그리고 부정연산자가 기본적으로 조건문 안에 들어가면 가독성에 안 좋다 생각합니다. |
||
} | ||
} |
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] 유효하지 않은 대답입니다.")); | ||
} | ||
} |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 자료구조 중 deque를 사용하신 이유가 있나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 고민했던 부분인데... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 맞아요 저도 2중 포문이 더 직관적인거 같아요 ㅎㅎ |
||
|
||
} |
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; | ||
} | ||
} |
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(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [의견] 이 메서드를 호출하는 코드를 작성할 때, 메서드 이름이 getCards 이기 때문에 반환 타입이 List 인 것으로 예상할 것 같아요. 메서드 이름을 바꿔보는건 어떤가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hands는 딜러와 플레이어 모두에게 있는데 이 결과 계산 메서드는 플레이어에게만 적용되는 메서드네요. 그리고 세 번째 if문에 첫 번째 조건... There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에이스가 두 장 이상 있어도 한 개만 10점이 더해질 수 있다는 점을 이용해 구현하셨군요! 그것과 별개로... 비슷한 로직인데 결과물은 제 메서드보다 더 깔끔한 것 같네요 ㅎㅎ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 후후후 어렵네요... EXTRA_ACE_VALUE를 Hands 가 알고 있는게 맞는지에 대한 고민도 있고;; |
||
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); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [질문] 동일한 카드를 들고 있다면, 내부의 순서에 상관 없이 동일해야 할 것으로 생각되는데, 맞나요? 맞다면 지금 그렇게 구현되어있는건가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 내부의 순서 상관없이 동일하게 생각되어야 맞는거 같네요! 그냥 습관적으로 equals 를 구현하는 것이 문제였.. 감사합니다1! |
||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(cards); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
개인적으로
승
과무
에참여자가 버스트가 아닌 경우
가 맨 앞에 가있거나, 다른 방식으로 언급이 되면 더 좋을 것 같아요~맨 뒤에 같은 말이 반복되어 가독성이 조금 떨어진다고 느껴집니다.
그리고
딜러 카드 합 == 참여자 카드 합
인데딜러 카드 수 < 참여자 카드 수
인 경우는 어떻게 되나요?언급이 되어 있지 않아 여쭤봅니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
딜러 카드 합 == 참여자 카드 합 && 딜러 카드 수 < 참여자 카드 수 라면 "패"조건이네요 작성을 놓쳤네요😅