-
Notifications
You must be signed in to change notification settings - Fork 388
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[1단계 - 블랙잭 게임 실행] 우르(김현우) 미션 제출합니다. (#391)
* docs : 기능 명세 작성 * feat : cardValue 추가 * feat : 카드 서브클래스 추가 * docs : 기능 명세 추가 * feat : CardArea 생성 * docs : 기능 명세 추가 * feat: 카드 추가 기능 구현 * feat : 카드 영역을 딜러 카드 영역과 참가자 카드 영역으로 나눔 * feat : 카드 점수 계산 기능 구현 * feat : 카드를 더 받을 수 있는 상태인지 확인하는 기능 추가 * feat : 카드를 더 받을 것인지 결정하는 기능 추가 * refactor : 카드 모양을 enum으로 설정 * feat : 카드 전체 생성 기능 추가 * feat : 카드 뽑기 기능 추가 * feat : 참가자 이름 구현 * feat : 참가자 카드 영역 Hit 여부 구현 * refactor : 패키지 구조 수정 * fix : 카드값 계산 오류 수정 * feat : 버스트 여부 확인 * refactor : Player를 부모로 두고 Dealer와 Participant를 가지게끔 변경 * refactor : CardArea 를 구체 클래스로 변경 - DealerCardArea, ParticipantCardArea 제거 * feat : 블랙잭 게임 완성 * feat : 출력 메세지 변환 * refactor: 게임 통계 로직 변경 * refactor : 첫 번째 카드를 보여주는 행위는 CardArea 가 적합하다고 생각하여 옮김 * refactor : Dealer 최대 점수 상수화 * refactor : enhanced-for 문 flatMap으로 수정 * refactor : state != STAY 를 풀어서 작성 - null 이거나 hit 상태일 경우 hit를 할 수 있음 - 그래서 null 과 hit 상태를 모두 나타내기 위해 연산을 풀어서 작성 - hit 상태를 State에게 직접 물어봄 * refactor : 패키지명 participant -> player 로 변경 * refactor : dealer name 은 고정적으로 딜러다 * feat : DealerResult 추가 - DealerResult는 ParticipantResult 반대 * refactor : OutputView 변경 * refactor : 패키지 변경 - Dealer 관련은 domain.player.dealer - Participant 관련은 domain.player.participant * refactor : CardDeck의 자료구조 List -> Stack 으로 변경 - 테스트 코드에서만 사용하는 메서드 삭제 * style : pr 제출 전 reformatting * fix : y 또는 n 을 입력하지 않을 경우 재입력 로직 추가 * mission : mini mission ArrayList 구현하기 * docs : readme 위치 변경 * refactor : 블랙잭 규칙 중 burst -> bust 변경 * refactor : Player에 이미 점수 계산하는 메서드가 있어서 변경 - Name은 하위 클래스에서 사용하지 않기 때문에 접근제어자 protected -> private 변경 * style : 개행 추가 * refactor : 딜러에서 첫 번째 카드를 보여주는 메서드 추가 - OutputView에서 "딜러의 cardArea에서 첫 번재 카드를 뽑는다"보다는 "딜러는 첫번째 카드를 보여준다" 가 자연스러워 보임 * refactor : State 삭제 * feat : cardArea를 생성하는 CardTable 추가 * feat : 점수를 관리하는 Score 객체 추가 * feat : 점수 관련 로직에서 int 가 아닌 Score 로 변경 * feat : 블랙잭 게임 결과를 CardTable에서 알려준다 * refactor : 블랙잭 게임 결과를 CardTable 로 책임을 위임하면서 코드 변경 * fix : 점수 출력을 위한 getter 메서드 추가 * refactor : 매직넘버 상수로 변경 * refactor : 값 객체의 변수 이름을 value 로 변경 * refactor : 카드 나눠주는 행위를 Controller에서 하지 않고 CardTable로 위임 - 도메인 룰은 Participant 1장 -> Dealer 1장 -> Participant 1 -> Dealer 1장 임 - 현재 코드는 Participant 2장 한번에 주고 -> Dealer 2장 한번에 주기 때문에 도메인 룰 위반 - 카드 나눠주는 행위도 도메인 룰이기 때문에 CardTable에 위임 - CardArea 생성자를 삭제하고 나서의 코드 수정 * refactor : 카드를 나눠줄 수 있으면 true를 반환 * refactor : 카드 나눠주는 행위를 CardTable 에 위임 * style : 제출 전 reformatting * minimission : ArrayList, LinkedList 구현하기 * feat : Score에서 Ace 계산하기 - Ace 가 하나이상 존재하면서 더할 때 버스트가 아닐 경우에는 11을 더해줌 - 그 외는 1로 더하기 * refactor : CardArea 에서 카드 값 계산 로직 수정 * refactor : int 로 비교하지 않고 Score 에 있는 연산 사용 * style : 제출 전 code reformatting --------- Co-authored-by: shin-mallang <[email protected]>
1 parent
b467f6f
commit 10779cd
Showing
34 changed files
with
2,428 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import controller.BlackJackController; | ||
|
||
public class BlackJackApplication { | ||
public static void main(String[] args) { | ||
new BlackJackController().run(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package controller; | ||
|
||
import domain.cardtable.CardTable; | ||
import domain.deck.CardDeck; | ||
import domain.player.Name; | ||
import domain.player.Player; | ||
import domain.player.dealer.Dealer; | ||
import domain.player.dealer.DealerResult; | ||
import domain.player.participant.Participant; | ||
import domain.player.participant.ParticipantResult; | ||
import view.InputView; | ||
import view.OutputView; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
import static java.util.stream.Collectors.counting; | ||
|
||
public class BlackJackController { | ||
|
||
public void run() { | ||
|
||
final CardDeck cardDeck = CardDeck.shuffledFullCardDeck(); | ||
final CardTable cardTable = CardTable.readyToPlayBlackjack(cardDeck); | ||
|
||
final List<Participant> participants = createParticipants(); | ||
final Dealer dealer = createDealer(); | ||
final List<Player> players = createPlayers(participants, dealer); | ||
|
||
deal(cardTable, players); | ||
|
||
printStateAfterDeal(participants, dealer); | ||
hittingPlayer(cardTable, participants, dealer); | ||
printStateAfterHit(participants, dealer); | ||
|
||
final Map<Participant, ParticipantResult> participantsResult = cardTable.determineWinner(participants, dealer); | ||
final Map<DealerResult, Long> scoreBoard = countDealerResult(participantsResult); | ||
|
||
printPlayerScoreBoard(participants, participantsResult, scoreBoard); | ||
} | ||
|
||
private static List<Player> createPlayers(final List<Participant> participants, final Dealer dealer) { | ||
List<Player> players = new ArrayList<>(participants); | ||
players.add(dealer); | ||
|
||
return players; | ||
} | ||
|
||
private static void deal(final CardTable cardTable, final List<Player> players) { | ||
dealCard(cardTable, players); | ||
dealCard(cardTable, players); | ||
} | ||
|
||
private static void dealCard(final CardTable cardTable, final List<Player> players) { | ||
players.forEach(cardTable::dealCardTo); | ||
} | ||
|
||
private static Dealer createDealer() { | ||
return new Dealer(); | ||
} | ||
|
||
private static List<Participant> createParticipants() { | ||
return InputView.readParticipantsName() | ||
.stream() | ||
.map(Name::new) | ||
.map(Participant::new) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
private static void printPlayerScoreBoard(final List<Participant> participants, | ||
final Map<Participant, ParticipantResult> playersResult, | ||
final Map<DealerResult, Long> scoreBoard) { | ||
OutputView.showDealerScoreBoard(scoreBoard); | ||
OutputView.showParticipantsScoreBoard(playersResult, participants); | ||
} | ||
|
||
private static void printStateAfterHit(final List<Participant> participants, final Dealer dealer) { | ||
OutputView.showPlayerStateResult(dealer); | ||
OutputView.showParticipantsStateResult(participants); | ||
} | ||
|
||
private static void printStateAfterDeal(final List<Participant> participants, final Dealer dealer) { | ||
OutputView.showDealtCardTo(participants); | ||
OutputView.showStateOf(dealer); | ||
OutputView.showStateOf(participants); | ||
} | ||
|
||
private static Map<DealerResult, Long> countDealerResult( | ||
final Map<Participant, ParticipantResult> playersResult) { | ||
return playersResult.keySet() | ||
.stream() | ||
.collect(Collectors.groupingBy(participant -> playersResult.get(participant) | ||
.convertToDealerResult(), | ||
counting())); | ||
} | ||
|
||
private void hittingPlayer(final CardTable cardTable, final List<Participant> participants, final Dealer dealer) { | ||
hitForParticipants(cardTable, participants); | ||
hitForDealer(cardTable, dealer); | ||
} | ||
|
||
private void hitForDealer(final CardTable cardTable, final Dealer dealer) { | ||
do { | ||
OutputView.dealerOneMoreCard(); | ||
} while (cardTable.dealCardTo(dealer)); | ||
} | ||
|
||
private void hitForParticipants(final CardTable cardTable, final List<Participant> participants) { | ||
participants.forEach(participant -> hitForParticipant(cardTable, participant)); | ||
} | ||
|
||
private void hitForParticipant(final CardTable cardTable, final Participant participant) { | ||
while (inputHitOrStay(participant)) { | ||
cardTable.dealCardTo(participant); | ||
OutputView.showStateOf(participant); | ||
} | ||
} | ||
|
||
private boolean inputHitOrStay(final Participant participant) { | ||
return InputView.readMoreCard(participant).equals("y"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package domain; | ||
|
||
import java.util.Objects; | ||
|
||
public class Score { | ||
|
||
public static final Score MIN = new Score(0); | ||
|
||
private static final Score UPPER_LIMIT_SCORE = new Score(21); | ||
|
||
private static final Score REMAIN_SCORE_ACE = new Score(10); | ||
|
||
private final int value; | ||
|
||
public Score(final int value) { | ||
this.value = value; | ||
} | ||
|
||
public boolean isBust() { | ||
return this.isGreaterThan(UPPER_LIMIT_SCORE); | ||
} | ||
|
||
public boolean canMoreCard() { | ||
return this.isLessThan(UPPER_LIMIT_SCORE); | ||
} | ||
|
||
public boolean isLessThan(final Score other) { | ||
return value < other.value; | ||
} | ||
|
||
public boolean isLessEqualThan(final Score other) { | ||
return value <= other.value; | ||
} | ||
|
||
public boolean isGreaterThan(final Score other) { | ||
return value > other.value; | ||
} | ||
|
||
public Score plusTenIfNotBurst() { | ||
final Score plusScore = this.plus(REMAIN_SCORE_ACE); | ||
|
||
if (plusScore.isLessEqualThan(UPPER_LIMIT_SCORE)) { | ||
return this.plus(REMAIN_SCORE_ACE); | ||
} | ||
|
||
return this; | ||
} | ||
|
||
public Score plus(Score other) { | ||
return new Score(value + other.value); | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
final Score score = (Score) o; | ||
return value == score.value; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(value); | ||
} | ||
|
||
public int value() { | ||
return value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package domain.area; | ||
|
||
import domain.Score; | ||
import domain.card.Card; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class CardArea { | ||
|
||
private final List<Card> cards = new ArrayList<>(); | ||
|
||
public List<Card> cards() { | ||
return new ArrayList<>(cards); | ||
} | ||
|
||
public void addCard(final Card card) { | ||
cards.add(card); | ||
} | ||
|
||
public Score calculate() { | ||
Score score = score(); | ||
|
||
if (hasAce()) { | ||
score = score.plusTenIfNotBurst(); | ||
} | ||
|
||
return score; | ||
} | ||
|
||
private Score score() { | ||
return cards.stream() | ||
.map(card -> new Score(card.cardValue().value())) | ||
.reduce(Score.MIN, (Score::plus)); | ||
|
||
} | ||
|
||
private boolean hasAce() { | ||
return cards.stream() | ||
.anyMatch(card -> card.cardValue().isAce()); | ||
} | ||
|
||
public boolean canMoreCard() { | ||
return calculate().canMoreCard(); | ||
} | ||
|
||
public boolean isBust() { | ||
return calculate().isBust(); | ||
} | ||
|
||
public Card firstCard() { | ||
return cards.get(0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package domain.card; | ||
|
||
import java.util.Objects; | ||
|
||
public class Card { | ||
|
||
private final CardShape cardShape; | ||
private final CardValue cardValue; | ||
|
||
public Card(final CardShape cardShape, final CardValue cardValue) { | ||
this.cardShape = cardShape; | ||
this.cardValue = cardValue; | ||
} | ||
|
||
public CardShape cardShape() { | ||
return cardShape; | ||
} | ||
|
||
public CardValue cardValue() { | ||
return this.cardValue; | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) return true; | ||
if (!(o instanceof Card)) return false; | ||
final Card card = (Card) o; | ||
return cardShape == card.cardShape && cardValue == card.cardValue; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(cardShape, cardValue); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package domain.card; | ||
|
||
public enum CardShape { | ||
DIAMOND, | ||
CLOVER, | ||
HEART, | ||
SPADE, | ||
; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package domain.card; | ||
|
||
public enum CardValue { | ||
|
||
ACE(1), | ||
|
||
TWO(2), | ||
THREE(3), | ||
FOUR(4), | ||
FIVE(5), | ||
SIX(6), | ||
SEVEN(7), | ||
EIGHT(8), | ||
NINE(9), | ||
TEN(10), | ||
|
||
KING(10), | ||
QUEEN(10), | ||
JACK(10); | ||
|
||
private final int value; | ||
|
||
CardValue(final int value) { | ||
this.value = value; | ||
} | ||
|
||
public int value() { | ||
return value; | ||
} | ||
|
||
public boolean isAce() { | ||
return this == ACE; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package domain.cardtable; | ||
|
||
import domain.deck.CardDeck; | ||
import domain.player.Player; | ||
import domain.player.dealer.Dealer; | ||
import domain.player.participant.Participant; | ||
import domain.player.participant.ParticipantResult; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
public class CardTable { | ||
|
||
private final CardDeck cardDeck; | ||
|
||
private CardTable(final CardDeck cardDeck) { | ||
this.cardDeck = cardDeck; | ||
} | ||
|
||
public static CardTable readyToPlayBlackjack(final CardDeck cardDeck) { | ||
return new CardTable(cardDeck); | ||
} | ||
|
||
public Map<Participant, ParticipantResult> determineWinner(final List<Participant> participants, | ||
final Dealer dealer) { | ||
return participants.stream() | ||
.collect(Collectors.toMap( | ||
Function.identity(), | ||
participant -> matchBetween(participant, dealer)) | ||
); | ||
} | ||
|
||
private ParticipantResult matchBetween(final Participant participant, final Dealer dealer) { | ||
if (participant.isBust()) { | ||
return ParticipantResult.LOSER; | ||
} | ||
if (dealer.isBust()) { | ||
return ParticipantResult.WINNER; | ||
} | ||
if (participant.score().isGreaterThan(dealer.score())) { | ||
return ParticipantResult.WINNER; | ||
} | ||
if (participant.score().equals(dealer.score())) { | ||
return ParticipantResult.DRAWER; | ||
} | ||
return ParticipantResult.LOSER; | ||
} | ||
|
||
public boolean dealCardTo(Player player) { | ||
if (player.canHit()) { | ||
player.hit(cardDeck.draw()); | ||
return true; | ||
} | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package domain.deck; | ||
|
||
import domain.card.Card; | ||
import domain.card.CardShape; | ||
import domain.card.CardValue; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.Stack; | ||
import java.util.stream.Collectors; | ||
|
||
public class CardDeck { | ||
|
||
private final Stack<Card> cards; | ||
|
||
private CardDeck(final Stack<Card> cards) { | ||
this.cards = cards; | ||
} | ||
|
||
public static CardDeck shuffledFullCardDeck() { | ||
|
||
Stack<Card> cards = Arrays.stream(CardShape.values()) | ||
.flatMap(cardShape -> Arrays.stream(CardValue.values()) | ||
.map(cardValue -> new Card(cardShape, cardValue))) | ||
.collect(Collectors.toCollection(Stack::new)); | ||
|
||
Collections.shuffle(cards); | ||
|
||
return new CardDeck(cards); | ||
} | ||
|
||
public Card draw() { | ||
return cards.pop(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package domain.player; | ||
|
||
public class Name { | ||
|
||
private final String value; | ||
|
||
public Name(final String name) { | ||
validateEmpty(name); | ||
this.value = name; | ||
} | ||
|
||
private void validateEmpty(final String name) { | ||
if (name == null || name.isBlank()) { | ||
throw new IllegalArgumentException("이름은 빈칸일 수 없습니다."); | ||
} | ||
} | ||
|
||
public String value() { | ||
return value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package domain.player; | ||
|
||
import domain.Score; | ||
import domain.area.CardArea; | ||
import domain.card.Card; | ||
|
||
public abstract class Player { | ||
|
||
protected final CardArea cardArea; | ||
private final Name name; | ||
|
||
protected Player(final Name name) { | ||
this.name = name; | ||
cardArea = new CardArea(); | ||
} | ||
|
||
public Name name() { | ||
return name; | ||
} | ||
|
||
public CardArea cardArea() { | ||
return cardArea; | ||
} | ||
|
||
public boolean isBust() { | ||
return cardArea.isBust(); | ||
} | ||
|
||
public void hit(final Card card) { | ||
cardArea.addCard(card); | ||
} | ||
|
||
public abstract boolean canHit(); | ||
|
||
public Score score() { | ||
return cardArea.calculate(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package domain.player.dealer; | ||
|
||
import domain.Score; | ||
import domain.card.Card; | ||
import domain.player.Name; | ||
import domain.player.Player; | ||
|
||
public class Dealer extends Player { | ||
|
||
private static final int DEALER_LIMIT_SCORE = 16; | ||
private static final String DEALER_NAME = "딜러"; | ||
|
||
public Dealer() { | ||
super(new Name(DEALER_NAME)); | ||
} | ||
|
||
@Override | ||
public boolean canHit() { | ||
return score().isLessEqualThan(new Score(DEALER_LIMIT_SCORE)); | ||
} | ||
|
||
public Card faceUpFirstCard() { | ||
return cardArea.firstCard(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package domain.player.dealer; | ||
|
||
public enum DealerResult { | ||
|
||
WINNER, | ||
LOSER, | ||
DRAWER; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package domain.player.participant; | ||
|
||
import domain.player.Name; | ||
import domain.player.Player; | ||
|
||
public class Participant extends Player { | ||
|
||
public Participant(final Name name) { | ||
super(name); | ||
} | ||
|
||
@Override | ||
public boolean canHit() { | ||
return cardArea.canMoreCard(); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/main/java/domain/player/participant/ParticipantResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package domain.player.participant; | ||
|
||
import domain.player.dealer.DealerResult; | ||
|
||
public enum ParticipantResult { | ||
WINNER, | ||
LOSER, | ||
DRAWER; | ||
|
||
public DealerResult convertToDealerResult() { | ||
if (this == WINNER) { | ||
return DealerResult.LOSER; | ||
} | ||
if (this == LOSER) { | ||
return DealerResult.WINNER; | ||
} | ||
return DealerResult.DRAWER; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package view; | ||
|
||
import domain.player.participant.Participant; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Scanner; | ||
import java.util.stream.Collectors; | ||
|
||
public class InputView { | ||
|
||
private static final String YES_COMMAND = "y"; | ||
private static final String NO_COMMAND = "n"; | ||
private static final Scanner scanner = new Scanner(System.in); | ||
|
||
private InputView() { | ||
} | ||
|
||
public static List<String> readParticipantsName() { | ||
System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); | ||
final String input = scanner.nextLine(); | ||
|
||
return Arrays.stream(input.split(",")) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
public static String readMoreCard(final Participant participant) { | ||
System.out.println(participant.name().value() + "는 한장의 카드를 더 받으시겠습니다?(예는 y, 아니오는 n)"); | ||
|
||
final String input = scanner.nextLine(); | ||
|
||
if (validateIneligibleCommand(input)) { | ||
return readMoreCard(participant); | ||
} | ||
|
||
return input; | ||
} | ||
|
||
private static boolean validateIneligibleCommand(final String input) { | ||
try { | ||
if (ineligibleCommand(input)) { | ||
throw new IllegalArgumentException(input + " 은 명령어가 아닙니다."); | ||
} | ||
} catch (IllegalArgumentException exception) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
private static boolean ineligibleCommand(final String input) { | ||
return !input.equals(YES_COMMAND) && !input.equals(NO_COMMAND); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package view; | ||
|
||
import domain.card.Card; | ||
import domain.card.CardShape; | ||
import domain.card.CardValue; | ||
import domain.player.Player; | ||
import domain.player.dealer.Dealer; | ||
import domain.player.dealer.DealerResult; | ||
import domain.player.participant.Participant; | ||
import domain.player.participant.ParticipantResult; | ||
|
||
import java.util.EnumMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
public class OutputView { | ||
|
||
private static final Map<CardShape, String> SHAPE_MESSAGE_MAP = new EnumMap<>(CardShape.class); | ||
private static final Map<CardValue, String> VALUE_MESSAGE_MAP = new EnumMap<>(CardValue.class); | ||
private static final Map<ParticipantResult, String> PARTICIPANT_RESULT_MAP = new EnumMap<>(ParticipantResult.class); | ||
private static final Map<DealerResult, String> DEALER_RESULT_MAP = new EnumMap<>(DealerResult.class); | ||
|
||
private static final String DELIM = ", "; | ||
private static final String CARD_INFORMATION_FORMAT = "카드: "; | ||
private static final String DEALER_INFORMATION_FORMAT = "딜러: "; | ||
private static final String EMPTY_DELIM = " "; | ||
private static final String COLON = ": "; | ||
|
||
static { | ||
makeShapeMessage(); | ||
makeValueMessage(); | ||
makeParticipantResultMessage(); | ||
makeDealerResultMessage(); | ||
} | ||
|
||
private OutputView() { | ||
} | ||
|
||
private static void makeShapeMessage() { | ||
SHAPE_MESSAGE_MAP.put(CardShape.DIAMOND, "다이아몬드"); | ||
SHAPE_MESSAGE_MAP.put(CardShape.CLOVER, "클로버"); | ||
SHAPE_MESSAGE_MAP.put(CardShape.HEART, "하트"); | ||
SHAPE_MESSAGE_MAP.put(CardShape.SPADE, "스페이드"); | ||
} | ||
|
||
private static void makeValueMessage() { | ||
VALUE_MESSAGE_MAP.put(CardValue.TWO, "2"); | ||
VALUE_MESSAGE_MAP.put(CardValue.THREE, "3"); | ||
VALUE_MESSAGE_MAP.put(CardValue.FOUR, "4"); | ||
VALUE_MESSAGE_MAP.put(CardValue.FIVE, "5"); | ||
VALUE_MESSAGE_MAP.put(CardValue.SIX, "6"); | ||
VALUE_MESSAGE_MAP.put(CardValue.SEVEN, "7"); | ||
VALUE_MESSAGE_MAP.put(CardValue.EIGHT, "8"); | ||
VALUE_MESSAGE_MAP.put(CardValue.NINE, "9"); | ||
VALUE_MESSAGE_MAP.put(CardValue.TEN, "10"); | ||
VALUE_MESSAGE_MAP.put(CardValue.KING, "킹"); | ||
VALUE_MESSAGE_MAP.put(CardValue.QUEEN, "퀸"); | ||
VALUE_MESSAGE_MAP.put(CardValue.JACK, "잭"); | ||
VALUE_MESSAGE_MAP.put(CardValue.ACE, "A"); | ||
} | ||
|
||
private static void makeParticipantResultMessage() { | ||
PARTICIPANT_RESULT_MAP.put(ParticipantResult.WINNER, "승"); | ||
PARTICIPANT_RESULT_MAP.put(ParticipantResult.LOSER, "패"); | ||
PARTICIPANT_RESULT_MAP.put(ParticipantResult.DRAWER, "무"); | ||
} | ||
|
||
private static void makeDealerResultMessage() { | ||
DEALER_RESULT_MAP.put(DealerResult.WINNER, "승"); | ||
DEALER_RESULT_MAP.put(DealerResult.LOSER, "패"); | ||
DEALER_RESULT_MAP.put(DealerResult.DRAWER, "무"); | ||
} | ||
|
||
public static void showDealtCardTo(final List<? extends Player> participants) { | ||
System.out.println("딜러와 " + printDrawing(participants) + "에게 2장을 나누었습니다"); | ||
} | ||
|
||
private static String printDrawing(final List<? extends Player> participants) { | ||
return participants.stream() | ||
.map(OutputView::getPlayerName) | ||
.collect(Collectors.joining(DELIM)); | ||
} | ||
|
||
public static void showStateOf(final Dealer dealer) { | ||
final Card card = dealer.faceUpFirstCard(); | ||
|
||
System.out.println(getPlayerName(dealer) | ||
+ COLON | ||
+ VALUE_MESSAGE_MAP.get(card.cardValue()) | ||
+ SHAPE_MESSAGE_MAP.get(card.cardShape()) | ||
); | ||
} | ||
|
||
public static void showStateOf(final List<? extends Player> participants) { | ||
participants.forEach(participant -> System.out.println(makeStateMessage(participant))); | ||
} | ||
|
||
public static void showStateOf(final Player player) { | ||
System.out.println(makeStateMessage(player)); | ||
} | ||
|
||
private static String makeStateMessage(final Player player) { | ||
return player.cardArea() | ||
.cards() | ||
.stream() | ||
.map(card -> String.format("%s %s", | ||
VALUE_MESSAGE_MAP.get(card.cardValue()), | ||
SHAPE_MESSAGE_MAP.get(card.cardShape()))) | ||
.collect(Collectors.joining(DELIM, getPlayerName(player) | ||
+ CARD_INFORMATION_FORMAT, "")); | ||
} | ||
|
||
public static void showParticipantsStateResult(final List<? extends Player> participants) { | ||
participants.forEach(OutputView::showPlayerStateResult); | ||
} | ||
|
||
public static void showPlayerStateResult(final Player player) { | ||
final String message = player.cardArea().cards().stream() | ||
.map(card -> String.format("%s %s", | ||
VALUE_MESSAGE_MAP.get(card.cardValue()), | ||
SHAPE_MESSAGE_MAP.get(card.cardShape()))) | ||
.collect(Collectors.joining(DELIM, getPlayerName(player) | ||
+ CARD_INFORMATION_FORMAT, | ||
String.format(" - 결과: %d", | ||
player.cardArea().calculate().value()))); | ||
|
||
System.out.println(message); | ||
} | ||
|
||
private static String getPlayerName(final Player player) { | ||
return player.name().value(); | ||
} | ||
|
||
public static void dealerOneMoreCard() { | ||
System.out.println("딜러는 16 이하라 한장의 카드를 더 받았습니다."); | ||
} | ||
|
||
public static void showDealerScoreBoard(final Map<DealerResult, Long> scoreBoard) { | ||
|
||
System.out.println(scoreBoard.keySet() | ||
.stream() | ||
.map(dealerResult -> scoreBoard.get(dealerResult) | ||
+ DEALER_RESULT_MAP.get(dealerResult)) | ||
.collect(Collectors.joining(EMPTY_DELIM, DEALER_INFORMATION_FORMAT, ""))); | ||
} | ||
|
||
public static void showParticipantsScoreBoard(final Map<Participant, ParticipantResult> scoreBoard, | ||
final List<Participant> participants) { | ||
|
||
participants.stream() | ||
.map(participant -> getPlayerName(participant) | ||
+ COLON | ||
+ PARTICIPANT_RESULT_MAP.get(scoreBoard.get(participant)) | ||
) | ||
.forEach(System.out::println); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package domain; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.CsvSource; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
|
||
import java.util.stream.Stream; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class ScoreTest { | ||
|
||
@ParameterizedTest | ||
@MethodSource("compareScore") | ||
@DisplayName("isLessThan() : 파라미터로 들어온 점수보다 낮으면 true를 반환한다.") | ||
void test_isLessThan(final Score origin, final Score other, final boolean isLess) throws Exception { | ||
//when & then | ||
assertEquals(isLess, origin.isLessThan(other)); | ||
} | ||
|
||
static Stream<Arguments> compareScore() { | ||
//true | ||
final Score origin1 = new Score(51); | ||
final Score other1 = new Score(100); | ||
|
||
//false | ||
final Score origin2 = new Score(100); | ||
final Score other2 = new Score(51); | ||
|
||
//true | ||
final Score origin3 = new Score(1); | ||
final Score other3 = new Score(2); | ||
|
||
//false | ||
final Score origin4 = new Score(1000); | ||
final Score other4 = new Score(1000); | ||
|
||
return Stream.of( | ||
Arguments.of(origin1, other1, true), | ||
Arguments.of(origin2, other2, false), | ||
Arguments.of(origin3, other3, true), | ||
Arguments.of(origin4, other4, false) | ||
); | ||
} | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = { | ||
"22 -> true", | ||
"21 -> false", | ||
"20 -> false", | ||
"1 -> false" | ||
}, delimiterString = " -> ") | ||
@DisplayName("isBust() : 점수가 21 점 초과이면 bust가 된다.") | ||
void test_isBust(final int sum, final boolean isBust) throws Exception { | ||
//when | ||
final Score score = new Score(sum); | ||
|
||
//then | ||
assertEquals(isBust, score.isBust()); | ||
} | ||
|
||
@Test | ||
@DisplayName("canMoreCard() : 점수가 21점 미만아면 카드를 더 받을 수 있다.") | ||
void test_canMoreCard() throws Exception { | ||
// given | ||
final Score score = new Score(20); | ||
|
||
// when & then | ||
assertTrue(score.canMoreCard()); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("compareEqualScore") | ||
@DisplayName("isLessEqualThan() : 파라미터로 들어온 점수보다 작거나 같으면 true를 반환한다.") | ||
void test_isLessEqualThen(final Score origin, final Score other, final boolean isLess) throws Exception { | ||
//when & then | ||
assertEquals(isLess, origin.isLessEqualThan(other)); | ||
} | ||
|
||
static Stream<Arguments> compareEqualScore() { | ||
//true | ||
final Score origin1 = new Score(1); | ||
final Score other1 = new Score(2); | ||
|
||
//false | ||
final Score origin2 = new Score(21); | ||
final Score other2 = new Score(21); | ||
|
||
//true | ||
final Score origin3 = new Score(16); | ||
final Score other3 = new Score(15); | ||
|
||
//false | ||
final Score origin4 = new Score(16); | ||
final Score other4 = new Score(17); | ||
|
||
return Stream.of( | ||
Arguments.of(origin1, other1, true), | ||
Arguments.of(origin2, other2, true), | ||
Arguments.of(origin3, other3, false), | ||
Arguments.of(origin4, other4, true) | ||
); | ||
} | ||
|
||
@Test | ||
@DisplayName("isGreaterThan() : 파라미터로 들어온 점수보다 높으면 true를 반환한다.") | ||
void test_isGreaterThan() throws Exception { | ||
//given | ||
final Score origin1 = new Score(21); | ||
final Score other1 = new Score(21); | ||
|
||
final Score origin2 = new Score(21); | ||
final Score other2 = new Score(20); | ||
|
||
//when & then | ||
assertFalse(origin1.isGreaterThan(other1)); | ||
assertTrue(origin2.isGreaterThan(other2)); | ||
} | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = { | ||
"21,21,42", | ||
"21,20,41" | ||
}) | ||
@DisplayName("plus() : 두 점수를 더할 수 있다.") | ||
void test_add(final int value, final int other, final int sum) throws Exception { | ||
//given | ||
final Score origin = new Score(value); | ||
final Score resultScore = new Score(sum); | ||
|
||
//when & then | ||
assertEquals(origin.plus(new Score(other)), resultScore); | ||
} | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = { | ||
"10,20", | ||
"12,12", | ||
"11,21", | ||
"13,13", | ||
"14,14", | ||
}) | ||
@DisplayName("plusTenIfNotBurst() : 다른 숫자를 더 할 때 bust 당하지 않으면 그 숫자를 더해줄 수 있다.") | ||
void test_plusIfNotBurst(final int value, final int sum) throws Exception { | ||
//given | ||
final Score origin = new Score(value); | ||
final Score result = new Score(sum); | ||
|
||
//when & then | ||
assertEquals(origin.plusTenIfNotBurst(), result); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package domain.area; | ||
|
||
import domain.Score; | ||
import domain.card.Card; | ||
import domain.card.CardShape; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.DisplayNameGeneration; | ||
import org.junit.jupiter.api.DisplayNameGenerator; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.CsvSource; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
|
||
import java.util.stream.Stream; | ||
|
||
import static domain.card.CardValue.ACE; | ||
import static domain.card.CardValue.FOUR; | ||
import static domain.card.CardValue.JACK; | ||
import static domain.card.CardValue.NINE; | ||
import static domain.card.CardValue.SEVEN; | ||
import static domain.card.CardValue.SIX; | ||
import static domain.card.CardValue.TEN; | ||
import static domain.card.CardValue.THREE; | ||
import static domain.card.CardValue.valueOf; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
@SuppressWarnings("NonAsciiCharacters") | ||
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | ||
@DisplayName("CardArea 은") | ||
class CardAreaTest { | ||
|
||
@Test | ||
void 카드를_추가할_수_있다() { | ||
// given | ||
final CardArea cardArea = new CardArea(); | ||
|
||
// when | ||
final int beforeSize = cardArea.cards().size(); | ||
cardArea.addCard(new Card(CardShape.CLOVER, FOUR)); | ||
|
||
// then | ||
assertThat(cardArea.cards().size()).isEqualTo(beforeSize + 1); | ||
} | ||
|
||
@ParameterizedTest(name = "카드 목록이 {0} 일 때, 총합은 {1}다.") | ||
@CsvSource(value = { | ||
"TWO+THREE = 5", | ||
"FIVE+SIX = 11", | ||
"TWO+TWO = 4", | ||
"TEN+TEN = 20", | ||
}, delimiterString = " = ") | ||
void 자신이_가진_카드의_합을_구할_수_있다(final String values, final int totalScore) { | ||
// given | ||
final String[] split = values.split("\\+"); | ||
|
||
final CardArea cardArea = new CardArea(); | ||
|
||
// when | ||
cardArea.addCard(new Card(CardShape.CLOVER, valueOf(split[0]))); | ||
cardArea.addCard(new Card(CardShape.CLOVER, valueOf(split[1]))); | ||
|
||
// then | ||
assertThat(cardArea.calculate()).isEqualTo(new Score(totalScore)); | ||
} | ||
|
||
@ParameterizedTest(name = "킹, 퀸, 잭은 10으로 계산한다") | ||
@CsvSource(value = { | ||
"KING+QUEEN = 20", | ||
"KING+JACK = 20", | ||
"QUEEN+JACK = 20", | ||
"KING+THREE = 13", | ||
"QUEEN+THREE = 13", | ||
"JACK+SIX = 16", | ||
}, delimiterString = " = ") | ||
void 킹_퀸_잭은_10으로_계산한다(final String values, final int totalScore) { | ||
// given | ||
final String[] split = values.split("\\+"); | ||
final CardArea cardArea = new CardArea(); | ||
|
||
cardArea.addCard(new Card(CardShape.CLOVER, valueOf(split[0]))); | ||
cardArea.addCard(new Card(CardShape.CLOVER, valueOf(split[1]))); | ||
|
||
// when & then | ||
assertThat(cardArea.calculate()).isEqualTo(new Score(totalScore)); | ||
} | ||
|
||
@ParameterizedTest(name = "[{index}] ACE 는 이전까지의 총합이 10 이하면 11로 계산한다") | ||
@MethodSource("containsAceCardArea") | ||
void ACE_는_이전까지의_총합이_10_이하면_11로_계산한다(final CardArea cardArea, final int totalScore) { | ||
// then | ||
assertThat(cardArea.calculate()).isEqualTo(new Score(totalScore)); | ||
} | ||
|
||
static Stream<Arguments> containsAceCardArea() { | ||
|
||
// 10 + [11] = 21 | ||
final CardArea cardArea1 = new CardArea(); | ||
cardArea1.addCard(new Card(CardShape.CLOVER, TEN)); | ||
cardArea1.addCard(new Card(CardShape.CLOVER, ACE)); | ||
|
||
// 10 + 10 + [1] = 21 | ||
final CardArea cardArea2 = new CardArea(); | ||
cardArea2.addCard(new Card(CardShape.CLOVER, JACK)); | ||
cardArea2.addCard(new Card(CardShape.CLOVER, TEN)); | ||
|
||
cardArea2.addCard(new Card(CardShape.CLOVER, ACE)); | ||
|
||
// [11] + 9 + [1] = 21 | ||
final CardArea cardArea3 = new CardArea(); | ||
cardArea3.addCard(new Card(CardShape.CLOVER, ACE)); | ||
cardArea3.addCard(new Card(CardShape.CLOVER, NINE)); | ||
|
||
cardArea3.addCard(new Card(CardShape.CLOVER, ACE)); | ||
|
||
// [11] + 6 + 3 = 20 | ||
final CardArea cardArea4 = new CardArea(); | ||
cardArea4.addCard(new Card(CardShape.CLOVER, SIX)); | ||
cardArea4.addCard(new Card(CardShape.CLOVER, THREE)); | ||
|
||
cardArea4.addCard(new Card(CardShape.CLOVER, ACE)); | ||
|
||
// [11] + 10 = 21 | ||
final CardArea cardArea5 = new CardArea(); | ||
cardArea5.addCard(new Card(CardShape.CLOVER, ACE)); | ||
cardArea5.addCard(new Card(CardShape.CLOVER, TEN)); | ||
|
||
// 10 + [1] + 7 = 18 | ||
final CardArea cardArea6 = new CardArea(); | ||
cardArea6.addCard(new Card(CardShape.CLOVER, TEN)); | ||
cardArea6.addCard(new Card(CardShape.CLOVER, ACE)); | ||
|
||
cardArea6.addCard(new Card(CardShape.SPADE, SEVEN)); | ||
|
||
return Stream.of( | ||
Arguments.of(cardArea1, 21), | ||
Arguments.of(cardArea2, 21), | ||
Arguments.of(cardArea3, 21), | ||
Arguments.of(cardArea4, 20), | ||
Arguments.of(cardArea5, 21), | ||
Arguments.of(cardArea6, 18) | ||
); | ||
} | ||
|
||
@Test | ||
void 총합이_20_이하면_카드를_더_받을_수_있는_상태이다() { | ||
// given | ||
final CardArea cardArea = new CardArea(); | ||
cardArea.addCard(new Card(CardShape.CLOVER, TEN)); | ||
cardArea.addCard(new Card(CardShape.CLOVER, TEN)); | ||
|
||
// when & then | ||
assertTrue(cardArea.canMoreCard()); | ||
} | ||
|
||
@Test | ||
void 총합이_21_이상이면_카드를_더_받을_수_없는_상태이다() { | ||
// given | ||
final CardArea cardArea = new CardArea(); | ||
cardArea.addCard(new Card(CardShape.CLOVER, ACE)); | ||
cardArea.addCard(new Card(CardShape.SPADE, TEN)); | ||
|
||
// when & then | ||
assertFalse(cardArea.canMoreCard()); | ||
} | ||
|
||
@Test | ||
void 총합이_21_초과이면_버스트_된다() { | ||
// given | ||
final CardArea cardArea = new CardArea(); | ||
cardArea.addCard(new Card(CardShape.CLOVER, TEN)); | ||
cardArea.addCard(new Card(CardShape.SPADE, TEN)); | ||
|
||
cardArea.addCard(new Card(CardShape.DIAMOND, TEN)); | ||
|
||
// when & then | ||
assertTrue(cardArea.isBust()); | ||
} | ||
|
||
@Test | ||
void 총합이_21_이하이면_버스트_아니다() { | ||
// given | ||
final CardArea cardArea = new CardArea(); | ||
cardArea.addCard(new Card(CardShape.CLOVER, TEN)); | ||
cardArea.addCard(new Card(CardShape.CLOVER, ACE)); | ||
|
||
// when & then | ||
assertFalse(cardArea.isBust()); | ||
} | ||
|
||
@Test | ||
void 딜러는_첫_장만_보여줄_수_있다() { | ||
// given | ||
final CardArea cardArea = new CardArea(); | ||
|
||
cardArea.addCard(new Card(CardShape.CLOVER, TEN)); | ||
cardArea.addCard(new Card(CardShape.CLOVER, SEVEN)); | ||
|
||
// when & then | ||
assertEquals(cardArea.firstCard(), new Card(CardShape.CLOVER, TEN)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package domain.card; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.DisplayNameGeneration; | ||
import org.junit.jupiter.api.DisplayNameGenerator; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
@SuppressWarnings("NonAsciiCharacters") | ||
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | ||
@DisplayName("Card 은") | ||
class CardTest { | ||
|
||
@Test | ||
void 카드_값을_갖는다() { | ||
// given | ||
final Card card = new Card(CardShape.CLOVER, CardValue.TWO); | ||
|
||
// when | ||
CardValue cardValue = card.cardValue(); | ||
|
||
// then | ||
assertThat(cardValue.value()).isEqualTo(2); | ||
} | ||
|
||
@Test | ||
void 카드_종류를_가진다() throws Exception { | ||
// given | ||
final Card card = new Card(CardShape.CLOVER, CardValue.TEN); | ||
|
||
// when & then | ||
assertThat(card.cardShape()).isEqualTo(CardShape.CLOVER); | ||
} | ||
|
||
@Test | ||
void 모양과_값이_같으면_같은_카드이다() { | ||
// given | ||
final Card card1 = new Card(CardShape.CLOVER, CardValue.TWO); | ||
final Card card2 = new Card(CardShape.CLOVER, CardValue.TWO); | ||
|
||
// when & then | ||
assertThat(card1).isEqualTo(card2); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package domain.card; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.DisplayNameGeneration; | ||
import org.junit.jupiter.api.DisplayNameGenerator; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.CsvSource; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
@SuppressWarnings("NonAsciiCharacters") | ||
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | ||
@DisplayName("CardValue 은") | ||
class CardValueTest { | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = { | ||
"TWO -> 2", | ||
"THREE -> 3", | ||
"FOUR -> 4", | ||
"FIVE -> 5", | ||
"SIX -> 6", | ||
"SEVEN -> 7", | ||
"EIGHT -> 8", | ||
"NINE -> 9", | ||
"TEN -> 10", | ||
"KING -> 10", | ||
"QUEEN -> 10", | ||
"JACK -> 10", | ||
"ACE -> 1", | ||
}, delimiterString = " -> ") | ||
void 값을_가진다(final CardValue cardValue, final int value) { | ||
// when & then | ||
assertEquals(cardValue.value(), value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package domain.cardtable; | ||
|
||
import domain.card.Card; | ||
import domain.card.CardShape; | ||
import domain.deck.CardDeck; | ||
import domain.player.Name; | ||
import domain.player.dealer.Dealer; | ||
import domain.player.participant.Participant; | ||
import domain.player.participant.ParticipantResult; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Stream; | ||
|
||
import static domain.card.CardShape.CLOVER; | ||
import static domain.card.CardShape.DIAMOND; | ||
import static domain.card.CardShape.HEART; | ||
import static domain.card.CardShape.SPADE; | ||
import static domain.card.CardValue.NINE; | ||
import static domain.card.CardValue.TEN; | ||
import static domain.player.participant.ParticipantResult.DRAWER; | ||
import static domain.player.participant.ParticipantResult.LOSER; | ||
import static domain.player.participant.ParticipantResult.WINNER; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.api.Assertions.assertAll; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class CardTableTest { | ||
|
||
Participant participant; | ||
|
||
Dealer dealer; | ||
|
||
CardDeck cardDeck; | ||
|
||
CardTable cardTable; | ||
|
||
@BeforeEach | ||
void initCardArea() { | ||
cardDeck = CardDeck.shuffledFullCardDeck(); | ||
cardTable = CardTable.readyToPlayBlackjack(cardDeck); | ||
|
||
participant = new Participant(new Name("name")); | ||
participant.hit(new Card(CLOVER, TEN)); | ||
participant.hit(new Card(CLOVER, NINE)); | ||
|
||
dealer = new Dealer(); | ||
dealer.hit(new Card(DIAMOND, TEN)); | ||
dealer.hit(new Card(DIAMOND, NINE)); | ||
} | ||
|
||
@Test | ||
@DisplayName("matchBetween() : 참여자가 bust 일 경우에는 참여자가 무조건 게임에서 진다.") | ||
void test_matchBetween_bust_participant_must_lose_participant() throws Exception { | ||
//given | ||
participant.hit(new Card(SPADE, TEN)); | ||
|
||
//when | ||
final Map<Participant, ParticipantResult> gameResult = | ||
cardTable.determineWinner(List.of(participant), dealer); | ||
|
||
//then | ||
assertAll( | ||
() -> assertThat(gameResult).hasSize(1), | ||
() -> assertThat(gameResult).containsValue(LOSER) | ||
); | ||
} | ||
|
||
@Test | ||
@DisplayName("matchBetween() : 딜러는 bust 이면서 참여자가 bust 가 아니면 참여자가 무조건 게임에서 이긴다.") | ||
void test_matchBetween_bust_dealer_must_lose_dealer() throws Exception { | ||
//given | ||
dealer.hit(new Card(SPADE, TEN)); | ||
|
||
//when | ||
final Map<Participant, ParticipantResult> gameResult = | ||
cardTable.determineWinner(List.of(participant), dealer); | ||
|
||
//then | ||
assertAll( | ||
() -> assertThat(gameResult).hasSize(1), | ||
() -> assertThat(gameResult).containsValue(WINNER) | ||
); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("makeBothNotBust") | ||
@DisplayName("matchBetween() : 딜러, 참여자 모두 버스트가 아닐 때 점수가 높은 쪽이 이기고, 같으면 무승부이다.") | ||
void test_matchBetween_not_bust_win_higher_score_or_draw_same_score( | ||
final Participant participant, final Dealer dealer, | ||
final ParticipantResult participantResult) throws Exception { | ||
|
||
//when & then | ||
final Map<Participant, ParticipantResult> gameResult = cardTable.determineWinner( | ||
List.of(participant), dealer); | ||
|
||
//then | ||
assertAll( | ||
() -> assertThat(gameResult).hasSize(1), | ||
() -> assertThat(gameResult).containsValue(participantResult) | ||
); | ||
} | ||
|
||
static Stream<Arguments> makeBothNotBust() { | ||
|
||
//무승부 | ||
final Participant participant1 = new Participant(new Name("name1")); | ||
participant1.hit(new Card(CardShape.DIAMOND, TEN)); | ||
participant1.hit(new Card(CardShape.SPADE, TEN)); | ||
|
||
final Dealer dealer1 = new Dealer(); | ||
dealer1.hit(new Card(HEART, TEN)); | ||
dealer1.hit(new Card(CLOVER, TEN)); | ||
|
||
//참여자가 이길 경우 | ||
final Participant participant2 = new Participant(new Name("name2")); | ||
participant2.hit(new Card(CardShape.SPADE, TEN)); | ||
participant2.hit(new Card(CardShape.DIAMOND, TEN)); | ||
|
||
final Dealer dealer2 = new Dealer(); | ||
dealer2.hit(new Card(HEART, TEN)); | ||
dealer2.hit(new Card(CLOVER, NINE)); | ||
|
||
//딜러가 이길 경우 | ||
final Participant participant3 = new Participant(new Name("name3")); | ||
participant3.hit(new Card(CardShape.SPADE, TEN)); | ||
participant3.hit(new Card(CardShape.DIAMOND, NINE)); | ||
|
||
final Dealer dealer3 = new Dealer(); | ||
dealer3.hit(new Card(HEART, TEN)); | ||
dealer3.hit(new Card(CLOVER, TEN)); | ||
|
||
return Stream.of( | ||
Arguments.of(participant1, dealer1, DRAWER), | ||
Arguments.of(participant2, dealer2, WINNER), | ||
Arguments.of(participant3, dealer3, LOSER) | ||
); | ||
} | ||
|
||
@Test | ||
@DisplayName("dealCardTo() : Player에게 카드를 나눠줄 수 있다.") | ||
void test_dealCardTo() throws Exception { | ||
//when & then | ||
assertAll( | ||
() -> assertTrue(cardTable.dealCardTo(participant)), | ||
() -> assertFalse(cardTable.dealCardTo(dealer)), | ||
() -> assertThat(participant.cardArea().cards()).hasSize(3), | ||
() -> assertThat(dealer.cardArea().cards()).hasSize(2) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package domain.deck; | ||
|
||
import domain.card.Card; | ||
import domain.card.CardShape; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.DisplayNameGeneration; | ||
import org.junit.jupiter.api.DisplayNameGenerator; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.ArrayList; | ||
import java.util.EmptyStackException; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static domain.card.CardShape.CLOVER; | ||
import static domain.card.CardShape.DIAMOND; | ||
import static domain.card.CardShape.HEART; | ||
import static domain.card.CardShape.SPADE; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.junit.jupiter.api.Assertions.assertAll; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
@SuppressWarnings("NonAsciiCharacters") | ||
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | ||
@DisplayName("CardDeck 은") | ||
class CardDeckTest { | ||
|
||
@Test | ||
void 카드를_종류별로_값_별로_한장씩_모두_가진_채로_생성한다() { | ||
// given | ||
final CardDeck cardDeck = CardDeck.shuffledFullCardDeck(); | ||
final Map<CardShape, List<Card>> numberOfEachCardShape = new HashMap<>(); | ||
|
||
// when | ||
for (int count = 0; count < 52; count++) { | ||
final Card card = cardDeck.draw(); | ||
final CardShape cardShape = card.cardShape(); | ||
|
||
numberOfEachCardShape.computeIfAbsent(cardShape, key -> new ArrayList<>()); | ||
|
||
numberOfEachCardShape.get(cardShape).add(card); | ||
} | ||
|
||
// then | ||
assertAll( | ||
() -> assertEquals(4, numberOfEachCardShape.size()), | ||
() -> assertEquals(13, numberOfEachCardShape.get(HEART).size()), | ||
() -> assertEquals(13, numberOfEachCardShape.get(CLOVER).size()), | ||
() -> assertEquals(13, numberOfEachCardShape.get(DIAMOND).size()), | ||
() -> assertEquals(13, numberOfEachCardShape.get(SPADE).size()) | ||
); | ||
} | ||
|
||
@Test | ||
void 카드를_한_장씩_꺼낼_수_있다() { | ||
// given | ||
final CardDeck cardDeck = CardDeck.shuffledFullCardDeck(); | ||
int maxDrawCount = 52; | ||
|
||
// when | ||
while (maxDrawCount-- > 0) { | ||
cardDeck.draw(); | ||
} | ||
|
||
// then | ||
assertThatThrownBy(cardDeck::draw).isInstanceOf(EmptyStackException.class); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package domain.player; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.DisplayNameGeneration; | ||
import org.junit.jupiter.api.DisplayNameGenerator; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.NullAndEmptySource; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
@SuppressWarnings("NonAsciiCharacters") | ||
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | ||
@DisplayName("Name 은") | ||
class NameTest { | ||
|
||
@Test | ||
void 문자열을_받아_생성된다() { | ||
// given | ||
final String input = "문자열"; | ||
final Name name = new Name(input); | ||
|
||
// when | ||
final String value = name.value(); | ||
|
||
// then | ||
assertThat(value).isEqualTo(input); | ||
} | ||
|
||
@ParameterizedTest(name = "빈 문자열인 경우 예외가 발생한다") | ||
@NullAndEmptySource | ||
void 빈_문자열인_경우_예외가_발생한다(final String input) { | ||
// when & then | ||
assertThatThrownBy(() -> new Name(input)) | ||
.isInstanceOf(IllegalArgumentException.class); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package domain.player; | ||
|
||
import domain.card.Card; | ||
import domain.card.CardShape; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.DisplayNameGeneration; | ||
import org.junit.jupiter.api.DisplayNameGenerator; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static domain.card.CardValue.TEN; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
|
||
@SuppressWarnings("NonAsciiCharacters") | ||
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | ||
@DisplayName("Player 은") | ||
class PlayerTest { | ||
|
||
final Name name = new Name("name"); | ||
|
||
final Player player = new Player(name) { | ||
@Override | ||
public boolean canHit() { | ||
return false; | ||
} | ||
}; | ||
|
||
@Test | ||
void 추상클래스다() { | ||
//when & then | ||
assertThat(Player.class).isAbstract(); | ||
} | ||
|
||
@Test | ||
void 이름과_area_가진다() { | ||
// when & then | ||
assertDoesNotThrow(() -> new Player(name) { | ||
@Override | ||
public boolean canHit() { | ||
return false; | ||
} | ||
}); | ||
} | ||
|
||
@Test | ||
void 카드를_추가할_수_있다() { | ||
// when | ||
final int beforeSize = player.cardArea().cards().size(); | ||
player.hit(new Card(CardShape.SPADE, TEN)); | ||
|
||
// then | ||
assertThat(player.cardArea.cards()).hasSize(beforeSize + 1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package domain.player.dealer; | ||
|
||
import domain.card.Card; | ||
import domain.card.CardShape; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.DisplayNameGeneration; | ||
import org.junit.jupiter.api.DisplayNameGenerator; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static domain.card.CardValue.SEVEN; | ||
import static domain.card.CardValue.SIX; | ||
import static domain.card.CardValue.TEN; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
@SuppressWarnings("NonAsciiCharacters") | ||
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | ||
@DisplayName("Dealer 은") | ||
class DealerTest { | ||
|
||
@Test | ||
void 딜러는_16이하면_카드를_더_받을_수_있다() { | ||
// given | ||
final Dealer dealer = new Dealer(); | ||
|
||
dealer.hit(new Card(CardShape.CLOVER, SIX)); | ||
dealer.hit(new Card(CardShape.SPADE, TEN)); | ||
|
||
// when & then | ||
assertTrue(dealer.canHit()); | ||
} | ||
|
||
@Test | ||
void 딜러는_16초과면_카드를_더_받을_수_없다() { | ||
// given | ||
final Dealer dealer = new Dealer(); | ||
|
||
dealer.hit(new Card(CardShape.CLOVER, SEVEN)); | ||
dealer.hit(new Card(CardShape.SPADE, TEN)); | ||
|
||
// when & then | ||
assertFalse(dealer.canHit()); | ||
} | ||
|
||
@Test | ||
@DisplayName("faceUpFirstCard() : 딜러는 첫 번째 카드만을 보여줍니다.") | ||
void test_faceUpFirstCard() throws Exception { | ||
//given | ||
final Dealer dealer = new Dealer(); | ||
|
||
dealer.hit(new Card(CardShape.CLOVER, TEN)); | ||
dealer.hit(new Card(CardShape.CLOVER, SEVEN)); | ||
|
||
// when & then | ||
assertEquals(dealer.faceUpFirstCard(), new Card(CardShape.CLOVER, TEN)); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/test/java/domain/player/participant/ParticipantResultTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package domain.player.participant; | ||
|
||
import domain.player.dealer.DealerResult; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.CsvSource; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
@DisplayName("ParticipantResult 의") | ||
class ParticipantResultTest { | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = { | ||
"WINNER -> LOSER", | ||
"DRAWER -> DRAWER", | ||
"LOSER -> WINNER" | ||
}, delimiterString = " -> ") | ||
@DisplayName("convertToDealerResult() : 무승부를 제외하고 딜러의 결과는 참여자의 결과에 대해 반대이다.") | ||
void test_convertToDealerResult_opposite_DealerResult_And_ParticipantResult( | ||
final ParticipantResult participantResult, final DealerResult dealerResult) throws Exception { | ||
|
||
//when & then | ||
assertEquals(participantResult.convertToDealerResult(), dealerResult); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/test/java/domain/player/participant/ParticipantTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package domain.player.participant; | ||
|
||
import domain.card.Card; | ||
import domain.player.Name; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static domain.card.CardShape.CLOVER; | ||
import static domain.card.CardShape.SPADE; | ||
import static domain.card.CardValue.ACE; | ||
import static domain.card.CardValue.TEN; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
@DisplayName("Participant 은") | ||
class ParticipantTest { | ||
|
||
final Participant participant = new Participant(new Name("name")); | ||
|
||
@BeforeEach | ||
void makeCardScoreTwenty() { | ||
participant.hit(new Card(CLOVER, TEN)); | ||
participant.hit(new Card(SPADE, TEN)); | ||
} | ||
|
||
@Test | ||
@DisplayName("canHit() : 참여자는 21점 미만일 경우 카드를 더 받을 수 있다.") | ||
void test_canHit_underScore21() { | ||
// when & then | ||
assertTrue(participant.canHit()); | ||
} | ||
|
||
@Test | ||
@DisplayName("canHit() : 참여자는 21점 이상일 경우 카드를 더 받을 수 없다.") | ||
void test_canHit_overScore21() { | ||
// when | ||
participant.hit(new Card(CLOVER, ACE)); | ||
|
||
// then | ||
assertFalse(participant.canHit()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package minimission; | ||
|
||
import java.util.Arrays; | ||
|
||
public class SimpleArrayList implements SimpleList { | ||
|
||
private static final int DEFAULT_CAPACITY = 10; | ||
|
||
private String[] array; | ||
private int currentSize; | ||
|
||
public SimpleArrayList() { | ||
this.array = new String[DEFAULT_CAPACITY]; | ||
currentSize = 0; | ||
} | ||
|
||
@Override | ||
public boolean add(final String value) { | ||
if (currentSize == array.length) { | ||
grow(); | ||
} | ||
|
||
array[currentSize] = value; | ||
currentSize++; | ||
return true; | ||
} | ||
|
||
@Override | ||
public void add(final int index, final String value) { | ||
if (index < 0 || index > currentSize) { | ||
throw new IndexOutOfBoundsException("여기에는 값을 넣을 수 없습니다."); | ||
} | ||
|
||
if (currentSize == array.length) { | ||
grow(); | ||
} | ||
|
||
System.arraycopy(array, index, array, index + 1, currentSize - index); | ||
|
||
array[index] = value; | ||
currentSize += 1; | ||
} | ||
|
||
private void grow() { | ||
int limitCapacity = currentSize + currentSize / 2; | ||
array = Arrays.copyOf(array, limitCapacity); | ||
} | ||
|
||
@Override | ||
public String set(final int index, final String value) { | ||
if (index < 0 || index > currentSize) { | ||
throw new IndexOutOfBoundsException("여기에는 값을 넣을 수 없습니다."); | ||
} | ||
|
||
array[index] = value; | ||
|
||
return value; | ||
} | ||
|
||
@Override | ||
public String get(final int index) { | ||
if (index < 0 || index > currentSize) { | ||
throw new IndexOutOfBoundsException("여기에는 값을 넣을 수 없습니다."); | ||
} | ||
|
||
return array[index]; | ||
} | ||
|
||
@Override | ||
public boolean contains(final String value) { | ||
return indexOf(value) != -1; | ||
} | ||
|
||
@Override | ||
public int indexOf(final String value) { | ||
for (int index = 0; index < array.length; index++) { | ||
if (value.equals(array[index])) { | ||
return index; | ||
} | ||
} | ||
|
||
return -1; | ||
} | ||
|
||
@Override | ||
public int size() { | ||
return currentSize; | ||
} | ||
|
||
@Override | ||
public boolean isEmpty() { | ||
return currentSize == 0; | ||
} | ||
|
||
@Override | ||
public boolean remove(final String value) { | ||
final int index = indexOf(value); | ||
|
||
if (index == -1) { | ||
return false; | ||
} | ||
|
||
remove(index); | ||
currentSize -= 1; | ||
|
||
return true; | ||
} | ||
|
||
@Override | ||
public String remove(final int index) { | ||
if (index < 0 || index > currentSize) { | ||
throw new IndexOutOfBoundsException("여기에는 값을 넣을 수 없습니다."); | ||
} | ||
|
||
final String oldValue = array[index]; | ||
|
||
if (currentSize - 1 > index) { | ||
System.arraycopy(array, index + 1, array, index, currentSize - index); | ||
} | ||
|
||
array[currentSize] = null; | ||
currentSize -= 1; | ||
|
||
return oldValue; | ||
} | ||
|
||
@Override | ||
public void clear() { | ||
Arrays.fill(array, null); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
package minimission; | ||
|
||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.CsvSource; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
|
||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.junit.jupiter.api.Assertions.assertAll; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertNull; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class SimpleArrayListTest { | ||
|
||
SimpleList simpleArrayList; | ||
|
||
@BeforeEach | ||
void init() { | ||
simpleArrayList = new SimpleArrayList(); | ||
} | ||
|
||
@Test | ||
@DisplayName("add() : arrayList의 사이즈가 충분할 때, 값을 추가할 수 있다.") | ||
void test_add_enough_size() throws Exception { | ||
//given | ||
SimpleArrayList simpleArrayList = new SimpleArrayList(); | ||
|
||
//when & then | ||
simpleArrayList.add("a"); | ||
assertEquals(1, simpleArrayList.size()); | ||
|
||
simpleArrayList.add("b"); | ||
assertEquals(2, simpleArrayList.size()); | ||
} | ||
|
||
@Test | ||
@DisplayName("add(value) : arrayList가 가득 찼을 때, 자동으로 현재 크기의 절반을 늘린다음에 값을 넣을 수 있다.") | ||
void test_add_full() throws Exception { | ||
//given | ||
SimpleArrayList simpleArrayList = new SimpleArrayList(); | ||
|
||
//when & then | ||
for (int i = 0; i < 10; i++) { | ||
simpleArrayList.add("a"); | ||
} | ||
|
||
simpleArrayList.add("b"); | ||
assertEquals(11, simpleArrayList.size()); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(ints = {-1, 11}) | ||
@DisplayName("add(index, value) : index의 크기가 0보다 작거나, 현재 크기보다 클 경우에는 IndexOutOfBoundsException 발생한다.") | ||
void test_add_specific_index_indexOutOfBoundsException(int index) throws Exception { | ||
//given | ||
SimpleList simpleArrayList = new SimpleArrayList(); | ||
|
||
//when & then | ||
assertThatThrownBy(() -> simpleArrayList.add(index, "a")) | ||
.isInstanceOf(IndexOutOfBoundsException.class); | ||
} | ||
|
||
@Test | ||
@DisplayName("add(index, value) : 원하는 index 에 값을 넣을 수 있다. 뒷 값들은 한 칸씩 밀려나간다.") | ||
void test_add_specific_index() throws Exception { | ||
// given | ||
for (int i = 0; i < 10; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
// when | ||
simpleArrayList.add(5, "k"); | ||
|
||
|
||
// then | ||
assertEquals("k", simpleArrayList.get(5)); | ||
} | ||
|
||
@Test | ||
@DisplayName("set() : 원하는 index에 값을 변경할 수 있다.") | ||
void test_set() throws Exception { | ||
//given | ||
for (int i = 0; i < 10; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//when | ||
simpleArrayList.set(4, "k"); | ||
|
||
//then | ||
assertEquals("k", simpleArrayList.get(4)); | ||
} | ||
|
||
@Test | ||
@DisplayName("get() : 원하는 index 의 값을 가져올 수 있다.") | ||
void get_element_using_index() throws Exception { | ||
//when | ||
for (int i = 0; i < 10; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//then | ||
assertAll( | ||
() -> assertEquals("a", simpleArrayList.get(0)), | ||
() -> assertEquals("b", simpleArrayList.get(1)), | ||
() -> assertEquals("c", simpleArrayList.get(2)), | ||
() -> assertEquals("d", simpleArrayList.get(3)), | ||
() -> assertEquals("e", simpleArrayList.get(4)), | ||
() -> assertEquals("f", simpleArrayList.get(5)), | ||
() -> assertEquals("g", simpleArrayList.get(6)), | ||
() -> assertEquals("h", simpleArrayList.get(7)), | ||
() -> assertEquals("i", simpleArrayList.get(8)), | ||
() -> assertEquals("j", simpleArrayList.get(9)) | ||
); | ||
} | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = { | ||
"z -> false", | ||
"a -> true", | ||
"b -> true", | ||
"c -> true", | ||
"d -> true", | ||
"m -> false" | ||
}, delimiterString = " -> ") | ||
@DisplayName("contains() : 배열에 해당 값이 있다면 true를 반환한다") | ||
void test_contains(final String value, final boolean isContains) throws Exception { | ||
//given | ||
for (int i = 0; i < 10; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//when & then | ||
assertEquals(isContains, simpleArrayList.contains(value)); | ||
} | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = { | ||
"z -> -1", | ||
"a -> 0", | ||
"b -> 1", | ||
"c -> 2", | ||
"d -> 3" | ||
}, delimiterString = " -> ") | ||
@DisplayName("indexOf() : 배열에 해당 값이 없다면 -1 을 반환하고, 있으면 해당 index 를 반환한다") | ||
void test_indexOf_notIn(final String value, final int index) throws Exception { | ||
//given | ||
for (int i = 0; i < 10; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//when & then | ||
assertEquals(index, simpleArrayList.indexOf(value)); | ||
} | ||
|
||
@Test | ||
@DisplayName("size() : 현재 배열에 있는 값들의 개수를 구할 수 있다.") | ||
void test_size() throws Exception { | ||
//given | ||
assertEquals(0, simpleArrayList.size()); | ||
|
||
for (int i = 0; i < 10; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//when & then | ||
assertEquals(10, simpleArrayList.size()); | ||
} | ||
|
||
@Test | ||
@DisplayName("empty() : 배열에 element 가 하나도 없으면 true를 반환한다.") | ||
void test_empty() throws Exception { | ||
//given | ||
assertTrue(simpleArrayList.isEmpty()); | ||
|
||
for (int i = 0; i < 10; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//when & then | ||
assertFalse(simpleArrayList.isEmpty()); | ||
} | ||
|
||
@Test | ||
@DisplayName("remove(index) : 특정 index의 값을 삭제하고 뒤에 있는 값들을 당겨온다.") | ||
void test_remove_specific_index() throws Exception { | ||
//given | ||
for (int i = 0; i < 13; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//when | ||
final String oldValue = simpleArrayList.remove(11); | ||
|
||
//then | ||
assertEquals("l", oldValue); | ||
assertEquals("m", simpleArrayList.get(11)); | ||
} | ||
|
||
@Test | ||
@DisplayName("remove(value) : 특정 값을 삭제하고 뒤에 있는 값들을 당겨온다.") | ||
void test_remove_specific_value() throws Exception { | ||
//given | ||
for (int i = 0; i < 13; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//when | ||
final boolean removeFlag = simpleArrayList.remove("l"); | ||
|
||
//then | ||
assertTrue(removeFlag); | ||
assertEquals("m", simpleArrayList.get(11)); | ||
} | ||
|
||
@Test | ||
@DisplayName("clear() : array의 값을 모두 비운다.") | ||
void test_clear() throws Exception { | ||
//given | ||
for (int i = 0; i < 13; i++) { | ||
simpleArrayList.add(String.valueOf((char) (i + 'a'))); | ||
} | ||
|
||
//when | ||
simpleArrayList.clear(); | ||
|
||
//then | ||
for (int i = 0; i < simpleArrayList.size(); i++) { | ||
assertNull(simpleArrayList.get(i)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package minimission; | ||
|
||
public class SimpleLinkedList implements SimpleList { | ||
|
||
private Node head; | ||
|
||
private int size; | ||
|
||
public SimpleLinkedList() { | ||
} | ||
|
||
@Override | ||
public boolean add(final String value) { | ||
|
||
final Node newNode = new Node(null, value); | ||
|
||
if (head == null) { | ||
head = newNode; | ||
} else { | ||
Node curNode = head; | ||
|
||
while (curNode.next != null) { | ||
curNode = curNode.next; | ||
} | ||
|
||
curNode.next = newNode; | ||
} | ||
|
||
size++; | ||
return true; | ||
} | ||
|
||
@Override | ||
public void add(final int index, final String value) { | ||
if (index < 0 || index > size) { | ||
throw new IndexOutOfBoundsException("여기에 넣을 수 없습니다."); | ||
} | ||
|
||
final Node newNode = new Node(null, value); | ||
|
||
if (index == 0) { | ||
newNode.next = head; | ||
head = newNode; | ||
} else { | ||
Node current = head; | ||
for (int i = 0; i < index - 1 && current != null; i++) { | ||
current = current.next; | ||
} | ||
if (current != null) { | ||
newNode.next = current.next; | ||
current.next = newNode; | ||
} | ||
} | ||
|
||
size++; | ||
} | ||
|
||
@Override | ||
public String set(final int index, final String value) { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String get(final int index) { | ||
if (index < 0 || index > size) { | ||
throw new IndexOutOfBoundsException("여기에 넣을 수 없습니다."); | ||
} | ||
|
||
Node node = head; | ||
int start = index; | ||
|
||
while (start-- > 0) { | ||
node = node.next; | ||
} | ||
|
||
return node.value; | ||
} | ||
|
||
@Override | ||
public boolean contains(final String value) { | ||
return indexOf(value) >= 0; | ||
} | ||
|
||
@Override | ||
public int indexOf(final String value) { | ||
int index = 0; | ||
|
||
for (Node start = head; start != null; start = start.next) { | ||
if (start.value.equals(value)) { | ||
return index; | ||
} | ||
|
||
index++; | ||
} | ||
|
||
return -1; | ||
} | ||
|
||
@Override | ||
public int size() { | ||
return size; | ||
} | ||
|
||
@Override | ||
public boolean isEmpty() { | ||
return head == null; | ||
} | ||
|
||
@Override | ||
public boolean remove(final String value) { | ||
|
||
final int index = indexOf(value); | ||
if (index == -1) { | ||
return false; | ||
} | ||
|
||
remove(index); | ||
return true; | ||
} | ||
|
||
@Override | ||
public String remove(final int index) { | ||
|
||
final Node targetNode = node(index); | ||
final Node next = targetNode.next; | ||
final Node prev = node(index - 1); | ||
|
||
final String removedValue = targetNode.value; | ||
|
||
if (next == null) { | ||
prev.next = null; | ||
} else { | ||
prev.next = next; | ||
targetNode.next = null; | ||
targetNode.value = null; | ||
} | ||
|
||
size--; | ||
return removedValue; | ||
} | ||
|
||
void print() { | ||
for (Node current = head; current != null; current = current.next) { | ||
System.out.println(current.value + " "); | ||
} | ||
} | ||
|
||
Node node(int index) { | ||
Node node = head; | ||
|
||
for (int i = 0; i < index; i++) { | ||
node = node.next; | ||
} | ||
|
||
return node; | ||
} | ||
|
||
@Override | ||
public void clear() { | ||
|
||
for (Node current = head; current != null;) { | ||
Node next = current.next; | ||
current.value = null; | ||
current.next = null; | ||
|
||
current = next; | ||
} | ||
|
||
size = 0; | ||
head = null; | ||
} | ||
|
||
static class Node { | ||
|
||
Node next; | ||
String value; | ||
|
||
public Node(final Node next, final String value) { | ||
this.next = next; | ||
this.value = value; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package minimission; | ||
|
||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertAll; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class SimpleLinkedListTest { | ||
|
||
SimpleList simpleLinkedList; | ||
|
||
@BeforeEach | ||
void init() { | ||
simpleLinkedList = new SimpleLinkedList(); | ||
} | ||
|
||
|
||
@Test | ||
@DisplayName("add(value) : 링크드리스트의 끝에 값을 넣을 수 있다.") | ||
void add_last() throws Exception { | ||
//when | ||
simpleLinkedList.add("a"); | ||
simpleLinkedList.add("b"); | ||
simpleLinkedList.add("c"); | ||
|
||
//then | ||
assertAll( | ||
() -> assertEquals(3, simpleLinkedList.size()), | ||
() -> assertEquals("a", simpleLinkedList.get(0)), | ||
() -> assertEquals("b", simpleLinkedList.get(1)), | ||
() -> assertEquals("c", simpleLinkedList.get(2)) | ||
); | ||
} | ||
|
||
@Test | ||
@DisplayName("add(index, value) : 링크드리스트의 원하는 곳에 값을 넣을 수 있다.") | ||
void add_specific_index() throws Exception { | ||
//given | ||
simpleLinkedList.add("a"); | ||
simpleLinkedList.add("b"); | ||
simpleLinkedList.add("c"); | ||
|
||
//when a d b e c | ||
simpleLinkedList.add(1, "d"); | ||
simpleLinkedList.add(3, "e"); | ||
|
||
//then | ||
assertAll( | ||
() -> assertEquals(5, simpleLinkedList.size()), | ||
() -> assertEquals("a", simpleLinkedList.get(0)), | ||
() -> assertEquals("d", simpleLinkedList.get(1)), | ||
() -> assertEquals("b", simpleLinkedList.get(2)), | ||
() -> assertEquals("e", simpleLinkedList.get(3)), | ||
() -> assertEquals("c", simpleLinkedList.get(4)) | ||
); | ||
} | ||
|
||
@Test | ||
@DisplayName("contains() : 특정 값이 포함되어있는지 알 수 있다.") | ||
void test_contains() throws Exception { | ||
//given | ||
simpleLinkedList.add("1"); | ||
simpleLinkedList.add("2"); | ||
simpleLinkedList.add("3"); | ||
|
||
//when | ||
final boolean isContains = simpleLinkedList.contains("3"); | ||
final boolean isNotContains = simpleLinkedList.contains("4"); | ||
|
||
//then | ||
assertTrue(isContains); | ||
assertFalse(isNotContains); | ||
} | ||
|
||
@Test | ||
@DisplayName("indexOf() : 특정 값의 index를 알 수 있다.") | ||
void test_indexOf() throws Exception { | ||
//given | ||
simpleLinkedList.add("1"); | ||
simpleLinkedList.add("2"); | ||
simpleLinkedList.add("3"); | ||
|
||
//when | ||
final int index = simpleLinkedList.indexOf("2"); | ||
|
||
//then | ||
assertEquals(index, 1); | ||
} | ||
|
||
@Test | ||
@DisplayName("isEmpty() : 링크드리스트가 비어있는지 확인할 수 있다.") | ||
void test_isEmpty() throws Exception { | ||
//given | ||
final boolean empty = simpleLinkedList.isEmpty(); | ||
|
||
assertTrue(empty); | ||
|
||
//when | ||
simpleLinkedList.add("1"); | ||
simpleLinkedList.add("2"); | ||
simpleLinkedList.add("3"); | ||
|
||
final boolean notEmpty = simpleLinkedList.isEmpty(); | ||
|
||
//then | ||
assertFalse(notEmpty); | ||
} | ||
|
||
@Test | ||
@DisplayName("remove(value) : 특정 값을 삭제할 수 있다.") | ||
void test_remove_value() throws Exception { | ||
//given | ||
simpleLinkedList.add("1"); | ||
simpleLinkedList.add("2"); | ||
simpleLinkedList.add("3"); | ||
|
||
//when | ||
simpleLinkedList.remove("2"); | ||
|
||
//then | ||
assertEquals(simpleLinkedList.size(), 2); | ||
} | ||
|
||
@Test | ||
@DisplayName("remove(index) : 특정 index의 값을 삭제할 수 있다.") | ||
void test_remove_index() throws Exception { | ||
//given | ||
simpleLinkedList.add("1"); | ||
simpleLinkedList.add("2"); | ||
simpleLinkedList.add("3"); | ||
|
||
//when | ||
final String oldValue = simpleLinkedList.remove(2); | ||
|
||
//then | ||
assertEquals(oldValue, "3"); | ||
assertEquals(simpleLinkedList.size(), 2); | ||
} | ||
|
||
@Test | ||
@DisplayName("clear() : LinkedList를 비울 수 있다.") | ||
void test_clear() throws Exception { | ||
//given | ||
simpleLinkedList.add("1"); | ||
simpleLinkedList.add("2"); | ||
simpleLinkedList.add("3"); | ||
|
||
//when | ||
simpleLinkedList.clear(); | ||
|
||
//then | ||
assertEquals(simpleLinkedList.size(), 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package minimission; | ||
|
||
public interface SimpleList { | ||
|
||
boolean add(String value); | ||
|
||
void add(int index, String value); | ||
|
||
String set(int index, String value); | ||
|
||
String get(int index); | ||
|
||
boolean contains(String value); | ||
|
||
int indexOf(String value); | ||
|
||
int size(); | ||
|
||
boolean isEmpty(); | ||
|
||
boolean remove(String value); | ||
|
||
String remove(int index); | ||
|
||
void clear(); | ||
} |