diff --git a/README.md b/README.md index 03ba7ed3..679fbe1c 100644 --- a/README.md +++ b/README.md @@ -1 +1,50 @@ -# java-blackjack \ No newline at end of file +# java-blackjack + +## 기능 + +### Car +- [X] 이동거리로 주입해야할 연료량을 계산한다. +- [X] 차종을 리턴한다. +- [X] 연료량을 리턴한다. + +### RentCompany +- [X] InputView를 생성한다. +- [X] InputView로 부터 받은 이동거리로 Car를 생성한다. +- [X] ReportView를 생성한다. +- [X] Car로부터 받은 연료량으로 ReportView를 생성한다. + +### InputView +- [X] 이동거리 입력 받는다. + +### ReportView +- [X] 차량별 주입해야할 연료량을 출력한다. + + +## 블랙잭 기능 요구사항 + +### 입력 +- [x] 게임에 참여할 사람의 이름을 입력한다. + - [x] 쉼표 기준으로 분리한다 + +### 출력 +- [ ] + +### 카드 +- [x] Ace는 1 또는 11로 계산한다. +- [x] King, Queen, Jack은 10으로 계산한다. +- [x] 나머지 카드는 카드숫자를 기본으로 계산한다. + +### 게임 +- [x] 게임시작 시 2장의 카드를 지급 받는다. +- [x] 21을 초과하지 않으면 카드를 계속 받을 수 있다. +- [x] 21을 초과하면 패배로 처리한다. +- [x] 21을 초과하지 않으면서 21에 가까운 사람이 이긴다. +#### 딜러 +- [x] 처음에 받은 2장의 합계가 16이하면 1장을 추가로 지급받는다. + +# TODO +- [x] 플레이어를 추상클래스 혹은 인터페이스로 구현하기 +- [x] 딜러가 한장 더 받는다는 메시지를 맨 마지막에 위치 +- [x] 딜러의 상대적 최종 승패 구현 +- [x] 단위 테스트 보완하기 +- [x] 최종승패 딜러 로직 버그 수정 \ No newline at end of file diff --git a/src/main/java/blackjack/Application.java b/src/main/java/blackjack/Application.java new file mode 100644 index 00000000..97abce7f --- /dev/null +++ b/src/main/java/blackjack/Application.java @@ -0,0 +1,11 @@ +package blackjack; + +import blackjack.controller.GameController; + +public class Application { + + public static void main(String[] args) { + GameController gameController = new GameController(); + gameController.start(); + } +} diff --git a/src/main/java/blackjack/controller/GameController.java b/src/main/java/blackjack/controller/GameController.java new file mode 100644 index 00000000..758952fc --- /dev/null +++ b/src/main/java/blackjack/controller/GameController.java @@ -0,0 +1,98 @@ +package blackjack.controller; + +import blackjack.domain.card.CardPack; +import blackjack.domain.gameplayer.GamePlayer; +import blackjack.domain.gameplayer.GamePlayers; +import blackjack.domain.gameplayer.Names; +import blackjack.view.InputView; +import blackjack.view.OutputView; +import java.util.List; + +public class GameController { + + public void start() { + final CardPack cardPack = new CardPack(); + cardPack.shuffle(); + final GamePlayers gamePlayers = new GamePlayers(getConsoleNames()); + + initializeGame(gamePlayers, cardPack); + printInitialStatus(gamePlayers); + + playGame(gamePlayers, cardPack); + printFinalStatus(gamePlayers); + } + + private Names getConsoleNames() { + try { + return convertStringToNames(InputView.getPlayerName()); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + return getConsoleNames(); + } + } + + private Names convertStringToNames(String names) { + return new Names(names); + } + + public void initializeGame(final GamePlayers gamePlayers, final CardPack cardPack) { + List players = gamePlayers.getAllPlayers(); + + players.forEach(player -> { + player.receiveCard(cardPack.pick()); + player.receiveCard(cardPack.pick()); + }); + } + + private void printInitialStatus(GamePlayers gamePlayers) { + printInitialMessage(gamePlayers); + printCardAllStatus(gamePlayers); + } + + private void printInitialMessage(GamePlayers gamePlayers) { + OutputView.printInitialMessage(gamePlayers); + } + + private void printCardAllStatus(GamePlayers gamePlayers) { + OutputView.printCardAllStatus(gamePlayers); + } + + public void playGame(GamePlayers gamePlayers, CardPack cardPack) { + final List players = gamePlayers.getPlayers(); + final GamePlayer dealer = gamePlayers.getDealer(); + + for (final GamePlayer player : players) { + playerGameProcess(player, cardPack); + } + dealerGameProcess(dealer, cardPack); + } + + private void playerGameProcess(final GamePlayer player, CardPack cardPack) { + while (player.isContinue() && InputView.getPlayerChoice(player)) { + player.receiveCard(cardPack.pick()); + OutputView.printCardStatus(player); + } + + OutputView.printCardStatus(player); + } + + private void dealerGameProcess(final GamePlayer dealer, CardPack cardPack) { + while (dealer.isLowerThanBound()) { + dealer.receiveCard(cardPack.pick()); + OutputView.printDealerAcceptCard(); + } + } + + private void printFinalStatus(GamePlayers gamePlayers) { + printCardResult(gamePlayers); + printGameResult(gamePlayers); + } + + private void printCardResult(GamePlayers gamePlayers) { + OutputView.printCardResult(gamePlayers); + } + + private void printGameResult(GamePlayers gamePlayers) { + OutputView.printGameResult(gamePlayers); + } +} diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java new file mode 100644 index 00000000..199e090d --- /dev/null +++ b/src/main/java/blackjack/domain/card/Card.java @@ -0,0 +1,39 @@ +package blackjack.domain.card; + +import java.util.Objects; + +public class Card { + + private final CardSymbol cardSymbol; + private final CardType cardType; + + public Card(final CardSymbol cardSymbol, final CardType cardType) { + this.cardSymbol = cardSymbol; + this.cardType = cardType; + } + + public CardSymbol getCardSymbol() { + return cardSymbol; + } + + public CardType getCardType() { + return cardType; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Card card = (Card) o; + return cardSymbol == card.cardSymbol && cardType == card.cardType; + } + + @Override + public int hashCode() { + return Objects.hash(cardSymbol, cardType); + } +} diff --git a/src/main/java/blackjack/domain/card/CardPack.java b/src/main/java/blackjack/domain/card/CardPack.java new file mode 100644 index 00000000..81bb2bc6 --- /dev/null +++ b/src/main/java/blackjack/domain/card/CardPack.java @@ -0,0 +1,40 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class CardPack { + + private static final int FRONT = 0; + private static final List cardPack = create(); + + private List cards; + + public CardPack() { + this.cards = new ArrayList<>(cardPack); + } + + private static List create() { + final List cards = new ArrayList<>(); + for (final CardSymbol symbol : CardSymbol.values()) { + Arrays.stream(CardType.values()) + .forEach(type -> cards.add(new Card(symbol, type))); + } + return new ArrayList<>(cards); + } + + public void shuffle() { + Collections.shuffle(cards); + this.cards = new ArrayList<>(cards); + } + + public List getCards() { + return new ArrayList<>(cards); + } + + public Card pick() { + return cards.remove(FRONT); + } +} diff --git a/src/main/java/blackjack/domain/card/CardSymbol.java b/src/main/java/blackjack/domain/card/CardSymbol.java new file mode 100644 index 00000000..225eb683 --- /dev/null +++ b/src/main/java/blackjack/domain/card/CardSymbol.java @@ -0,0 +1,18 @@ +package blackjack.domain.card; + +public enum CardSymbol { + SPADE("스페이드"), + DIAMOND("다이아몬드"), + CLOVER("클로버"), + HEART("하트"); + + private final String symbol; + + CardSymbol(final String symbol) { + this.symbol = symbol; + } + + public String getSymbol() { + return this.symbol; + } +} diff --git a/src/main/java/blackjack/domain/card/CardType.java b/src/main/java/blackjack/domain/card/CardType.java new file mode 100644 index 00000000..832ac510 --- /dev/null +++ b/src/main/java/blackjack/domain/card/CardType.java @@ -0,0 +1,48 @@ +package blackjack.domain.card; + +public enum CardType { + ACE("A", 11, 1), + TWO("2", 2), + THREE("3", 3), + FOUR("4", 4), + FIVE("5", 5), + SIX("6", 6), + SEVEN("7", 7), + EIGHT("8", 8), + NINE("9", 9), + TEN("10", 10), + JACK("J", 10), + QUEEN("Q", 10), + KING("K", 10); + + private final String name; + private final int point; + private int lowerAcePoint; + + CardType(String name, int point) { + this.name = name; + this.point = point; + } + + CardType(String name, int point, int lowerAcePoint) { + this.name = name; + this.point = point; + this.lowerAcePoint = lowerAcePoint; + } + + public String getName() { + return this.name; + } + + public int getPoint() { + return this.point; + } + + public int getLowerAcePoint() { + return this.lowerAcePoint; + } + + public boolean isAce() { + return this == CardType.ACE; + } +} diff --git a/src/main/java/blackjack/domain/card/Cards.java b/src/main/java/blackjack/domain/card/Cards.java new file mode 100644 index 00000000..992d3cde --- /dev/null +++ b/src/main/java/blackjack/domain/card/Cards.java @@ -0,0 +1,61 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Cards { + + private static final int BLACK_JACK = 21; + + private final List cards; + + public Cards() { + this.cards = new ArrayList<>(); + } + + public void receiveCard(final Card card) { + cards.add(card); + } + + public List toList() { + return Collections.unmodifiableList(cards); + } + + public int calculateCards() { + final int sumOfCards = getSumOfCards(); + final int aceCount = getAceCount(); + + if (sumOfCards > BLACK_JACK && aceCount > 0) { + return getBestSumWithAce(sumOfCards); + } + return sumOfCards; + } + + private int getAceCount() { + return (int) cards.stream() + .filter(card -> card.getCardType().isAce()) + .count(); + } + + private int getSumOfCards() { + return cards.stream() + .map(Card::getCardType) + .mapToInt(CardType::getPoint) + .sum(); + } + + private int getBestSumWithAce(final int sum) { + final int lowerAcePoint = CardType.ACE.getLowerAcePoint(); + final int higherAcePoint = CardType.ACE.getPoint(); + + int aceCount = getAceCount(); + int totalPoint = sum; + + while (aceCount > 0 && totalPoint > BLACK_JACK) { + totalPoint = totalPoint - higherAcePoint + lowerAcePoint; + aceCount--; + } + return totalPoint; + } +} diff --git a/src/main/java/blackjack/domain/gameplayer/Dealer.java b/src/main/java/blackjack/domain/gameplayer/Dealer.java new file mode 100644 index 00000000..fe0ad5e2 --- /dev/null +++ b/src/main/java/blackjack/domain/gameplayer/Dealer.java @@ -0,0 +1,40 @@ +package blackjack.domain.gameplayer; + +import java.util.List; + +public class Dealer extends GamePlayer { + + private static final String DEALER = "딜러"; + private static final int DEALER_BOUND = 16; + private static final String DealerResult = "%d승 %d패"; + + public Dealer() { + this(new Name(DEALER)); + } + + public Dealer(final Name name) { + super(name); + } + + @Override + public boolean isLowerThanBound() { + return getScore() <= DEALER_BOUND; + } + + @Override + public String getGameResult(List players) { + int winCount = 0; + if (isContinue()) { + winCount = findWinCount(getScore(), players); + } + int loseCount = players.size() - winCount; + + return String.format(DealerResult, winCount, loseCount); + } + + private int findWinCount(int dealerScore, List players) { + return (int) players.stream() + .filter(player -> !player.isContinue() || player.getScore() < dealerScore) + .count(); + } +} diff --git a/src/main/java/blackjack/domain/gameplayer/GamePlayer.java b/src/main/java/blackjack/domain/gameplayer/GamePlayer.java new file mode 100644 index 00000000..78da1ae6 --- /dev/null +++ b/src/main/java/blackjack/domain/gameplayer/GamePlayer.java @@ -0,0 +1,42 @@ +package blackjack.domain.gameplayer; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Cards; +import java.util.List; + +public abstract class GamePlayer { + + private static final int BLACK_JACK = 21; + + private final Name name; + private final Cards cards; + + public GamePlayer(final Name name) { + this.name = name; + this.cards = new Cards(); + } + + public void receiveCard(final Card card) { + cards.receiveCard(card); + } + + public String getName() { + return name.getName(); + } + + public int getScore() { + return cards.calculateCards(); + } + + public List getCards() { + return cards.toList(); + } + + public boolean isContinue() { + return getScore() <= BLACK_JACK; + } + + public abstract boolean isLowerThanBound(); + + public abstract String getGameResult(final List gamePlayers); +} diff --git a/src/main/java/blackjack/domain/gameplayer/GamePlayers.java b/src/main/java/blackjack/domain/gameplayer/GamePlayers.java new file mode 100644 index 00000000..ced03b32 --- /dev/null +++ b/src/main/java/blackjack/domain/gameplayer/GamePlayers.java @@ -0,0 +1,45 @@ +package blackjack.domain.gameplayer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class GamePlayers { + + private static final String DEALER = "딜러"; + private static final int FRONT = 0; + + private final List players; + + public GamePlayers(final Names names) { + this.players = makePlayers(names); + } + + public static List makePlayers(final Names names) { + final List players = names.getNames().stream() + .map(Player::new) + .collect(Collectors.toList()); + + players.add(FRONT, new Dealer()); + + return new ArrayList<>(players); + } + + public GamePlayer getDealer() { + return players.stream() + .filter(player -> player.getName().equals(DEALER)) + .findAny() + .orElseThrow(NullPointerException::new); + } + + public List getPlayers() { + return players.stream() + .filter(player -> player != getDealer()) + .collect(Collectors.toList()); + } + + public List getAllPlayers() { + return Collections.unmodifiableList(players); + } +} diff --git a/src/main/java/blackjack/domain/gameplayer/Name.java b/src/main/java/blackjack/domain/gameplayer/Name.java new file mode 100644 index 00000000..eb8ac62d --- /dev/null +++ b/src/main/java/blackjack/domain/gameplayer/Name.java @@ -0,0 +1,42 @@ +package blackjack.domain.gameplayer; + +import java.util.Objects; + +public class Name { + + private static final String PLAYER_NAME_ERROR_MESSAGE = "이름은 공백이 들어올 수 없습니다."; + + private final String name; + + public Name(final String name) { + validateName(name); + this.name = name; + } + + private void validateName(final String name) { + if (name.trim().isEmpty()) { + throw new IllegalArgumentException(PLAYER_NAME_ERROR_MESSAGE); + } + } + + public String getName() { + return this.name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Name name1 = (Name) o; + return Objects.equals(name, name1.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } +} diff --git a/src/main/java/blackjack/domain/gameplayer/Names.java b/src/main/java/blackjack/domain/gameplayer/Names.java new file mode 100644 index 00000000..cd1f71ce --- /dev/null +++ b/src/main/java/blackjack/domain/gameplayer/Names.java @@ -0,0 +1,52 @@ +package blackjack.domain.gameplayer; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class Names { + + private static final String DELIMITER = ","; + private static final String DUPLICATE_NAMES = "중복된 이름은 들어올 수 없습니다."; + + private List names; + + public Names(final String input) { + this(stringToNames(input)); + } + + public Names(final List names) { + validateDuplicateNames(names); + this.names = names; + } + + private static List stringToNames(String input) { + List names = splitPlayerName(input); + + return names.stream() + .map(Name::new) + .collect(Collectors.toList()); + } + + private static List splitPlayerName(String input) { + String[] split = input.split(DELIMITER); + return Arrays.stream(split) + .map(String::trim) + .filter(name -> !name.isEmpty()) + .collect(Collectors.toList()); + } + + private void validateDuplicateNames(final List names) { + final Set set = new HashSet<>(names); + if (set.size() != names.size()) { + throw new IllegalArgumentException(DUPLICATE_NAMES); + } + } + + public List getNames() { + return Collections.unmodifiableList(names); + } +} diff --git a/src/main/java/blackjack/domain/gameplayer/Player.java b/src/main/java/blackjack/domain/gameplayer/Player.java new file mode 100644 index 00000000..c877086e --- /dev/null +++ b/src/main/java/blackjack/domain/gameplayer/Player.java @@ -0,0 +1,33 @@ +package blackjack.domain.gameplayer; + +import java.util.List; +import java.util.NoSuchElementException; + +public class Player extends GamePlayer { + + private static final String WIN = "승"; + private static final String LOSE = "패"; + + public Player(Name name) { + super(name); + } + + @Override + public boolean isLowerThanBound() { + return isContinue(); + } + + @Override + public String getGameResult(List gamePlayers) { + int winnerScore = gamePlayers.stream() + .filter(GamePlayer::isContinue) + .map(GamePlayer::getScore) + .max(Integer::compareTo) + .orElseThrow(NoSuchElementException::new); + + if (this.getScore() == winnerScore && isContinue()){ + return WIN; + } + return LOSE; + } +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 00000000..bdf14198 --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,38 @@ +package blackjack.view; + +import blackjack.domain.gameplayer.GamePlayer; +import java.util.Scanner; + +public class InputView { + + private static final Scanner scanner = new Scanner(System.in); + private static final String GET_PLAYER_MESSAGE = "게임에 참여할 사람의 이름을 입력하세요."; + private static final String QUESTION_ACCEPT_CARD_MESSAGE = "%s는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"; + private static final String USER_OK = "y"; + + public static String getPlayerName() { + System.out.println(GET_PLAYER_MESSAGE); + String input = scanner.nextLine(); + validateEmpty(input); + + return input; + } + + public static boolean getPlayerChoice(GamePlayer player) { + try { + System.out.println(String.format(QUESTION_ACCEPT_CARD_MESSAGE, player.getName())); + String input = scanner.nextLine(); + validateEmpty(input); + return input.equals(USER_OK); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + return getPlayerChoice(player); + } + } + + private static void validateEmpty(String input) { + if (input.isEmpty()) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java new file mode 100644 index 00000000..8e2ad151 --- /dev/null +++ b/src/main/java/blackjack/view/OutputView.java @@ -0,0 +1,80 @@ +package blackjack.view; + +import blackjack.domain.gameplayer.GamePlayer; +import blackjack.domain.gameplayer.GamePlayers; +import java.util.List; +import java.util.stream.Collectors; + +public class OutputView { + + private static final String ENTER = "\n"; + private static final String GAME_INITIAL_MANAGER = "%s에게 2장의 카드를 나누었습니다."; + private static final String COMMA = ", "; + private static final String CARDS_LOG = "%s: %s"; + private static final String DEALER_RECEIVE_ONE_CARD_MESSAGE = "딜러는 16이하라 한장의 카드를 더 받았습니다."; + private static final String RESULT_CARDS_LOG = "%s카드: %s - 결과: %d"; + private static final String RESULT_HEADER_LOG = "## 최종 승패"; + private static final String RESULT_GAME_LOG = "%s: %s"; + + public static void printInitialMessage(GamePlayers gamePlayers) { + List players = gamePlayers.getAllPlayers(); + + String playerNames = players.stream() + .map(GamePlayer::getName) + .collect(Collectors.joining(COMMA)); + + System.out.println(ENTER + String.format(GAME_INITIAL_MANAGER, playerNames)); + } + + public static void printCardAllStatus(GamePlayers gamePlayers) { + List players = gamePlayers.getAllPlayers(); + + players.forEach(player -> System.out.println(String.format(CARDS_LOG, player.getName(), getPlayerCardStatus(player)))); + System.out.println(); + } + + public static void printCardStatus(GamePlayer player) { + System.out.println(String.format(CARDS_LOG, player.getName(), getPlayerCardStatus(player))); + } + + public static void printDealerAcceptCard() { + System.out.println(DEALER_RECEIVE_ONE_CARD_MESSAGE); + } + + public static void printCardResult(GamePlayers gamePlayers) { + System.out.println(); + List players = gamePlayers.getAllPlayers(); + + for (GamePlayer player : players) { + System.out.println(String.format(RESULT_CARDS_LOG, player.getName(), getPlayerCardStatus(player), player.getScore())); + } + System.out.println(); + } + + private static String getPlayerCardStatus(GamePlayer gamePlayer) { + return gamePlayer.getCards().stream() + .map(card -> card.getCardType().getName() + card.getCardSymbol().getSymbol()) + .collect(Collectors.joining(COMMA)); + } + + public static void printGameResult(GamePlayers gamePlayers) { + System.out.println(RESULT_HEADER_LOG); + + dealerPlayerResult(gamePlayers); + generalPlayerResult(gamePlayers); + } + + private static void dealerPlayerResult(GamePlayers gamePlayers) { + GamePlayer dealer = gamePlayers.getDealer(); + List players = gamePlayers.getPlayers(); + System.out.println(String.format(RESULT_GAME_LOG, dealer.getName(), dealer.getGameResult(players))); + } + + private static void generalPlayerResult(GamePlayers gamePlayers) { + List players = gamePlayers.getPlayers(); + List AllPlayer = gamePlayers.getAllPlayers(); + for (GamePlayer player : players) { + System.out.println(String.format(RESULT_GAME_LOG, player.getName(), player.getGameResult(AllPlayer))); + } + } +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/rentcar/Application.java b/src/main/java/rentcar/Application.java new file mode 100644 index 00000000..a13b70d3 --- /dev/null +++ b/src/main/java/rentcar/Application.java @@ -0,0 +1,11 @@ +package rentcar; + +import rentcar.controller.RentController; + +public class Application { + + public static void main(String[] args) { + RentController rentController = new RentController(); + rentController.start(); + } +} diff --git a/src/main/java/rentcar/controller/RentController.java b/src/main/java/rentcar/controller/RentController.java new file mode 100644 index 00000000..9f2e7133 --- /dev/null +++ b/src/main/java/rentcar/controller/RentController.java @@ -0,0 +1,15 @@ +package rentcar.controller; + +import rentcar.domain.RentCompany; +import rentcar.view.InputView; +import rentcar.view.ReportView; + +public class RentController { + + public void start() { + RentCompany rentCompany = new RentCompany(); + rentCompany.receive(InputView.getTripDistance()); + + ReportView.generateReport(rentCompany.generateChargeQuantityByName()); + } +} diff --git a/src/main/java/rentcar/domain/Avante.java b/src/main/java/rentcar/domain/Avante.java new file mode 100644 index 00000000..f447b7ab --- /dev/null +++ b/src/main/java/rentcar/domain/Avante.java @@ -0,0 +1,26 @@ +package rentcar.domain; + +public class Avante extends Car { + + private static final String NAME = "Avante"; + private static final int DISTANCE_PER_LITER = 15; + + public Avante(final int distance) { + super(distance); + } + + @Override + double getTripDistance() { + return this.distance; + } + + @Override + double getDistancePerLiter() { + return this.DISTANCE_PER_LITER; + } + + @Override + String getCarName() { + return NAME; + } +} diff --git a/src/main/java/rentcar/domain/Car.java b/src/main/java/rentcar/domain/Car.java new file mode 100644 index 00000000..816c210f --- /dev/null +++ b/src/main/java/rentcar/domain/Car.java @@ -0,0 +1,20 @@ +package rentcar.domain; + +public abstract class Car { + + protected final int distance; + + public Car(int distance) { + this.distance = distance; + } + + abstract double getTripDistance(); + + abstract double getDistancePerLiter(); + + abstract String getCarName(); + + double getChargeQuantity() { + return getTripDistance() / getDistancePerLiter(); + } +} diff --git a/src/main/java/rentcar/domain/K5.java b/src/main/java/rentcar/domain/K5.java new file mode 100644 index 00000000..84b7306e --- /dev/null +++ b/src/main/java/rentcar/domain/K5.java @@ -0,0 +1,26 @@ +package rentcar.domain; + +public class K5 extends Car { + + private static final String NAME = "K5"; + private static final int DISTANCE_PER_LITER = 13; + + public K5(final int distance) { + super(distance); + } + + @Override + double getTripDistance() { + return this.distance; + } + + @Override + double getDistancePerLiter() { + return this.DISTANCE_PER_LITER; + } + + @Override + String getCarName() { + return NAME; + } +} diff --git a/src/main/java/rentcar/domain/RentCompany.java b/src/main/java/rentcar/domain/RentCompany.java new file mode 100644 index 00000000..a5dcad81 --- /dev/null +++ b/src/main/java/rentcar/domain/RentCompany.java @@ -0,0 +1,30 @@ +package rentcar.domain; + +import java.util.ArrayList; +import java.util.List; + +public class RentCompany { + + private final Report report = new Report(); + private final List cars; + + public RentCompany() { + this(new ArrayList<>()); + } + + public RentCompany(final List cars) { + this.cars = cars; + } + + public void receive(final int distance) { + cars.add(new Avante(distance)); + cars.add(new K5(distance)); + cars.add(new Sonata(distance)); + } + + public Report generateChargeQuantityByName() { + cars + .forEach(car -> report.putDetails(car)); + return report; + } +} diff --git a/src/main/java/rentcar/domain/Report.java b/src/main/java/rentcar/domain/Report.java new file mode 100644 index 00000000..8e2f78e7 --- /dev/null +++ b/src/main/java/rentcar/domain/Report.java @@ -0,0 +1,23 @@ +package rentcar.domain; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class Report { + + private final Map report; + + public Report() { + this.report = new ConcurrentHashMap<>(); + } + + public void putDetails(final Car car) { + String carName = car.getCarName(); + double chargeQuantity = car.getChargeQuantity(); + report.put(carName, chargeQuantity); + } + + public Map getReport() { + return this.report; + } +} diff --git a/src/main/java/rentcar/domain/Sonata.java b/src/main/java/rentcar/domain/Sonata.java new file mode 100644 index 00000000..07676f86 --- /dev/null +++ b/src/main/java/rentcar/domain/Sonata.java @@ -0,0 +1,26 @@ +package rentcar.domain; + +public class Sonata extends Car { + + private static final String NAME = "Sonata"; + private static final int DISTANCE_PER_LITER = 10; + + public Sonata(final int distance) { + super(distance); + } + + @Override + double getTripDistance() { + return this.distance; + } + + @Override + double getDistancePerLiter() { + return this.DISTANCE_PER_LITER; + } + + @Override + String getCarName() { + return NAME; + } +} diff --git a/src/main/java/rentcar/view/InputView.java b/src/main/java/rentcar/view/InputView.java new file mode 100644 index 00000000..ba871821 --- /dev/null +++ b/src/main/java/rentcar/view/InputView.java @@ -0,0 +1,28 @@ +package rentcar.view; + +import java.util.Scanner; + +public class InputView { + + private final static Scanner scanner = new Scanner(System.in); + private final static String TRIP_MANAGER = "주행 거리(km)를 입력해 주세요."; + private final static String ERROR_LOG = "[ERROR] 0이나 빈 값이 들어올 수 없습니다"; + + public static int getTripDistance() { + try { + System.out.println(TRIP_MANAGER); + String input = scanner.nextLine(); + validateEmptyOrZero(input); + return Integer.parseInt(input); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + return getTripDistance(); + } + } + + private static void validateEmptyOrZero(String input) { + if (input.trim().isEmpty() || input.equals("0")) { + throw new IllegalArgumentException(ERROR_LOG); + } + } +} diff --git a/src/main/java/rentcar/view/ReportView.java b/src/main/java/rentcar/view/ReportView.java new file mode 100644 index 00000000..b79f8eeb --- /dev/null +++ b/src/main/java/rentcar/view/ReportView.java @@ -0,0 +1,16 @@ +package rentcar.view; + +import java.util.Map; +import rentcar.domain.Report; + +public class ReportView { + + private static final String MESSAGE = "%s : %.2f 리터"; + + public static void generateReport(Report report) { + Map reportMap = report.getReport(); + for (String carName : reportMap.keySet()) { + System.out.printf((MESSAGE) + "%n", carName, reportMap.get(carName)); + } + } +} diff --git a/src/test/java/blackjack/domain/card/CardPackTest.java b/src/test/java/blackjack/domain/card/CardPackTest.java new file mode 100644 index 00000000..c7810281 --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardPackTest.java @@ -0,0 +1,66 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +class CardPackTest { + + @Test + public void 초기_카드팩은_52장의_카드가_생성된다() { + //given + CardPack cardPack = new CardPack(); + + //when + List cards = cardPack.getCards(); + + //then + assertThat(cards.size()).isEqualTo(52); + } + + @Test + public void 카드팩에서_한장_빼온뒤에는_카드팩에_남아있는_재고가_하나_감소한다() { + //given + CardPack cardPack = new CardPack(); + + //when + cardPack.pick(); + + //then + assertThat(cardPack.getCards().size()).isEqualTo(51); + } + + @Test + public void 카드팩_생성초기_카드팩은_셔플되어지지_않은_상태다() { + //given + CardPack cardPack = new CardPack(); + List originCards = cardPack.getCards(); + + //when + final List cards = new ArrayList<>(); + for (final CardSymbol symbol : CardSymbol.values()) { + Arrays.stream(CardType.values()) + .forEach(type -> cards.add(new Card(symbol, type))); + } + + //then + assertThat(originCards).containsExactly(cards.toArray(new Card[0])); + } + + @Test + public void 카드가_정상적으로_셔플되어진다() { + //given + CardPack cardPack = new CardPack(); + + //when + List originCards = cardPack.getCards(); + cardPack.shuffle(); + List shuffleCards = cardPack.getCards(); + + //then + assertThat(originCards).doesNotContainSequence(shuffleCards.toArray(new Card[0])); + } +} diff --git a/src/test/java/blackjack/domain/gameplayer/CardsTest.java b/src/test/java/blackjack/domain/gameplayer/CardsTest.java new file mode 100644 index 00000000..bef40fab --- /dev/null +++ b/src/test/java/blackjack/domain/gameplayer/CardsTest.java @@ -0,0 +1,40 @@ +package blackjack.domain.gameplayer; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.CardSymbol; +import blackjack.domain.card.CardType; +import blackjack.domain.card.Cards; +import org.junit.jupiter.api.Test; + +class CardsTest { + + @Test + public void ACE가_포함되지않는_카드리스트는_모두_합산하여_계산한다() { + //given + Cards cards = new Cards(); + + //when + cards.receiveCard(new Card(CardSymbol.DIAMOND, CardType.FIVE)); + cards.receiveCard(new Card(CardSymbol.DIAMOND, CardType.FIVE)); + cards.receiveCard(new Card(CardSymbol.DIAMOND, CardType.FIVE)); + + //then + assertThat(cards.calculateCards()).isEqualTo(15); + } + + @Test + public void ACE가_포함된_카드리스트는_자체적으로_최선의합으로_계산한다() { + //given + Cards cards = new Cards(); + + //when + cards.receiveCard(new Card(CardSymbol.DIAMOND, CardType.ACE)); + cards.receiveCard(new Card(CardSymbol.DIAMOND, CardType.TEN)); + cards.receiveCard(new Card(CardSymbol.DIAMOND, CardType.NINE)); + + //then + assertThat(cards.calculateCards()).isEqualTo(20); + } +} diff --git a/src/test/java/blackjack/domain/gameplayer/DealerPlayerTest.java b/src/test/java/blackjack/domain/gameplayer/DealerPlayerTest.java new file mode 100644 index 00000000..99b36b83 --- /dev/null +++ b/src/test/java/blackjack/domain/gameplayer/DealerPlayerTest.java @@ -0,0 +1,74 @@ +package blackjack.domain.gameplayer; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.CardSymbol; +import blackjack.domain.card.CardType; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +class DealerPlayerTest { + + @Test + public void 딜러는_카드의합이_16이하면_카드를_받을수있는_조건이_된다() { + //given + GamePlayers gamePlayers = new GamePlayers(new Names(Arrays.asList(new Name("pobi"), new Name("jason")))); + GamePlayer dealer = gamePlayers.getDealer(); + + //when + dealer.receiveCard(new Card(CardSymbol.DIAMOND, CardType.ACE)); + + //then + assertThat(dealer.isLowerThanBound()).isTrue(); + } + + @Test + public void 딜러는_카드의합이_16초과면_카드를_받을수없다() { + //given + GamePlayers gamePlayers = new GamePlayers(new Names(Arrays.asList(new Name("pobi"), new Name("jason")))); + GamePlayer dealer = gamePlayers.getDealer(); + + //when + dealer.receiveCard(new Card(CardSymbol.DIAMOND, CardType.ACE)); + dealer.receiveCard(new Card(CardSymbol.DIAMOND, CardType.NINE)); + + //then + assertThat(dealer.isLowerThanBound()).isFalse(); + } + + @Test + public void 딜러플레이어의_최종승패는_올바르게_계산된다_2승을출력() { + //given + GamePlayers gamePlayers = new GamePlayers(new Names(Arrays.asList(new Name("pobi"), new Name("jason")))); + List players = gamePlayers.getPlayers(); + GamePlayer dealer = gamePlayers.getDealer(); + dealer.receiveCard(new Card(CardSymbol.DIAMOND, CardType.ACE)); + players.get(0).receiveCard(new Card(CardSymbol.CLOVER, CardType.NINE)); + players.get(1).receiveCard(new Card(CardSymbol.CLOVER, CardType.FOUR)); + + //when + String dealerResult = dealer.getGameResult(gamePlayers.getPlayers()); + + //then + assertThat(dealerResult).isEqualTo("2승 0패"); + } + + @Test + public void 딜러플레이어의_최종승패는_올바르게_계산된다_1승1패를출력() { + //given + GamePlayers gamePlayers = new GamePlayers(new Names(Arrays.asList(new Name("pobi"), new Name("jason")))); + List players = gamePlayers.getPlayers(); + GamePlayer dealer = gamePlayers.getDealer(); + dealer.receiveCard(new Card(CardSymbol.DIAMOND, CardType.SIX)); + players.get(0).receiveCard(new Card(CardSymbol.CLOVER, CardType.NINE)); + players.get(1).receiveCard(new Card(CardSymbol.CLOVER, CardType.FOUR)); + + //when + String dealerResult = dealer.getGameResult(gamePlayers.getPlayers()); + + //then + assertThat(dealerResult).isEqualTo("1승 1패"); + } +} diff --git a/src/test/java/blackjack/domain/gameplayer/GamePlayersTest.java b/src/test/java/blackjack/domain/gameplayer/GamePlayersTest.java new file mode 100644 index 00000000..0374d551 --- /dev/null +++ b/src/test/java/blackjack/domain/gameplayer/GamePlayersTest.java @@ -0,0 +1,26 @@ +package blackjack.domain.gameplayer; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; + +class GamePlayersTest { + + @Test + public void 입력된_플레이어이름으로_게임플레이어객체가_생성된다() { + //given + List names = Arrays.asList("pobi", "jason"); + + //when + GamePlayers gamePlayers = new GamePlayers(new Names(Arrays.asList(new Name("pobi"), new Name("jason")))); + List players = gamePlayers.getPlayers().stream() + .map(GamePlayer::getName) + .collect(Collectors.toList()); + + //then + assertThat(players).contains(names.toArray(new String[0])); + } +} diff --git a/src/test/java/blackjack/domain/gameplayer/NameTest.java b/src/test/java/blackjack/domain/gameplayer/NameTest.java new file mode 100644 index 00000000..9e015efb --- /dev/null +++ b/src/test/java/blackjack/domain/gameplayer/NameTest.java @@ -0,0 +1,21 @@ +package blackjack.domain.gameplayer; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class NameTest { + + @Test + public void 이름은_공백이_들어올_수_없다() { + //given + String PLAYER_NAME_ERROR_MESSAGE = "이름은 공백이 들어올 수 없습니다."; + + //when + + //then + assertThatThrownBy(() -> new Name("")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(PLAYER_NAME_ERROR_MESSAGE); + } +} diff --git a/src/test/java/blackjack/domain/gameplayer/NamesTest.java b/src/test/java/blackjack/domain/gameplayer/NamesTest.java new file mode 100644 index 00000000..e4a03421 --- /dev/null +++ b/src/test/java/blackjack/domain/gameplayer/NamesTest.java @@ -0,0 +1,22 @@ +package blackjack.domain.gameplayer; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +class NamesTest { + + @Test + public void 중복된_이름은_들어올_수_없다() { + //given + final String DUPLICATE_NAMES = "중복된 이름은 들어올 수 없습니다."; + + //when + + //then + assertThatThrownBy(() -> new Names(Arrays.asList(new Name("pobi"), new Name("pobi")))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(DUPLICATE_NAMES); + } +} diff --git a/src/test/java/blackjack/domain/gameplayer/PlayerTest.java b/src/test/java/blackjack/domain/gameplayer/PlayerTest.java new file mode 100644 index 00000000..25f5c6ae --- /dev/null +++ b/src/test/java/blackjack/domain/gameplayer/PlayerTest.java @@ -0,0 +1,105 @@ +package blackjack.domain.gameplayer; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.CardSymbol; +import blackjack.domain.card.CardType; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +class PlayerTest { + + @Test + public void 특정카드를_받았을때_그만큼의_점수를_더해준다() { + //given + GamePlayer player = new Player(new Name("pobi")); + + //when + player.receiveCard(new Card(CardSymbol.CLOVER, CardType.FIVE)); + + //then + assertThat(player.getScore()).isEqualTo(5); + } + + @Test + public void Ace를_받았을때_합이_21을_넘을경우_1을_더해준다() { + //given + GamePlayer player = new Player(new Name("pobi")); + + //when + player.receiveCard(new Card(CardSymbol.CLOVER, CardType.FIVE)); + player.receiveCard(new Card(CardSymbol.CLOVER, CardType.FIVE)); + player.receiveCard(new Card(CardSymbol.CLOVER, CardType.FIVE)); + player.receiveCard(new Card(CardSymbol.CLOVER, CardType.ACE)); + //then + assertThat(player.getScore()).isEqualTo(16); + } + + @Test + public void Ace를_받았을때_합이_21을_넘지않을경우_11을_더해준다() { + //given + GamePlayer player = new Player(new Name("pobi")); + + //when + player.receiveCard(new Card(CardSymbol.CLOVER, CardType.FIVE)); + player.receiveCard(new Card(CardSymbol.CLOVER, CardType.FIVE)); + player.receiveCard(new Card(CardSymbol.CLOVER, CardType.ACE)); + + //then + assertThat(player.getScore()).isEqualTo(21); + } + + @Test + public void 플레이어는_카드의합이_21이하면_카드를_받을수있는_조건이_된다() { + //given + GamePlayers gamePlayers = new GamePlayers(new Names(Arrays.asList(new Name("pobi")))); + + List players = gamePlayers.getPlayers(); + GamePlayer player = players.get(0); + + //when + player.receiveCard(new Card(CardSymbol.DIAMOND, CardType.NINE)); + player.receiveCard(new Card(CardSymbol.DIAMOND, CardType.TEN)); + + //then + assertThat(player.isLowerThanBound()).isTrue(); + } + + @Test + public void 딜러는_카드의합이_21초과면_카드를_받을수없다() { + //given + GamePlayers gamePlayers = new GamePlayers(new Names(Arrays.asList(new Name("pobi")))); + + List players = gamePlayers.getPlayers(); + GamePlayer player = players.get(0); + + //when + player.receiveCard(new Card(CardSymbol.DIAMOND, CardType.NINE)); + player.receiveCard(new Card(CardSymbol.DIAMOND, CardType.TEN)); + player.receiveCard(new Card(CardSymbol.DIAMOND, CardType.EIGHT)); + + //then + assertThat(player.isLowerThanBound()).isFalse(); + } + + @Test + public void 일반플레이어의_최종승패는_올바르게_계산된다() { + //given + GamePlayers gamePlayers = new GamePlayers(new Names(Arrays.asList(new Name("pobi"), new Name("jason")))); + List players = gamePlayers.getPlayers(); + players.get(0).receiveCard(new Card(CardSymbol.CLOVER, CardType.NINE)); + players.get(1).receiveCard(new Card(CardSymbol.CLOVER, CardType.FOUR)); + + //when + String pobiResult = players.get(0).getGameResult(gamePlayers.getAllPlayers()); + String jasonResult = players.get(1).getGameResult(gamePlayers.getAllPlayers()); + + //then + org.junit.jupiter.api.Assertions.assertAll( + () -> assertThat(pobiResult).isEqualTo("승"), + () -> assertThat(jasonResult).isEqualTo("패") + ); + } +} diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test/java/rentcar/domain/CarTest.java b/src/test/java/rentcar/domain/CarTest.java new file mode 100644 index 00000000..e305fe88 --- /dev/null +++ b/src/test/java/rentcar/domain/CarTest.java @@ -0,0 +1,36 @@ +package rentcar.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CarTest { + + @DisplayName("이동거리로 주입해야할 연료량을 계산한다.") + @Test + public void getChargeQuantity() { + //given + final List FUELS = Arrays.asList(20.00, 15.00, 20.00); + final List DISTANCES = Arrays.asList(260, 150, 300); + + final List cars = new ArrayList<>(); + + cars.add(new K5(DISTANCES.get(0))); + cars.add(new Sonata(DISTANCES.get(1))); + cars.add(new Avante(DISTANCES.get(2))); + + //when + final List results = new ArrayList<>(); + + for (Car car : cars) { + results.add(car.getChargeQuantity()); + } + + //then + assertThat(results).isEqualTo(FUELS); + } +} diff --git a/src/test/java/rentcar/domain/RentCompanyTest.java b/src/test/java/rentcar/domain/RentCompanyTest.java new file mode 100644 index 00000000..2313fa96 --- /dev/null +++ b/src/test/java/rentcar/domain/RentCompanyTest.java @@ -0,0 +1,30 @@ +package rentcar.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class RentCompanyTest { + + @Test + void 자동차_이름과_연료량을_받아_map을_반환한다() { + List cars = new ArrayList<>(); + + cars.add(new Avante(300)); + cars.add(new K5(260)); + cars.add(new Sonata(150)); + + RentCompany rentCompany = new RentCompany(cars); + + Report report = rentCompany.generateChargeQuantityByName(); + + org.junit.jupiter.api.Assertions.assertAll( + () -> assertThat(report.getReport().get("Avante")).isEqualTo(20), + () -> assertThat(report.getReport().get("K5")).isEqualTo(20), + () -> assertThat(report.getReport().get("Sonata")).isEqualTo(15) + ); + } +}