diff --git a/README.md b/README.md index a05a3c0ce7b..a053a9f0c8d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,16 @@ # java-blackjack -블랙잭 미션 저장소 +## 기능 요구 사항 -## [미션 리드미](https://github.com/talmood/private-mission-README/tree/main/%EB%AF%B8%EC%85%98%204%20-%20%EB%B8%94%EB%9E%99%EC%9E%AD) +- 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. +- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. +- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다. +- 게임을 완료한 후 각 플레이어별로 승패를 출력한다. + +## 구현 목록 + +- [x] 게임의 참여할 사람을 입력받는다. +- [x] 딜러와 플레이어들이 카드를 나눠 받는다. +- [x] 딜러와 플레이어들이 추가 카드를 나눠 갖는다. +- [x] 딜러와 플레이어들의 결과를 출력한다. +- [x] 최종 결과를 출력한다. \ No newline at end of file diff --git a/src/main/java/BlackjackApplication.java b/src/main/java/BlackjackApplication.java new file mode 100644 index 00000000000..14f26c8dde0 --- /dev/null +++ b/src/main/java/BlackjackApplication.java @@ -0,0 +1,23 @@ +import Controller.BlackjackSimulatorRunner; +import domain.game.BlackjackSimulator; +import view.input.ConsoleInputReader; +import view.input.ConsoleInputView; +import view.input.InputReader; +import view.input.InputView; +import view.output.ConsoleOutputView; +import view.output.ConsoleOutputWriter; +import view.output.OutputView; +import view.output.OutputWriter; + +public class BlackjackApplication { + + public static void main(String[] args) { + InputReader inputReader = new ConsoleInputReader(); + OutputWriter outputWriter = new ConsoleOutputWriter(); + InputView inputView = new ConsoleInputView(inputReader, outputWriter); + OutputView outputView = new ConsoleOutputView(outputWriter); + + BlackjackSimulatorRunner blackjackSimulatorRunner = new BlackjackSimulatorRunner(new BlackjackSimulator(inputView, outputView)); + blackjackSimulatorRunner.run(); + } +} diff --git a/src/main/java/Controller/BlackjackSimulatorRunner.java b/src/main/java/Controller/BlackjackSimulatorRunner.java new file mode 100644 index 00000000000..cbd4d6526cb --- /dev/null +++ b/src/main/java/Controller/BlackjackSimulatorRunner.java @@ -0,0 +1,21 @@ +package Controller; + +import domain.game.BlackjackGame; +import domain.game.BlackjackSimulator; + +public class BlackjackSimulatorRunner { + + private final BlackjackSimulator blackjackSimulator; + + public BlackjackSimulatorRunner(BlackjackSimulator blackjackSimulator) { + this.blackjackSimulator = blackjackSimulator; + } + + public void run() { + BlackjackGame notPlayedGame = blackjackSimulator.createNotPlayedGame(); + BlackjackGame initializeHandOutGame = blackjackSimulator.InitialHandOutGame(notPlayedGame); + BlackjackGame handOutPlayersGame = blackjackSimulator.handOutPlayers(initializeHandOutGame); + BlackjackGame handOutDealerGame = blackjackSimulator.handOutDealer(handOutPlayersGame); + blackjackSimulator.decideWinOrLose(handOutDealerGame); + } +} diff --git a/src/main/java/domain/game/BlackjackGame.java b/src/main/java/domain/game/BlackjackGame.java new file mode 100644 index 00000000000..5e0b204ab05 --- /dev/null +++ b/src/main/java/domain/game/BlackjackGame.java @@ -0,0 +1,45 @@ +package domain.game; + +import domain.participant.*; +import domain.trumpcard.TrumpCardDeck; +import domain.validator.ObjectsValidator; + +import java.util.List; + +public class BlackjackGame { + + private final BlackjackParticipants blackjackParticipants; + private final TrumpCardDeck trumpCardDeck; + + public BlackjackGame(BlackjackParticipants blackjackParticipants, TrumpCardDeck trumpCardDeck) { + ObjectsValidator.validateNotNull(blackjackParticipants, trumpCardDeck); + this.blackjackParticipants = blackjackParticipants; + this.trumpCardDeck = trumpCardDeck; + } + + public BlackjackGame(BlackjackGame blackjackGame) { + ObjectsValidator.validateNotNull(blackjackGame); + this.blackjackParticipants = blackjackGame.blackjackParticipants; + this.trumpCardDeck = blackjackGame.trumpCardDeck; + } + + public List fetchBlackjackParticipants() { + return this.blackjackParticipants.fetchBlackjackParticipants(); + } + + public List fetchBlackjackPlayers() { + return this.blackjackParticipants.findPlayers().fetchBlackjackPlayers(); + } + + public BlackjackDealer findDealer() { + return this.blackjackParticipants.findDealer(); + } + + public BlackjackPlayers findPlayers() { + return this.blackjackParticipants.findPlayers(); + } + + public TrumpCardDeck getTrumpCardDeck() { + return trumpCardDeck; + } +} diff --git a/src/main/java/domain/game/BlackjackGameCreator.java b/src/main/java/domain/game/BlackjackGameCreator.java new file mode 100644 index 00000000000..3d610c9e467 --- /dev/null +++ b/src/main/java/domain/game/BlackjackGameCreator.java @@ -0,0 +1,32 @@ +package domain.game; + +import domain.participant.BlackjackDealer; +import domain.participant.BlackjackParticipants; +import domain.participant.BlackjackPlayerName; +import domain.participant.BlackjackPlayers; +import domain.trumpcard.TrumpCardDeck; +import domain.validator.ObjectsValidator; +import view.input.InputView; +import view.input.dto.PlayersInput; + +import java.util.List; + +public class BlackjackGameCreator { + + private final InputView inputView; + + public BlackjackGameCreator(InputView inputView) { + ObjectsValidator.validateNotNull(inputView); + this.inputView = inputView; + } + + public BlackjackGame create() { + PlayersInput playersInput = inputView.viewPlayers(); + List blackjackPlayerNames = playersInput.toBlackjackPlayerNames(); + BlackjackPlayers blackjackPlayers = BlackjackPlayers.fromNamesWithEmptyCards(blackjackPlayerNames); + BlackjackDealer blackjackDealer = BlackjackDealer.createWithEmptyCards(); + TrumpCardDeck trumpCardDeck = new TrumpCardDeck(); + + return new BlackjackGame(BlackjackParticipants.of(blackjackDealer, blackjackPlayers), trumpCardDeck); + } +} diff --git a/src/main/java/domain/game/BlackjackSimulator.java b/src/main/java/domain/game/BlackjackSimulator.java new file mode 100644 index 00000000000..f1afa544e9b --- /dev/null +++ b/src/main/java/domain/game/BlackjackSimulator.java @@ -0,0 +1,59 @@ +package domain.game; + +import domain.handouter.*; +import domain.validator.ObjectsValidator; +import domain.winorlose.BlackjackWinOrLoseDecider; +import domain.winorlose.FinalWinOrLose; +import view.input.InputView; +import view.output.OutputView; +import view.output.dto.BlackjackResultOutputs; +import view.output.dto.FinalWinOrLoseOutput; +import view.output.dto.InitialHandOutOutput; + +public class BlackjackSimulator { + + private static final int INITIAL_HANDOUT_COUNT = 2; + + private final InputView inputView; + + private final OutputView outputView; + + public BlackjackSimulator(InputView inputView, OutputView outputView) { + ObjectsValidator.validateNotNull(inputView, outputView); + this.inputView = inputView; + this.outputView = outputView; + } + + public BlackjackGame createNotPlayedGame() { + BlackjackGameCreator blackjackGameCreator = new BlackjackGameCreator(inputView); + return blackjackGameCreator.create(); + } + + public BlackjackGame InitialHandOutGame(BlackjackGame blackjackGame) { + HandOutCount handOutCount = new HandOutCount(INITIAL_HANDOUT_COUNT); + BlackjackHandOuter blackjackInitialHandOuter = new BlackjackInitialHandOuter(blackjackGame, handOutCount); + BlackjackGame initialHandOutGame = blackjackInitialHandOuter.handOut(); + outputView.viewInitialHandOut(InitialHandOutOutput.of(handOutCount, initialHandOutGame)); + + return initialHandOutGame; + } + + public BlackjackGame handOutPlayers(BlackjackGame blackjackGame) { + BlackjackHandOuter blackjackPlayersHandOuter = new BlackjackPlayersHandOuter(blackjackGame, inputView, outputView); + return blackjackPlayersHandOuter.handOut(); + } + + public BlackjackGame handOutDealer(BlackjackGame blackjackGame) { + BlackjackHandOuter blackjackDealerHandOuter = new BlackjackDealerHandOuter(blackjackGame, outputView); + BlackjackGame dealerHandOutGame = blackjackDealerHandOuter.handOut(); + outputView.viewBlackjackResult(BlackjackResultOutputs.from(dealerHandOutGame)); + + return dealerHandOutGame; + } + + public void decideWinOrLose(BlackjackGame blackjackGame) { + BlackjackWinOrLoseDecider blackjackWinOrLoseDecider = new BlackjackWinOrLoseDecider(blackjackGame); + FinalWinOrLose winOrLose = blackjackWinOrLoseDecider.decide(); + outputView.viewFinalWinOrLose(FinalWinOrLoseOutput.from(winOrLose)); + } +} diff --git a/src/main/java/domain/handouter/BlackjackDealerHandOuter.java b/src/main/java/domain/handouter/BlackjackDealerHandOuter.java new file mode 100644 index 00000000000..63287f95386 --- /dev/null +++ b/src/main/java/domain/handouter/BlackjackDealerHandOuter.java @@ -0,0 +1,48 @@ +package domain.handouter; + +import domain.game.BlackjackGame; +import domain.participant.BlackjackDealer; +import domain.participant.BlackjackParticipant; +import domain.participant.BlackjackParticipants; +import domain.trumpcard.TrumpCard; +import domain.trumpcard.TrumpCardDeck; +import domain.validator.ObjectsValidator; +import domain.winorlose.BlackjackPoint; +import view.output.OutputView; +import view.output.dto.HandOutDealerOutput; + +import java.util.ArrayList; +import java.util.List; + +public class BlackjackDealerHandOuter implements BlackjackHandOuter { + + private static final int DEALER_HAND_OUT_THRESHOLD = 17; + + private final BlackjackGame blackjackGame; + + private final OutputView outputView; + + public BlackjackDealerHandOuter(BlackjackGame blackjackGame, OutputView outputView) { + ObjectsValidator.validateNotNull(blackjackGame, outputView); + this.blackjackGame = blackjackGame; + this.outputView = outputView; + } + + @Override + public BlackjackGame handOut() { + List receivedParticipants = new ArrayList<>(this.blackjackGame.fetchBlackjackPlayers()); + BlackjackDealer dealer = this.blackjackGame.findDealer(); + BlackjackPoint blackjackPoint = dealer.calculatePoint(); + TrumpCardDeck trumpCardDeck = this.blackjackGame.getTrumpCardDeck(); + + if (blackjackPoint.isLowerThan(new BlackjackPoint(DEALER_HAND_OUT_THRESHOLD))) { + TrumpCard trumpCard = trumpCardDeck.fetchTopOne(); + trumpCardDeck = trumpCardDeck.takeOutTopOne(); + dealer = dealer.receiveCard(trumpCard); + this.outputView.viewHandOutDealer(new HandOutDealerOutput(DEALER_HAND_OUT_THRESHOLD - 1)); + } + + receivedParticipants.add(dealer); + return new BlackjackGame(new BlackjackParticipants(receivedParticipants), trumpCardDeck); + } +} diff --git a/src/main/java/domain/handouter/BlackjackHandOuter.java b/src/main/java/domain/handouter/BlackjackHandOuter.java new file mode 100644 index 00000000000..8eb351143f1 --- /dev/null +++ b/src/main/java/domain/handouter/BlackjackHandOuter.java @@ -0,0 +1,8 @@ +package domain.handouter; + +import domain.game.BlackjackGame; + +public interface BlackjackHandOuter { + + BlackjackGame handOut(); +} diff --git a/src/main/java/domain/handouter/BlackjackInitialHandOuter.java b/src/main/java/domain/handouter/BlackjackInitialHandOuter.java new file mode 100644 index 00000000000..d7800b07aac --- /dev/null +++ b/src/main/java/domain/handouter/BlackjackInitialHandOuter.java @@ -0,0 +1,51 @@ +package domain.handouter; + +import domain.game.BlackjackGame; +import domain.participant.BlackjackParticipant; +import domain.participant.BlackjackParticipants; +import domain.trumpcard.TrumpCard; +import domain.trumpcard.TrumpCardDeck; +import domain.validator.ObjectsValidator; + +import java.util.ArrayList; +import java.util.List; + +public class BlackjackInitialHandOuter implements BlackjackHandOuter { + + private final BlackjackGame blackjackGame; + + private final HandOutCount handOutCount; + + public BlackjackInitialHandOuter(BlackjackGame blackjackGame, HandOutCount handOutCount) { + ObjectsValidator.validateNotNull(blackjackGame, handOutCount); + this.blackjackGame = blackjackGame; + this.handOutCount = handOutCount; + } + + @Override + public BlackjackGame handOut() { + BlackjackGame blackjackGame = new BlackjackGame(this.blackjackGame); + + for (int i = 0; i < handOutCount.handOutCount(); i++) { + blackjackGame = this.handOutAllParticipants(blackjackGame); + } + + return blackjackGame; + } + + private BlackjackGame handOutAllParticipants(BlackjackGame blackjackGame) { + ArrayList receivedParticipants = new ArrayList<>(); + TrumpCardDeck trumpCardDeck = blackjackGame.getTrumpCardDeck(); + + List participants = blackjackGame.fetchBlackjackParticipants(); + + for (BlackjackParticipant participant : participants) { + TrumpCard trumpCard = trumpCardDeck.fetchTopOne(); + trumpCardDeck = trumpCardDeck.takeOutTopOne(); + BlackjackParticipant receiveParticipant = participant.receiveCard(trumpCard); + receivedParticipants.add(receiveParticipant); + } + + return new BlackjackGame(new BlackjackParticipants(receivedParticipants), trumpCardDeck); + } +} diff --git a/src/main/java/domain/handouter/BlackjackPlayersHandOuter.java b/src/main/java/domain/handouter/BlackjackPlayersHandOuter.java new file mode 100644 index 00000000000..e370e616057 --- /dev/null +++ b/src/main/java/domain/handouter/BlackjackPlayersHandOuter.java @@ -0,0 +1,62 @@ +package domain.handouter; + +import domain.game.BlackjackGame; +import domain.participant.BlackjackParticipant; +import domain.participant.BlackjackParticipants; +import domain.participant.BlackjackPlayer; +import domain.participant.BlackjackPlayerName; +import domain.trumpcard.TrumpCard; +import domain.trumpcard.TrumpCardDeck; +import domain.validator.ObjectsValidator; +import view.input.InputView; +import view.input.dto.HandOutPlayerInput; +import view.input.dto.HandOutPlayerRequest; +import view.output.OutputView; +import view.output.dto.HandOutPlayerOutput; + +import java.util.ArrayList; +import java.util.List; + +public class BlackjackPlayersHandOuter implements BlackjackHandOuter { + + private final BlackjackGame blackjackGame; + + private final InputView inputView; + + private final OutputView outputView; + + public BlackjackPlayersHandOuter(BlackjackGame blackjackGame, InputView inputView, OutputView outputView) { + ObjectsValidator.validateNotNull(blackjackGame, inputView, outputView); + this.blackjackGame = blackjackGame; + this.inputView = inputView; + this.outputView = outputView; + } + + @Override + public BlackjackGame handOut() { + List receivedParticipants = new ArrayList<>(); + TrumpCardDeck trumpCardDeck = this.blackjackGame.getTrumpCardDeck(); + List blackjackPlayers = this.blackjackGame.fetchBlackjackPlayers(); + + for (BlackjackPlayer blackjackPlayer : blackjackPlayers) { + BlackjackPlayerName blackjackPlayerName = blackjackPlayer.getBlackjackPlayerName(); + + while (this.isHandOut(blackjackPlayerName, this.inputView)) { + TrumpCard trumpCard = trumpCardDeck.fetchTopOne(); + trumpCardDeck = trumpCardDeck.takeOutTopOne(); + blackjackPlayer = blackjackPlayer.receiveCard(trumpCard); + this.outputView.viewHandOutPlayer(HandOutPlayerOutput.of(blackjackPlayerName, blackjackPlayer)); + } + receivedParticipants.add(blackjackPlayer); + } + + receivedParticipants.add(this.blackjackGame.findDealer()); + + return new BlackjackGame(new BlackjackParticipants(receivedParticipants), trumpCardDeck); + } + + private boolean isHandOut(BlackjackPlayerName blackjackPlayerName, InputView inputView) { + HandOutPlayerInput handOutPlayerInput = inputView.viewHandOutCardForPlayer(HandOutPlayerRequest.from(blackjackPlayerName)); + return handOutPlayerInput.isHandOut(); + } +} diff --git a/src/main/java/domain/handouter/HandOutCount.java b/src/main/java/domain/handouter/HandOutCount.java new file mode 100644 index 00000000000..ef1d7a1d9d2 --- /dev/null +++ b/src/main/java/domain/handouter/HandOutCount.java @@ -0,0 +1,14 @@ +package domain.handouter; + +public record HandOutCount(int handOutCount) { + + public HandOutCount { + this.validateGreaterThanZero(handOutCount); + } + + private void validateGreaterThanZero(int handOutCount) { + if (handOutCount <= 0) { + throw new IllegalArgumentException("나눠주는 카드는 1장 이상이어야 합니다."); + } + } +} diff --git a/src/main/java/domain/participant/BlackjackDealer.java b/src/main/java/domain/participant/BlackjackDealer.java new file mode 100644 index 00000000000..fd3eba4219b --- /dev/null +++ b/src/main/java/domain/participant/BlackjackDealer.java @@ -0,0 +1,56 @@ +package domain.participant; + +import domain.trumpcard.TrumpCard; +import domain.trumpcard.TrumpCards; +import domain.validator.ObjectsValidator; +import domain.winorlose.BlackjackPoint; + +import java.util.List; + +public class BlackjackDealer implements BlackjackParticipant { + + private final TrumpCards trumpCards; + + public BlackjackDealer(TrumpCards trumpCards) { + ObjectsValidator.validateNotNull(trumpCards); + this.trumpCards = trumpCards; + } + + public static BlackjackDealer createWithEmptyCards() { + return new BlackjackDealer(TrumpCards.createEmptyCards()); + } + + + @Override + public BlackjackDealer receiveCard(TrumpCard trumpCard) { + return new BlackjackDealer(this.trumpCards.addCard(trumpCard)); + } + + @Override + public List fetchKoreanCardNames() { + return this.trumpCards.fetchKoreanNames(); + } + + @Override + public boolean isDealer() { + return true; + } + + @Override + public boolean isPlayer() { + return false; + } + + @Override + public BlackjackPoint calculatePoint() { + BlackjackPoint minBlackjackPoint = trumpCards.totalMinBlackjackPoint(); + BlackjackPoint maxBlackjackPoint = trumpCards.totalMaxBlackjackPoint(); + + return minBlackjackPoint.fetchCloserPointThreshold(maxBlackjackPoint); + } + + @Override + public int fetchCardSize() { + return this.trumpCards.size(); + } +} diff --git a/src/main/java/domain/participant/BlackjackParticipant.java b/src/main/java/domain/participant/BlackjackParticipant.java new file mode 100644 index 00000000000..ab54037b056 --- /dev/null +++ b/src/main/java/domain/participant/BlackjackParticipant.java @@ -0,0 +1,21 @@ +package domain.participant; + +import domain.trumpcard.TrumpCard; +import domain.winorlose.BlackjackPoint; + +import java.util.List; + +public interface BlackjackParticipant { + + BlackjackParticipant receiveCard(TrumpCard trumpCard); + + List fetchKoreanCardNames(); + + boolean isDealer(); + + boolean isPlayer(); + + BlackjackPoint calculatePoint(); + + int fetchCardSize(); +} diff --git a/src/main/java/domain/participant/BlackjackParticipants.java b/src/main/java/domain/participant/BlackjackParticipants.java new file mode 100644 index 00000000000..306a4e3a645 --- /dev/null +++ b/src/main/java/domain/participant/BlackjackParticipants.java @@ -0,0 +1,52 @@ +package domain.participant; + +import domain.validator.CollectionValidator; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class BlackjackParticipants { + + private final List blackjackParticipants; + + public BlackjackParticipants(List blackjackParticipants) { + CollectionValidator.validateNotEmpty(blackjackParticipants); + this.blackjackParticipants = blackjackParticipants; + } + + public static BlackjackParticipants of(BlackjackDealer blackjackDealer, BlackjackPlayers blackjackPlayers) { + ArrayList participants = new ArrayList<>(); + participants.add(blackjackDealer); + participants.addAll(blackjackPlayers.fetchBlackjackParticipant()); + + return new BlackjackParticipants( + participants + ); + } + + public int size() { + return this.blackjackParticipants.size(); + } + + public List fetchBlackjackParticipants() { + return List.copyOf(this.blackjackParticipants); + } + + public BlackjackDealer findDealer() { + return this.blackjackParticipants.stream() + .filter(BlackjackParticipant::isDealer) + .findAny() + .map(BlackjackDealer.class::cast) + .orElseThrow(() -> new IllegalStateException("참가자에 딜러가 존재하지 않습니다.")); + } + + public BlackjackPlayers findPlayers() { + return new BlackjackPlayers( + this.blackjackParticipants.stream() + .filter(BlackjackParticipant::isPlayer) + .map(BlackjackPlayer.class::cast) + .collect(Collectors.toList()) + ); + } +} diff --git a/src/main/java/domain/participant/BlackjackPlayer.java b/src/main/java/domain/participant/BlackjackPlayer.java new file mode 100644 index 00000000000..4a156e8a5e0 --- /dev/null +++ b/src/main/java/domain/participant/BlackjackPlayer.java @@ -0,0 +1,61 @@ +package domain.participant; + +import domain.trumpcard.TrumpCard; +import domain.trumpcard.TrumpCards; +import domain.validator.ObjectsValidator; +import domain.winorlose.BlackjackPoint; + +import java.util.List; + +public class BlackjackPlayer implements BlackjackParticipant { + + private final BlackjackPlayerName blackjackPlayerName; + private final TrumpCards trumpCards; + + public BlackjackPlayer(BlackjackPlayerName blackjackPlayerName, TrumpCards trumpCards) { + ObjectsValidator.validateNotNull(blackjackPlayerName, trumpCards); + this.blackjackPlayerName = blackjackPlayerName; + this.trumpCards = trumpCards; + } + + @Override + public BlackjackPlayer receiveCard(TrumpCard trumpCard) { + return new BlackjackPlayer(this.blackjackPlayerName, this.trumpCards.addCard(trumpCard)); + } + + public BlackjackPlayerName getBlackjackPlayerName() { + return this.blackjackPlayerName; + } + + @Override + public List fetchKoreanCardNames() { + return this.trumpCards.fetchKoreanNames(); + } + + @Override + public boolean isDealer() { + return false; + } + + @Override + public boolean isPlayer() { + return true; + } + + @Override + public BlackjackPoint calculatePoint() { + BlackjackPoint minBlackjackPoint = trumpCards.totalMinBlackjackPoint(); + BlackjackPoint maxBlackjackPoint = trumpCards.totalMaxBlackjackPoint(); + + return minBlackjackPoint.fetchCloserPointThreshold(maxBlackjackPoint); + } + + @Override + public int fetchCardSize() { + return this.trumpCards.size(); + } + + public String getPlayerName() { + return blackjackPlayerName.name(); + } +} diff --git a/src/main/java/domain/participant/BlackjackPlayerName.java b/src/main/java/domain/participant/BlackjackPlayerName.java new file mode 100644 index 00000000000..7bca7095b24 --- /dev/null +++ b/src/main/java/domain/participant/BlackjackPlayerName.java @@ -0,0 +1,26 @@ +package domain.participant; + +import util.StringUtils; + +import java.util.Objects; + +public record BlackjackPlayerName(String name) { + + public BlackjackPlayerName { + this.validateNotEmpty(name); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlackjackPlayerName that = (BlackjackPlayerName) o; + return Objects.equals(name, that.name); + } + + private void validateNotEmpty(String name) { + if (StringUtils.isEmpty(name)) { + throw new IllegalArgumentException("블랙잭 참여자의 이름은 null이거나 공백이면 안됩니다."); + } + } +} diff --git a/src/main/java/domain/participant/BlackjackPlayers.java b/src/main/java/domain/participant/BlackjackPlayers.java new file mode 100644 index 00000000000..7ed4291b34e --- /dev/null +++ b/src/main/java/domain/participant/BlackjackPlayers.java @@ -0,0 +1,33 @@ +package domain.participant; + +import domain.trumpcard.TrumpCards; +import domain.validator.CollectionValidator; + +import java.util.List; +import java.util.stream.Collectors; + +public class BlackjackPlayers { + + private final List blackjackPlayers; + + public BlackjackPlayers(List blackjackPlayers) { + CollectionValidator.validateNotEmpty(blackjackPlayers); + this.blackjackPlayers = blackjackPlayers; + } + + public static BlackjackPlayers fromNamesWithEmptyCards(List blackjackPlayerNames) { + return new BlackjackPlayers( + blackjackPlayerNames.stream() + .map(name -> new BlackjackPlayer(name, TrumpCards.createEmptyCards())) + .collect(Collectors.toList()) + ); + } + + public List fetchBlackjackParticipant() { + return List.copyOf(this.blackjackPlayers); + } + + public List fetchBlackjackPlayers() { + return List.copyOf(this.blackjackPlayers); + } +} diff --git a/src/main/java/domain/trumpcard/TrumpCard.java b/src/main/java/domain/trumpcard/TrumpCard.java new file mode 100644 index 00000000000..fa51ed582a2 --- /dev/null +++ b/src/main/java/domain/trumpcard/TrumpCard.java @@ -0,0 +1,44 @@ +package domain.trumpcard; + +import domain.validator.ObjectsValidator; +import domain.winorlose.BlackjackPoint; + +import java.util.Objects; + +public class TrumpCard { + + private final TrumpCardRank trumpCardRank; + + private final TrumpCardSuit trumpCardSuit; + + public TrumpCard(TrumpCardRank trumpCardRank, TrumpCardSuit trumpCardSuit) { + ObjectsValidator.validateNotNull(trumpCardRank, trumpCardSuit); + this.trumpCardRank = trumpCardRank; + this.trumpCardSuit = trumpCardSuit; + } + + public String fetchKoreanName() { + return this.trumpCardRank.getExpression() + this.trumpCardSuit.getKoreanName(); + } + + public BlackjackPoint fetchMaxBlackjackPoint() { + return new BlackjackPoint(trumpCardRank.fetchMaxValue()); + } + + public BlackjackPoint fetchMinBlackjackPoint() { + return new BlackjackPoint(trumpCardRank.fetchMinValue()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrumpCard trumpCard = (TrumpCard) o; + return trumpCardRank == trumpCard.trumpCardRank && trumpCardSuit == trumpCard.trumpCardSuit; + } + + @Override + public int hashCode() { + return Objects.hash(trumpCardRank, trumpCardSuit); + } +} diff --git a/src/main/java/domain/trumpcard/TrumpCardDeck.java b/src/main/java/domain/trumpcard/TrumpCardDeck.java new file mode 100644 index 00000000000..f735bfce94b --- /dev/null +++ b/src/main/java/domain/trumpcard/TrumpCardDeck.java @@ -0,0 +1,31 @@ +package domain.trumpcard; + +import domain.validator.ObjectsValidator; + +public class TrumpCardDeck { + + private final TrumpCards trumpCards; + + public TrumpCardDeck() { + this.trumpCards = TrumpCards.createAll().shuffle(); + } + + public TrumpCardDeck(TrumpCards trumpCards) { + ObjectsValidator.validateNotNull(trumpCards); + this.trumpCards = trumpCards; + } + + public TrumpCard fetchTopOne() { + return this.trumpCards.fetchTopOne(); + } + + public TrumpCardDeck takeOutTopOne() { + return new TrumpCardDeck( + this.trumpCards.takeOutTopOne() + ); + } + + public int size() { + return this.trumpCards.size(); + } +} diff --git a/src/main/java/domain/trumpcard/TrumpCardRank.java b/src/main/java/domain/trumpcard/TrumpCardRank.java new file mode 100644 index 00000000000..a8071197a67 --- /dev/null +++ b/src/main/java/domain/trumpcard/TrumpCardRank.java @@ -0,0 +1,46 @@ +package domain.trumpcard; + +import java.util.List; + +public enum TrumpCardRank { + ACE(List.of(1, 11), "A"), + TWO(List.of(2), "2"), + THREE(List.of(3), "3"), + FOUR(List.of(4), "4"), + FIVE(List.of(5), "5"), + SIX(List.of(6), "6"), + SEVEN(List.of(7), "7"), + EIGHT(List.of(8), "8"), + NINE(List.of(9), "9"), + TEN(List.of(10), "10"), + KING(List.of(10), "K"), + QUEEN(List.of(10), "Q"), + JACK(List.of(10), "J"); + + + private final List values; + private final String expression; + + TrumpCardRank(List values, String expression) { + this.values = values; + this.expression = expression; + } + + public int fetchMaxValue() { + return this.values.stream() + .mapToInt(x -> x) + .max() + .orElse(0); + } + + public int fetchMinValue() { + return this.values.stream() + .mapToInt(x -> x) + .min() + .orElse(0); + } + + public String getExpression() { + return expression; + } +} diff --git a/src/main/java/domain/trumpcard/TrumpCardSuit.java b/src/main/java/domain/trumpcard/TrumpCardSuit.java new file mode 100644 index 00000000000..efb2f401ab8 --- /dev/null +++ b/src/main/java/domain/trumpcard/TrumpCardSuit.java @@ -0,0 +1,19 @@ +package domain.trumpcard; + +public enum TrumpCardSuit { + + HEARTS("하트"), + DIAMONDS("다이아몬드"), + CLUBS("클로버"), + SPADES("스페이드"); + + private final String koreanName; + + TrumpCardSuit(String koreanName) { + this.koreanName = koreanName; + } + + public String getKoreanName() { + return koreanName; + } +} diff --git a/src/main/java/domain/trumpcard/TrumpCards.java b/src/main/java/domain/trumpcard/TrumpCards.java new file mode 100644 index 00000000000..0670c29cd54 --- /dev/null +++ b/src/main/java/domain/trumpcard/TrumpCards.java @@ -0,0 +1,88 @@ +package domain.trumpcard; + +import domain.validator.TrumpCardsValidator; +import domain.winorlose.BlackjackPoint; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class TrumpCards { + + private final List trumpCards; + + public TrumpCards(List trumpCards) { + TrumpCardsValidator.validate(trumpCards); + this.trumpCards = trumpCards; + } + + public static TrumpCards createAll() { + return new TrumpCards( + Arrays.stream(TrumpCardRank.values()) + .flatMap(rank -> Arrays.stream(TrumpCardSuit.values()) + .map(suit -> new TrumpCard(rank, suit)) + ).collect(Collectors.toList()) + ); + } + + public static TrumpCards createEmptyCards() { + return new TrumpCards( + Collections.emptyList() + ); + } + + public TrumpCards shuffle() { + Collections.shuffle(this.trumpCards); + + return new TrumpCards(List.copyOf(this.trumpCards)); + } + + public TrumpCard fetchTopOne() { + this.validateHasCard(); + + return this.trumpCards.get(0); + } + + public TrumpCards takeOutTopOne() { + this.validateHasCard(); + + return new TrumpCards(this.trumpCards.subList(1, this.size())); + } + + private void validateHasCard() { + if (this.trumpCards.size() == 0) { + throw new IllegalStateException("카드가 없습니다."); + } + } + + public TrumpCards addCard(TrumpCard trumpCard) { + ArrayList trumpCards = new ArrayList<>(this.trumpCards); + trumpCards.add(trumpCard); + + return new TrumpCards(Collections.unmodifiableList(trumpCards)); + } + + public BlackjackPoint totalMaxBlackjackPoint() { + return this.trumpCards.stream() + .map(TrumpCard::fetchMaxBlackjackPoint) + .reduce(new BlackjackPoint(0), BlackjackPoint::sum); + } + + public BlackjackPoint totalMinBlackjackPoint() { + return this.trumpCards.stream() + .map(TrumpCard::fetchMinBlackjackPoint) + .reduce(new BlackjackPoint(0), BlackjackPoint::sum); + } + + public int size() { + return this.trumpCards.size(); + } + + public List fetchKoreanNames() { + return this.trumpCards.stream() + .map(TrumpCard::fetchKoreanName) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/domain/trumpcard/WinOrLose.java b/src/main/java/domain/trumpcard/WinOrLose.java new file mode 100644 index 00000000000..12a8913610a --- /dev/null +++ b/src/main/java/domain/trumpcard/WinOrLose.java @@ -0,0 +1,17 @@ +package domain.trumpcard; + +public enum WinOrLose { + WIN("승"), + LOSE("패"), + TIE("무승부"); + + private final String koreanName; + + WinOrLose(String koreanName) { + this.koreanName = koreanName; + } + + public String getKoreanName() { + return koreanName; + } +} diff --git a/src/main/java/domain/validator/CollectionValidator.java b/src/main/java/domain/validator/CollectionValidator.java new file mode 100644 index 00000000000..a35ea14b957 --- /dev/null +++ b/src/main/java/domain/validator/CollectionValidator.java @@ -0,0 +1,18 @@ +package domain.validator; + +import util.CollectionUtils; + +import java.util.Collection; + +public abstract class CollectionValidator { + + private static final String NOT_EMPTY_MESSAGE = "%s collection은 null이거나 empty이면 안됩니다."; + + public static void validateNotEmpty(Collection collection) { + String className = collection.getClass().getName(); + + if (CollectionUtils.isEmpty(collection)) { + throw new IllegalArgumentException(String.format(NOT_EMPTY_MESSAGE, className)); + } + } +} diff --git a/src/main/java/domain/validator/ObjectsValidator.java b/src/main/java/domain/validator/ObjectsValidator.java new file mode 100644 index 00000000000..064866bead9 --- /dev/null +++ b/src/main/java/domain/validator/ObjectsValidator.java @@ -0,0 +1,22 @@ +package domain.validator; + +import util.ObjectUtils; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class ObjectsValidator { + + private static final String NOT_NULL_MESSAGE = "%s는 null값이면 안됩니다."; + + public static void validateNotNull(Object... objects) { + String classNames = Stream.of(objects) + .map(Object::getClass) + .map(Class::getName) + .collect(Collectors.joining(", ")); + + if (ObjectUtils.hasNull(objects)) { + throw new IllegalArgumentException(String.format(NOT_NULL_MESSAGE, classNames)); + } + } +} diff --git a/src/main/java/domain/validator/TrumpCardsValidator.java b/src/main/java/domain/validator/TrumpCardsValidator.java new file mode 100644 index 00000000000..58dc88d2612 --- /dev/null +++ b/src/main/java/domain/validator/TrumpCardsValidator.java @@ -0,0 +1,23 @@ +package domain.validator; + +import domain.trumpcard.TrumpCard; + +import java.util.HashSet; +import java.util.List; + +public abstract class TrumpCardsValidator { + + public static void validate(List trumpCards) { + validateUnique(trumpCards); + } + + private static void validateUnique(List trumpCards) { + if (!isUnique(trumpCards)) { + throw new IllegalArgumentException("트럼프 카드들은 모두 달라야 합니다."); + } + } + + private static boolean isUnique(List trumpCards) { + return new HashSet<>(trumpCards).size() == trumpCards.size(); + } +} diff --git a/src/main/java/domain/winorlose/BlackjackPoint.java b/src/main/java/domain/winorlose/BlackjackPoint.java new file mode 100644 index 00000000000..365f8d89f07 --- /dev/null +++ b/src/main/java/domain/winorlose/BlackjackPoint.java @@ -0,0 +1,59 @@ +package domain.winorlose; + +public record BlackjackPoint(int point) { + + private static final int RESULT_POINT_THRESHOLD = 21; + + public BlackjackPoint { + this.validateLowerThanZero(point); + } + + public BlackjackPoint sum(BlackjackPoint blackjackPoint) { + return new BlackjackPoint(this.point + blackjackPoint.point); + } + + public boolean isLowerThan(BlackjackPoint blackjackPoint) { + return this.point < blackjackPoint.point; + } + + public BlackjackPoint fetchCloserPointThreshold(BlackjackPoint blackjackPoint) { + int first = Math.abs(RESULT_POINT_THRESHOLD - this.point); + int second = Math.abs(RESULT_POINT_THRESHOLD - blackjackPoint.point); + + if (first < second) { + return this; + } + + return blackjackPoint; + } + + public boolean isWin(BlackjackPoint blackjackPoint) { + return this.fetchDiff() < blackjackPoint.fetchDiff(); + } + + public boolean isLose(BlackjackPoint blackjackPoint) { + return this.fetchDiff() > blackjackPoint.fetchDiff(); + } + + public boolean isTie(BlackjackPoint blackjackPoint) { + return this.fetchDiff() == blackjackPoint.fetchDiff(); + } + + private int fetchDiff() { + return Math.abs(RESULT_POINT_THRESHOLD - this.point); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlackjackPoint that = (BlackjackPoint) o; + return point == that.point; + } + + private void validateLowerThanZero(int point) { + if (point < 0) { + throw new IllegalArgumentException("블랙잭 포인트는 음수일 수 없습니다."); + } + } +} diff --git a/src/main/java/domain/winorlose/BlackjackWinOrLoseDecider.java b/src/main/java/domain/winorlose/BlackjackWinOrLoseDecider.java new file mode 100644 index 00000000000..4b119b0fe5c --- /dev/null +++ b/src/main/java/domain/winorlose/BlackjackWinOrLoseDecider.java @@ -0,0 +1,57 @@ +package domain.winorlose; + +import domain.game.BlackjackGame; +import domain.participant.BlackjackDealer; +import domain.participant.BlackjackPlayer; +import domain.participant.BlackjackPlayers; +import domain.validator.ObjectsValidator; + +import java.util.ArrayList; +import java.util.List; + +import static domain.trumpcard.WinOrLose.*; + +public class BlackjackWinOrLoseDecider { + + private final BlackjackGame blackjackGame; + + public BlackjackWinOrLoseDecider(BlackjackGame blackjackGame) { + ObjectsValidator.validateNotNull(blackjackGame); + this.blackjackGame = blackjackGame; + } + + public FinalWinOrLose decide() { + int dealerWin = 0; + int dealerLose = 0; + int dealerTie = 0; + + List playerWinOrLoses = new ArrayList<>(); + + BlackjackDealer dealer = blackjackGame.findDealer(); + BlackjackPoint dealerPoint = dealer.calculatePoint(); + + BlackjackPlayers players = blackjackGame.findPlayers(); + List blackjackPlayers = players.fetchBlackjackPlayers(); + + for (BlackjackPlayer blackjackPlayer : blackjackPlayers) { + BlackjackPoint playerPoint = blackjackPlayer.calculatePoint(); + + if (dealerPoint.isWin(playerPoint)) { + dealerWin++; + playerWinOrLoses.add(new PlayerWinOrLose(blackjackPlayer.getBlackjackPlayerName(), LOSE)); + } + + if (dealerPoint.isLose(playerPoint)) { + dealerLose++; + playerWinOrLoses.add(new PlayerWinOrLose(blackjackPlayer.getBlackjackPlayerName(), WIN)); + } + + if (dealerPoint.isTie(playerPoint)) { + dealerTie++; + playerWinOrLoses.add(new PlayerWinOrLose(blackjackPlayer.getBlackjackPlayerName(), TIE)); + } + } + + return new FinalWinOrLose(new DealerWinOrLose(dealerWin, dealerLose, dealerTie), playerWinOrLoses); + } +} diff --git a/src/main/java/domain/winorlose/DealerWinOrLose.java b/src/main/java/domain/winorlose/DealerWinOrLose.java new file mode 100644 index 00000000000..204402aeda5 --- /dev/null +++ b/src/main/java/domain/winorlose/DealerWinOrLose.java @@ -0,0 +1,22 @@ +package domain.winorlose; + +public record DealerWinOrLose(int winCount, int loseCount, int tieCount) { + + public DealerWinOrLose { + this.validateLowerThanZero(winCount, loseCount, tieCount); + } + + private void validateLowerThanZero(int winCount, int loseCount, int tieCount) { + if (this.hasLowerThanZero(winCount, loseCount, tieCount)) { + throw new IllegalArgumentException("dealer 승패 수는 음수일 수 없습니다."); + } + } + + private boolean isLowerThanZero(int count) { + return count < 0; + } + + private boolean hasLowerThanZero(int winCount, int loseCount, int tieCount) { + return this.isLowerThanZero(winCount) || this.isLowerThanZero(loseCount) || this.isLowerThanZero(tieCount); + } +} diff --git a/src/main/java/domain/winorlose/FinalWinOrLose.java b/src/main/java/domain/winorlose/FinalWinOrLose.java new file mode 100644 index 00000000000..c979ae59c1f --- /dev/null +++ b/src/main/java/domain/winorlose/FinalWinOrLose.java @@ -0,0 +1,42 @@ +package domain.winorlose; + +import domain.validator.CollectionValidator; +import domain.validator.ObjectsValidator; + +import java.util.List; +import java.util.Objects; + +public class FinalWinOrLose { + + private final DealerWinOrLose dealerWinOrLose; + + private final List playerWinOrLose; + + public FinalWinOrLose(DealerWinOrLose dealerWinOrLose, List playerWinOrLose) { + ObjectsValidator.validateNotNull(dealerWinOrLose); + CollectionValidator.validateNotEmpty(playerWinOrLose); + this.dealerWinOrLose = dealerWinOrLose; + this.playerWinOrLose = playerWinOrLose; + } + + public DealerWinOrLose getDealerWinOrLose() { + return dealerWinOrLose; + } + + public List fetchPlayerWinOrLose() { + return List.copyOf(this.playerWinOrLose); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FinalWinOrLose that = (FinalWinOrLose) o; + return Objects.equals(dealerWinOrLose, that.dealerWinOrLose) && Objects.equals(playerWinOrLose, that.playerWinOrLose); + } + + @Override + public int hashCode() { + return Objects.hash(dealerWinOrLose, playerWinOrLose); + } +} diff --git a/src/main/java/domain/winorlose/PlayerWinOrLose.java b/src/main/java/domain/winorlose/PlayerWinOrLose.java new file mode 100644 index 00000000000..673554c6452 --- /dev/null +++ b/src/main/java/domain/winorlose/PlayerWinOrLose.java @@ -0,0 +1,39 @@ +package domain.winorlose; + +import domain.participant.BlackjackPlayerName; +import domain.trumpcard.WinOrLose; + +import java.util.Objects; + +public class PlayerWinOrLose { + + private final BlackjackPlayerName blackjackPlayerName; + + private final WinOrLose winOrLose; + + public PlayerWinOrLose(BlackjackPlayerName blackjackPlayerName, WinOrLose winOrLose) { + this.blackjackPlayerName = blackjackPlayerName; + this.winOrLose = winOrLose; + } + + public String fetchPlayerName() { + return this.blackjackPlayerName.name(); + } + + public String fetchWinOrLoseKoreanName() { + return this.winOrLose.getKoreanName(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PlayerWinOrLose that = (PlayerWinOrLose) o; + return Objects.equals(blackjackPlayerName, that.blackjackPlayerName) && winOrLose == that.winOrLose; + } + + @Override + public int hashCode() { + return Objects.hash(blackjackPlayerName, winOrLose); + } +} diff --git a/src/main/java/util/CollectionUtils.java b/src/main/java/util/CollectionUtils.java new file mode 100644 index 00000000000..48e5691e23f --- /dev/null +++ b/src/main/java/util/CollectionUtils.java @@ -0,0 +1,11 @@ +package util; + +import java.util.Collection; +import java.util.Objects; + +public abstract class CollectionUtils { + + public static boolean isEmpty(Collection collection) { + return Objects.isNull(collection) || collection.isEmpty(); + } +} diff --git a/src/main/java/util/Console.java b/src/main/java/util/Console.java new file mode 100644 index 00000000000..795be66b39f --- /dev/null +++ b/src/main/java/util/Console.java @@ -0,0 +1,17 @@ +package util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public abstract class Console { + + public static String readLine() { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); + try { + return bufferedReader.readLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/util/ObjectUtils.java b/src/main/java/util/ObjectUtils.java new file mode 100644 index 00000000000..320cec85899 --- /dev/null +++ b/src/main/java/util/ObjectUtils.java @@ -0,0 +1,17 @@ +package util; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public abstract class ObjectUtils { + + public static boolean hasNull(Object... args) { + return Objects.isNull(args) || + (!CollectionUtils.isEmpty(toList(args)) && toList(args).stream().anyMatch(Objects::isNull)); + } + + private static List toList(Object... args) { + return Arrays.stream(args).toList(); + } +} diff --git a/src/main/java/util/PatternUtils.java b/src/main/java/util/PatternUtils.java new file mode 100644 index 00000000000..546b362e36d --- /dev/null +++ b/src/main/java/util/PatternUtils.java @@ -0,0 +1,10 @@ +package util; + +import java.util.Objects; + +public abstract class PatternUtils { + + public static boolean matches(String regex, String str) { + return Objects.nonNull(str) && str.matches(regex); + } +} diff --git a/src/main/java/util/StringUtils.java b/src/main/java/util/StringUtils.java new file mode 100644 index 00000000000..540dd3e0cfb --- /dev/null +++ b/src/main/java/util/StringUtils.java @@ -0,0 +1,20 @@ +package util; + +import java.util.Objects; + +public abstract class StringUtils { + + private static final String EMPTY_STRING = ""; + + public static boolean isEmpty(String str) { + return Objects.isNull(str) || str.isEmpty(); + } + + public static String removeWhiteSpace(String str) { + if (Objects.isNull(str)) { + return EMPTY_STRING; + } + + return str.replace(" ", ""); + } +} diff --git a/src/main/java/view/input/ConsoleInputReader.java b/src/main/java/view/input/ConsoleInputReader.java new file mode 100644 index 00000000000..f45d28c0dbf --- /dev/null +++ b/src/main/java/view/input/ConsoleInputReader.java @@ -0,0 +1,19 @@ +package view.input; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class ConsoleInputReader implements InputReader { + + private final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); + + @Override + public String readLine() { + try { + return bufferedReader.readLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/view/input/ConsoleInputView.java b/src/main/java/view/input/ConsoleInputView.java new file mode 100644 index 00000000000..b01adc7e375 --- /dev/null +++ b/src/main/java/view/input/ConsoleInputView.java @@ -0,0 +1,38 @@ +package view.input; + +import view.input.dto.HandOutPlayerInput; +import view.input.dto.HandOutPlayerRequest; +import view.input.dto.PlayersInput; +import view.output.OutputWriter; + + +public class ConsoleInputView implements InputView { + + private final static String PLAYERS_NAVIGATION = "게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"; + private final static String HAND_OUT_PLAYER_NAVIGATION = "%s는 한장의 카드를 더 받겠습니까? (예는 y, 아니오는 n)"; + + private final InputReader inputReader; + private final OutputWriter outputWriter; + + public ConsoleInputView(InputReader inputReader, OutputWriter outputWriter) { + this.inputReader = inputReader; + this.outputWriter = outputWriter; + } + + @Override + public PlayersInput viewPlayers() { + outputWriter.writeLine(PLAYERS_NAVIGATION); + String input = inputReader.readLine(); + + return PlayersInput.from(input); + } + + @Override + public HandOutPlayerInput viewHandOutCardForPlayer(HandOutPlayerRequest handOutPlayerRequest) { + String playerName = handOutPlayerRequest.getPlayerName(); + outputWriter.writeFormat(HAND_OUT_PLAYER_NAVIGATION, playerName); + String line = inputReader.readLine(); + + return HandOutPlayerInput.from(line); + } +} diff --git a/src/main/java/view/input/InputReader.java b/src/main/java/view/input/InputReader.java new file mode 100644 index 00000000000..b7fb2d2a116 --- /dev/null +++ b/src/main/java/view/input/InputReader.java @@ -0,0 +1,6 @@ +package view.input; + +public interface InputReader { + + String readLine(); +} diff --git a/src/main/java/view/input/InputView.java b/src/main/java/view/input/InputView.java new file mode 100644 index 00000000000..0a08c88ea09 --- /dev/null +++ b/src/main/java/view/input/InputView.java @@ -0,0 +1,13 @@ +package view.input; + +import view.input.dto.HandOutPlayerInput; +import view.input.dto.HandOutPlayerRequest; +import view.input.dto.PlayersInput; + +public interface InputView { + + PlayersInput viewPlayers(); + + HandOutPlayerInput viewHandOutCardForPlayer(HandOutPlayerRequest handOutPlayerRequest); + +} diff --git a/src/main/java/view/input/dto/HandOutPlayerInput.java b/src/main/java/view/input/dto/HandOutPlayerInput.java new file mode 100644 index 00000000000..90a00bada1e --- /dev/null +++ b/src/main/java/view/input/dto/HandOutPlayerInput.java @@ -0,0 +1,26 @@ +package view.input.dto; + +public class HandOutPlayerInput { + + private final boolean handOut; + + public HandOutPlayerInput(boolean handOut) { + this.handOut = handOut; + } + + public static HandOutPlayerInput from(String input) { + if (input.equals("y")) { + return new HandOutPlayerInput(true); + } + + if (input.equals("n")) { + return new HandOutPlayerInput(false); + } + + throw new IllegalArgumentException("카드를 더 받을 건지에 대한 응답은 y아니면 n이어야 합니다."); + } + + public boolean isHandOut() { + return handOut; + } +} diff --git a/src/main/java/view/input/dto/HandOutPlayerRequest.java b/src/main/java/view/input/dto/HandOutPlayerRequest.java new file mode 100644 index 00000000000..8de1c6ff053 --- /dev/null +++ b/src/main/java/view/input/dto/HandOutPlayerRequest.java @@ -0,0 +1,20 @@ +package view.input.dto; + +import domain.participant.BlackjackPlayerName; + +public class HandOutPlayerRequest { + + private final String playerName; + + public HandOutPlayerRequest(String playerName) { + this.playerName = playerName; + } + + public static HandOutPlayerRequest from(BlackjackPlayerName blackjackPlayerName) { + return new HandOutPlayerRequest(blackjackPlayerName.name()); + } + + public String getPlayerName() { + return playerName; + } +} diff --git a/src/main/java/view/input/dto/PlayersInput.java b/src/main/java/view/input/dto/PlayersInput.java new file mode 100644 index 00000000000..98518386b52 --- /dev/null +++ b/src/main/java/view/input/dto/PlayersInput.java @@ -0,0 +1,45 @@ +package view.input.dto; + +import domain.participant.BlackjackPlayerName; +import util.CollectionUtils; +import util.PatternUtils; +import util.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class PlayersInput { + + private static final String USER_INPUT_PATTERN = "^[a-zA-Z]+(\\s*,\\s*[a-zA-Z]+)*$"; + + private final List playerNames; + + private PlayersInput(List playerNames) { + this.validateIsEmpty(playerNames); + this.playerNames = playerNames; + } + + public static PlayersInput from(String input) { + validateMatchesPattern(input); + return new PlayersInput(Arrays.stream(StringUtils.removeWhiteSpace(input).split(",")).collect(Collectors.toList())); + } + + private static void validateMatchesPattern(String input) { + if (!PatternUtils.matches(USER_INPUT_PATTERN, input)) { + throw new IllegalArgumentException("사용자 입력이 올바르지 않습니다."); + } + } + + private void validateIsEmpty(List playerNames) { + if (CollectionUtils.isEmpty(playerNames)) { + throw new IllegalArgumentException("최소 1명의 플레이어가 있어야 합니다."); + } + } + + public List toBlackjackPlayerNames() { + return this.playerNames.stream() + .map(BlackjackPlayerName::new) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/view/output/ConsoleOutputView.java b/src/main/java/view/output/ConsoleOutputView.java new file mode 100644 index 00000000000..6bb77476e0a --- /dev/null +++ b/src/main/java/view/output/ConsoleOutputView.java @@ -0,0 +1,100 @@ +package view.output; + +import view.output.dto.*; + +import java.util.List; + +public class ConsoleOutputView implements OutputView { + + private final static String INITIAL_HAND_OUT_CARDS_COUNT_NAVIGATION = "딜러와 %s에게 %d장을 나누었습니다."; + private final static String INITIAL_HAND_OUT_CARDS_NAVIGATION = "%s카드: %s"; + private final static String HAND_OUT_CARD_FOR_PLAYER_NAVIGATION = "%s카드 : %s"; + private final static String HAND_OUT_DEALER_NAVIGATION = "딜러는 %d이하라 한장의 카드를 더 받았습니다."; + private final static String DEALER_WIN_OR_LOSE_NAVIGATION = "딜러 : %d승 %d무 %d패"; + private final static String PLAYER_WIN_OR_LOSE_NAVIGATION = "%s : %s"; + private final static String BLACKJACK_RESULT_NAVIGATION = "%s 카드: %s - 결과: %d"; + private final OutputWriter outputWriter; + + public ConsoleOutputView(OutputWriter outputWriter) { + this.outputWriter = outputWriter; + } + + @Override + public void viewInitialHandOut(InitialHandOutOutput initialHandOutOutput) { + outputWriter.writeFormat( + INITIAL_HAND_OUT_CARDS_COUNT_NAVIGATION, + initialHandOutOutput.fetchJoinedPlayerNames(), + initialHandOutOutput.getHandOutCount() + ); + outputWriter.write(System.lineSeparator()); + + List initialHandOutParticipantOutput = + initialHandOutOutput.fetchInitialHandOutParticipantOutput(); + + for (InitialHandOutParticipantOutput handOutParticipantOutput : initialHandOutParticipantOutput) { + outputWriter.writeFormat( + INITIAL_HAND_OUT_CARDS_NAVIGATION, + handOutParticipantOutput.getParticipantName(), + handOutParticipantOutput.getJoinedCardNames() + ); + outputWriter.write(System.lineSeparator()); + } + } + + @Override + public void viewHandOutPlayer(HandOutPlayerOutput handOutPlayerOutput) { + outputWriter.writeFormat(HAND_OUT_CARD_FOR_PLAYER_NAVIGATION, handOutPlayerOutput.getPlayerName(), handOutPlayerOutput.fetchJoinedKoreanNames()); + outputWriter.write(System.lineSeparator()); + } + + @Override + public void viewHandOutDealer(HandOutDealerOutput handOutDealerOutput) { + outputWriter.writeFormat(HAND_OUT_DEALER_NAVIGATION, handOutDealerOutput.handOutThreshold()); + outputWriter.write(System.lineSeparator()); + } + + @Override + public void viewBlackjackResult(BlackjackResultOutputs blackjackResultOutput) { + List blackjackResultOutputs = blackjackResultOutput.blackjackResultOutputs(); + blackjackResultOutputs.forEach(output -> { + outputWriter.writeFormat( + BLACKJACK_RESULT_NAVIGATION, + output.getParticipantName(), + output.fetchJoinedCardNames(), + output.getTotalPoint() + ); + + outputWriter.write(System.lineSeparator()); + }); + outputWriter.write(System.lineSeparator()); + } + + @Override + public void viewFinalWinOrLose(FinalWinOrLoseOutput finalWinOrLoseOutput) { + this.printDealerWinOrLose(finalWinOrLoseOutput); + outputWriter.write(System.lineSeparator()); + this.printPlayerWinOrLose(finalWinOrLoseOutput); + } + + private void printDealerWinOrLose(FinalWinOrLoseOutput finalWinOrLoseOutput) { + DealerWinOrLoseOutput dealerWinOrLoseOutput = finalWinOrLoseOutput.getDealerWinOrLoseOutput(); + outputWriter.writeFormat( + DEALER_WIN_OR_LOSE_NAVIGATION, + dealerWinOrLoseOutput.winCount(), + dealerWinOrLoseOutput.tieCount(), + dealerWinOrLoseOutput.loseCount() + ); + } + + private void printPlayerWinOrLose(FinalWinOrLoseOutput finalWinOrLoseOutput) { + List playerWinOrLoses = finalWinOrLoseOutput.fetchPlayerWinOrLoses(); + playerWinOrLoses.forEach(playerWinOrLoseOutput -> { + outputWriter.writeFormat( + PLAYER_WIN_OR_LOSE_NAVIGATION, + playerWinOrLoseOutput.playerName(), + playerWinOrLoseOutput.winOrLose() + ); + outputWriter.write(System.lineSeparator()); + }); + } +} diff --git a/src/main/java/view/output/ConsoleOutputWriter.java b/src/main/java/view/output/ConsoleOutputWriter.java new file mode 100644 index 00000000000..7bd5e74e812 --- /dev/null +++ b/src/main/java/view/output/ConsoleOutputWriter.java @@ -0,0 +1,18 @@ +package view.output; + +public class ConsoleOutputWriter implements OutputWriter { + @Override + public void writeLine(String message) { + System.out.println(message); + } + + @Override + public void write(String message) { + System.out.print(message); + } + + @Override + public void writeFormat(String message, Object... args) { + System.out.printf(message, args); + } +} diff --git a/src/main/java/view/output/OutputView.java b/src/main/java/view/output/OutputView.java new file mode 100644 index 00000000000..0640c1adf43 --- /dev/null +++ b/src/main/java/view/output/OutputView.java @@ -0,0 +1,16 @@ +package view.output; + +import view.output.dto.*; + +public interface OutputView { + + void viewInitialHandOut(InitialHandOutOutput initialHandOutOutput); + + void viewHandOutPlayer(HandOutPlayerOutput handOutPlayerOutput); + + void viewHandOutDealer(HandOutDealerOutput handOutDealerOutput); + + void viewBlackjackResult(BlackjackResultOutputs blackjackResultOutput); + + void viewFinalWinOrLose(FinalWinOrLoseOutput finalWinOrLoseOutput); +} diff --git a/src/main/java/view/output/OutputWriter.java b/src/main/java/view/output/OutputWriter.java new file mode 100644 index 00000000000..86473bba2f4 --- /dev/null +++ b/src/main/java/view/output/OutputWriter.java @@ -0,0 +1,10 @@ +package view.output; + +public interface OutputWriter { + + void writeLine(String message); + + void write(String message); + + void writeFormat(String message, Object... args); +} diff --git a/src/main/java/view/output/dto/BlackjackResultOutput.java b/src/main/java/view/output/dto/BlackjackResultOutput.java new file mode 100644 index 00000000000..af2f1a5213d --- /dev/null +++ b/src/main/java/view/output/dto/BlackjackResultOutput.java @@ -0,0 +1,47 @@ +package view.output.dto; + +import domain.participant.BlackjackParticipant; +import domain.participant.BlackjackPlayer; + +import java.util.List; + +public class BlackjackResultOutput { + + private final String participantName; + + private final List cardKoreanNames; + + private final int totalPoint; + + public BlackjackResultOutput(String participantName, List cardKoreanNames, int totalPoint) { + this.participantName = participantName; + this.cardKoreanNames = cardKoreanNames; + this.totalPoint = totalPoint; + } + + public static BlackjackResultOutput from(BlackjackParticipant blackjackParticipant) { + String participantName = null; + + if (blackjackParticipant.isDealer()) { + participantName = "딜러"; + } + + if (blackjackParticipant.isPlayer()) { + participantName = ((BlackjackPlayer) blackjackParticipant).getPlayerName(); + } + + return new BlackjackResultOutput(participantName, blackjackParticipant.fetchKoreanCardNames(), blackjackParticipant.calculatePoint().point()); + } + + public String getParticipantName() { + return participantName; + } + + public int getTotalPoint() { + return totalPoint; + } + + public String fetchJoinedCardNames() { + return String.join(", ", this.cardKoreanNames); + } +} diff --git a/src/main/java/view/output/dto/BlackjackResultOutputs.java b/src/main/java/view/output/dto/BlackjackResultOutputs.java new file mode 100644 index 00000000000..b022ce6cb5f --- /dev/null +++ b/src/main/java/view/output/dto/BlackjackResultOutputs.java @@ -0,0 +1,17 @@ +package view.output.dto; + +import domain.game.BlackjackGame; + +import java.util.List; +import java.util.stream.Collectors; + +public record BlackjackResultOutputs(List blackjackResultOutputs) { + + public static BlackjackResultOutputs from(BlackjackGame blackjackGame) { + return new BlackjackResultOutputs( + blackjackGame.fetchBlackjackParticipants().stream() + .map(BlackjackResultOutput::from) + .collect(Collectors.toList()) + ); + } +} diff --git a/src/main/java/view/output/dto/DealerWinOrLoseOutput.java b/src/main/java/view/output/dto/DealerWinOrLoseOutput.java new file mode 100644 index 00000000000..806d5bd419e --- /dev/null +++ b/src/main/java/view/output/dto/DealerWinOrLoseOutput.java @@ -0,0 +1,10 @@ +package view.output.dto; + +import domain.winorlose.DealerWinOrLose; + +public record DealerWinOrLoseOutput(int winCount, int loseCount, int tieCount) { + + public static DealerWinOrLoseOutput from(DealerWinOrLose dealerWinOrLose) { + return new DealerWinOrLoseOutput(dealerWinOrLose.winCount(), dealerWinOrLose.loseCount(), dealerWinOrLose.tieCount()); + } +} diff --git a/src/main/java/view/output/dto/FinalWinOrLoseOutput.java b/src/main/java/view/output/dto/FinalWinOrLoseOutput.java new file mode 100644 index 00000000000..4a1489c5f5f --- /dev/null +++ b/src/main/java/view/output/dto/FinalWinOrLoseOutput.java @@ -0,0 +1,35 @@ +package view.output.dto; + +import domain.winorlose.FinalWinOrLose; + +import java.util.List; +import java.util.stream.Collectors; + +public class FinalWinOrLoseOutput { + + private final DealerWinOrLoseOutput dealerWinOrLoseOutput; + + private final List playerWinOrLoses; + + public FinalWinOrLoseOutput(DealerWinOrLoseOutput dealerWinOrLoseOutput, List playerWinOrLoses) { + this.dealerWinOrLoseOutput = dealerWinOrLoseOutput; + this.playerWinOrLoses = playerWinOrLoses; + } + + public static FinalWinOrLoseOutput from(FinalWinOrLose finalWinOrLose) { + return new FinalWinOrLoseOutput( + DealerWinOrLoseOutput.from(finalWinOrLose.getDealerWinOrLose()), + finalWinOrLose.fetchPlayerWinOrLose().stream() + .map(PlayerWinOrLoseOutput::from) + .collect(Collectors.toList()) + ); + } + + public DealerWinOrLoseOutput getDealerWinOrLoseOutput() { + return dealerWinOrLoseOutput; + } + + public List fetchPlayerWinOrLoses() { + return List.copyOf(this.playerWinOrLoses); + } +} diff --git a/src/main/java/view/output/dto/HandOutDealerOutput.java b/src/main/java/view/output/dto/HandOutDealerOutput.java new file mode 100644 index 00000000000..cda062c06d4 --- /dev/null +++ b/src/main/java/view/output/dto/HandOutDealerOutput.java @@ -0,0 +1,5 @@ +package view.output.dto; + +public record HandOutDealerOutput(int handOutThreshold) { + +} diff --git a/src/main/java/view/output/dto/HandOutPlayerOutput.java b/src/main/java/view/output/dto/HandOutPlayerOutput.java new file mode 100644 index 00000000000..d21713730bd --- /dev/null +++ b/src/main/java/view/output/dto/HandOutPlayerOutput.java @@ -0,0 +1,30 @@ +package view.output.dto; + +import domain.participant.BlackjackParticipant; +import domain.participant.BlackjackPlayerName; + +import java.util.List; + +public class HandOutPlayerOutput { + + private final String playerName; + + private final List cardKoreanNames; + + public HandOutPlayerOutput(String playerName, List cardKoreanNames) { + this.playerName = playerName; + this.cardKoreanNames = cardKoreanNames; + } + + public static HandOutPlayerOutput of(BlackjackPlayerName blackjackPlayerName, BlackjackParticipant blackjackParticipant) { + return new HandOutPlayerOutput(blackjackPlayerName.name(), blackjackParticipant.fetchKoreanCardNames()); + } + + public String getPlayerName() { + return playerName; + } + + public String fetchJoinedKoreanNames() { + return String.join(", ", this.cardKoreanNames); + } +} diff --git a/src/main/java/view/output/dto/InitialHandOutOutput.java b/src/main/java/view/output/dto/InitialHandOutOutput.java new file mode 100644 index 00000000000..bb0e76f6e93 --- /dev/null +++ b/src/main/java/view/output/dto/InitialHandOutOutput.java @@ -0,0 +1,43 @@ +package view.output.dto; + +import domain.game.BlackjackGame; +import domain.handouter.HandOutCount; + +import java.util.List; +import java.util.stream.Collectors; + +public class InitialHandOutOutput { + + private final int handOutCount; + + private final List initialHandOutParticipantOutput; + + public InitialHandOutOutput(int handOutCount, List initialHandOutParticipantOutput) { + this.handOutCount = handOutCount; + this.initialHandOutParticipantOutput = initialHandOutParticipantOutput; + } + + public static InitialHandOutOutput of(HandOutCount handOutCount, BlackjackGame blackjackGame) { + return new InitialHandOutOutput( + handOutCount.handOutCount(), + blackjackGame.fetchBlackjackParticipants().stream() + .map(InitialHandOutParticipantOutput::from) + .collect(Collectors.toList()) + ); + } + + public int getHandOutCount() { + return handOutCount; + } + + public List fetchInitialHandOutParticipantOutput() { + return List.copyOf(this.initialHandOutParticipantOutput); + } + + public String fetchJoinedPlayerNames() { + return this.initialHandOutParticipantOutput.stream() + .filter(InitialHandOutParticipantOutput::isPlayer) + .map(InitialHandOutParticipantOutput::getParticipantName) + .collect(Collectors.joining(", ")); + } +} diff --git a/src/main/java/view/output/dto/InitialHandOutParticipantOutput.java b/src/main/java/view/output/dto/InitialHandOutParticipantOutput.java new file mode 100644 index 00000000000..3f3b577572f --- /dev/null +++ b/src/main/java/view/output/dto/InitialHandOutParticipantOutput.java @@ -0,0 +1,50 @@ +package view.output.dto; + +import domain.participant.BlackjackDealer; +import domain.participant.BlackjackParticipant; +import domain.participant.BlackjackPlayer; + +import java.util.List; + +public class InitialHandOutParticipantOutput { + + private final String participantName; + + private final List cards; + + private final boolean player; + + public InitialHandOutParticipantOutput(String participantName, List cards, boolean player) { + this.participantName = participantName; + this.cards = cards; + this.player = player; + } + + public static InitialHandOutParticipantOutput from(BlackjackParticipant blackjackParticipant) { + if (blackjackParticipant instanceof BlackjackDealer) { + return new InitialHandOutParticipantOutput("딜러", blackjackParticipant.fetchKoreanCardNames(), false); + } + + if (blackjackParticipant instanceof BlackjackPlayer) { + return new InitialHandOutParticipantOutput( + ((BlackjackPlayer) blackjackParticipant).getPlayerName(), + blackjackParticipant.fetchKoreanCardNames(), + true + ); + } + + throw new IllegalArgumentException("유효하지 않은 블랙잭 참가자입니다."); + } + + public String getJoinedCardNames() { + return String.join(", ", this.cards); + } + + public String getParticipantName() { + return participantName; + } + + public boolean isPlayer() { + return player; + } +} diff --git a/src/main/java/view/output/dto/PlayerWinOrLoseOutput.java b/src/main/java/view/output/dto/PlayerWinOrLoseOutput.java new file mode 100644 index 00000000000..5a5021c3890 --- /dev/null +++ b/src/main/java/view/output/dto/PlayerWinOrLoseOutput.java @@ -0,0 +1,10 @@ +package view.output.dto; + +import domain.winorlose.PlayerWinOrLose; + +public record PlayerWinOrLoseOutput(String playerName, String winOrLose) { + + public static PlayerWinOrLoseOutput from(PlayerWinOrLose playerWinOrLose) { + return new PlayerWinOrLoseOutput(playerWinOrLose.fetchPlayerName(), playerWinOrLose.fetchWinOrLoseKoreanName()); + } +} diff --git a/src/test/java/domain/BlackjackDealerHandOuterTest.java b/src/test/java/domain/BlackjackDealerHandOuterTest.java new file mode 100644 index 00000000000..29ab34e5c1c --- /dev/null +++ b/src/test/java/domain/BlackjackDealerHandOuterTest.java @@ -0,0 +1,98 @@ +package domain; + +import domain.game.BlackjackGame; +import domain.handouter.BlackjackDealerHandOuter; +import domain.participant.*; +import domain.trumpcard.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import view.output.OutputView; +import view.output.dto.*; + +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BlackjackDealerHandOuterTest { + + static Stream blackjackDealerParameter() { + return Stream.of( + Arguments.arguments( + new BlackjackDealer( + new TrumpCards( + List.of( + new TrumpCard(TrumpCardRank.JACK, TrumpCardSuit.DIAMONDS), + new TrumpCard(TrumpCardRank.SEVEN, TrumpCardSuit.DIAMONDS) + ) + ) + ), 2, 52 + ), + Arguments.arguments( + new BlackjackDealer( + new TrumpCards( + List.of( + new TrumpCard(TrumpCardRank.JACK, TrumpCardSuit.DIAMONDS), + new TrumpCard(TrumpCardRank.SIX, TrumpCardSuit.DIAMONDS) + ) + ) + ), 3, 51 + ) + ); + } + + @DisplayName("블랙잭 점수가 임계값보다 작으면 딜러는 한장 받는다.") + @ParameterizedTest + @MethodSource(value = "blackjackDealerParameter") + void handOut(BlackjackDealer blackjackDealer, int expectCardCount, int expectDeckCount) { + BlackjackPlayers blackjackPlayers = new BlackjackPlayers( + List.of( + new BlackjackPlayer(new BlackjackPlayerName("pobi"), TrumpCards.createEmptyCards()), + new BlackjackPlayer(new BlackjackPlayerName("jason"), TrumpCards.createEmptyCards()) + ) + ); + + BlackjackGame blackjackGame = + new BlackjackGame(BlackjackParticipants.of(blackjackDealer, blackjackPlayers), new TrumpCardDeck()); + BlackjackDealerHandOuter blackjackDealerHandOuter = new BlackjackDealerHandOuter(blackjackGame, new FakeOutputView()); + BlackjackGame actualBlackjackGame = blackjackDealerHandOuter.handOut(); + + TrumpCardDeck trumpCardDeck = actualBlackjackGame.getTrumpCardDeck(); + BlackjackDealer dealer = actualBlackjackGame.findDealer(); + assertAll( + () -> assertEquals(expectCardCount, dealer.fetchCardSize()), + () -> assertEquals(expectDeckCount, trumpCardDeck.size()) + ); + } + + static class FakeOutputView implements OutputView { + + @Override + public void viewInitialHandOut(InitialHandOutOutput initialHandOutOutput) { + + } + + @Override + public void viewHandOutPlayer(HandOutPlayerOutput handOutPlayerOutput) { + + } + + @Override + public void viewHandOutDealer(HandOutDealerOutput handOutDealerOutput) { + + } + + @Override + public void viewBlackjackResult(BlackjackResultOutputs blackjackResultOutput) { + + } + + @Override + public void viewFinalWinOrLose(FinalWinOrLoseOutput finalWinOrLoseOutput) { + + } + } +} \ No newline at end of file diff --git a/src/test/java/domain/BlackjackGameCreatorTest.java b/src/test/java/domain/BlackjackGameCreatorTest.java new file mode 100644 index 00000000000..c5743b11b13 --- /dev/null +++ b/src/test/java/domain/BlackjackGameCreatorTest.java @@ -0,0 +1,71 @@ +package domain; + +import domain.game.BlackjackGame; +import domain.game.BlackjackGameCreator; +import domain.participant.BlackjackPlayer; +import domain.participant.BlackjackPlayers; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import view.input.InputView; +import view.input.dto.HandOutPlayerInput; +import view.input.dto.HandOutPlayerRequest; +import view.input.dto.PlayersInput; + +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BlackjackGameCreatorTest { + + static Stream playerNamesParameter() { + return Stream.of( + Arguments.arguments("pobi,jason"), + Arguments.arguments("pobi, jason") + ); + } + + @ParameterizedTest + @MethodSource(value = "playerNamesParameter") + @DisplayName("플레이어 이름 입력을 받으면 입력한 플레이어들이 참여하는 blackjackGame이 생성된다") + void createTest(String playerNamesInput) { + InputView fakeInputView = new FakeInputView(playerNamesInput); + + BlackjackGameCreator blackjackGameCreator = new BlackjackGameCreator(fakeInputView); + BlackjackGame blackjackGame = blackjackGameCreator.create(); + + BlackjackPlayers players = blackjackGame.findPlayers(); + List blackjackPlayers = players.fetchBlackjackPlayers(); + + assertEquals(blackjackPlayers.size(), playerNamesInput.split(",").length); + for (BlackjackPlayer blackjackPlayer : blackjackPlayers) { + String playerName = blackjackPlayer.getPlayerName(); + + assertTrue(playerNamesInput.contains(playerName)); + } + + } + + + static class FakeInputView implements InputView { + + private final String playerNameInput; + + public FakeInputView(String playerNameInput) { + this.playerNameInput = playerNameInput; + } + + @Override + public PlayersInput viewPlayers() { + return PlayersInput.from(playerNameInput); + } + + @Override + public HandOutPlayerInput viewHandOutCardForPlayer(HandOutPlayerRequest handOutPlayerRequest) { + return null; + } + } +} \ No newline at end of file diff --git a/src/test/java/domain/BlackjackInitialHandOuterTest.java b/src/test/java/domain/BlackjackInitialHandOuterTest.java new file mode 100644 index 00000000000..ee0dcf47a24 --- /dev/null +++ b/src/test/java/domain/BlackjackInitialHandOuterTest.java @@ -0,0 +1,57 @@ +package domain; + +import domain.game.BlackjackGame; +import domain.game.BlackjackGameCreator; +import domain.handouter.BlackjackInitialHandOuter; +import domain.handouter.HandOutCount; +import domain.participant.BlackjackParticipant; +import domain.trumpcard.TrumpCardDeck; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import view.input.InputView; +import view.input.dto.HandOutPlayerInput; +import view.input.dto.HandOutPlayerRequest; +import view.input.dto.PlayersInput; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertAll; + +class BlackjackInitialHandOuterTest { + + @Test + void handOut() { + BlackjackGameCreator blackjackGameCreator = new BlackjackGameCreator(new FakeInputView("pobi, jason")); + BlackjackGame blackjackGame = blackjackGameCreator.create(); + + BlackjackInitialHandOuter blackjackInitialHandOuter = new BlackjackInitialHandOuter(blackjackGame, new HandOutCount(2)); + BlackjackGame handOutGame = blackjackInitialHandOuter.handOut(); + + List participants = handOutGame.fetchBlackjackParticipants(); + TrumpCardDeck trumpCardDeck = handOutGame.getTrumpCardDeck(); + + assertAll( + () -> Assertions.assertThat(trumpCardDeck.size()).isEqualTo(46), + () -> Assertions.assertThat(participants).map(BlackjackParticipant::fetchCardSize).allMatch(size -> size == 2) + ); + } + + static class FakeInputView implements InputView { + + private final String playerNameInput; + + public FakeInputView(String playerNameInput) { + this.playerNameInput = playerNameInput; + } + + @Override + public PlayersInput viewPlayers() { + return PlayersInput.from(playerNameInput); + } + + @Override + public HandOutPlayerInput viewHandOutCardForPlayer(HandOutPlayerRequest handOutPlayerRequest) { + return null; + } + } +} \ No newline at end of file diff --git a/src/test/java/domain/BlackjackPlayersHandOuterTest.java b/src/test/java/domain/BlackjackPlayersHandOuterTest.java new file mode 100644 index 00000000000..25ce8b73533 --- /dev/null +++ b/src/test/java/domain/BlackjackPlayersHandOuterTest.java @@ -0,0 +1,100 @@ +package domain; + +import domain.game.BlackjackGame; +import domain.game.BlackjackGameCreator; +import domain.handouter.BlackjackPlayersHandOuter; +import domain.participant.BlackjackPlayer; +import domain.trumpcard.TrumpCardDeck; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import view.input.InputView; +import view.input.dto.HandOutPlayerInput; +import view.input.dto.HandOutPlayerRequest; +import view.input.dto.PlayersInput; +import view.output.OutputView; +import view.output.dto.*; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertAll; + +class BlackjackPlayersHandOuterTest { + + @Test + void handOut() { + BlackjackGameCreator blackjackGameCreator = new BlackjackGameCreator(new BlackjackInitialHandOuterTest.FakeInputView("pobi, jason")); + BlackjackGame blackjackGame = blackjackGameCreator.create(); + + BlackjackPlayersHandOuter blackjackPlayersHandOuter = + new BlackjackPlayersHandOuter(blackjackGame, new FakeInputView("pobi,jason"), new FakeOutputView()); + + BlackjackGame handOutGame = blackjackPlayersHandOuter.handOut(); + TrumpCardDeck trumpCardDeck = handOutGame.getTrumpCardDeck(); + List blackjackPlayers = handOutGame.fetchBlackjackPlayers(); + + assertAll( + () -> Assertions.assertThat(trumpCardDeck.size()).isEqualTo(50), + () -> Assertions.assertThat(blackjackPlayers).map(BlackjackPlayer::fetchCardSize).allMatch(size -> size == 1) + ); + } + + static class FakeInputView implements InputView { + + private final String playerNameInput; + private int count = 0; + + public FakeInputView(String playerNameInput) { + this.playerNameInput = playerNameInput; + } + + @Override + public PlayersInput viewPlayers() { + return PlayersInput.from(playerNameInput); + } + + @Override + public HandOutPlayerInput viewHandOutCardForPlayer(HandOutPlayerRequest handOutPlayerRequest) { + + if (count % 2 == 0) { + count++; + return new HandOutPlayerInput(true); + } + + if (count % 2 == 1) { + count++; + return new HandOutPlayerInput(false); + } + + return null; + } + } + + static class FakeOutputView implements OutputView { + + @Override + public void viewInitialHandOut(InitialHandOutOutput initialHandOutOutput) { + + } + + @Override + public void viewHandOutPlayer(HandOutPlayerOutput handOutPlayerOutput) { + + } + + @Override + public void viewHandOutDealer(HandOutDealerOutput handOutDealerOutput) { + + } + + @Override + public void viewBlackjackResult(BlackjackResultOutputs blackjackResultOutput) { + + } + + @Override + public void viewFinalWinOrLose(FinalWinOrLoseOutput finalWinOrLoseOutput) { + + } + } + +} \ No newline at end of file diff --git a/src/test/java/domain/BlackjackWinOrLoseDeciderTest.java b/src/test/java/domain/BlackjackWinOrLoseDeciderTest.java new file mode 100644 index 00000000000..dcee76ecc52 --- /dev/null +++ b/src/test/java/domain/BlackjackWinOrLoseDeciderTest.java @@ -0,0 +1,90 @@ +package domain; + +import domain.game.BlackjackGame; +import domain.participant.*; +import domain.trumpcard.*; +import domain.winorlose.BlackjackWinOrLoseDecider; +import domain.winorlose.DealerWinOrLose; +import domain.winorlose.FinalWinOrLose; +import domain.winorlose.PlayerWinOrLose; +import org.junit.jupiter.api.DisplayName; +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.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class BlackjackWinOrLoseDeciderTest { + + static Stream blackjackDealerAndPlayersAndWinOrLoseParameters() { + return Stream.of( + Arguments.arguments( + new BlackjackDealer( + new TrumpCards( + List.of( + new TrumpCard(TrumpCardRank.JACK, TrumpCardSuit.DIAMONDS), + new TrumpCard(TrumpCardRank.NINE, TrumpCardSuit.DIAMONDS) + ) + ) + ), + new BlackjackPlayers( + List.of( + new BlackjackPlayer( + new BlackjackPlayerName("pobi"), + new TrumpCards( + List.of( + new TrumpCard(TrumpCardRank.JACK, TrumpCardSuit.CLUBS), + new TrumpCard(TrumpCardRank.SEVEN, TrumpCardSuit.CLUBS) + ) + ) + ) + ) + ), + new FinalWinOrLose(new DealerWinOrLose(1, 0, 0), + List.of(new PlayerWinOrLose(new BlackjackPlayerName("pobi"), WinOrLose.LOSE)) + ) + ), + Arguments.arguments( + new BlackjackDealer( + new TrumpCards( + List.of( + new TrumpCard(TrumpCardRank.JACK, TrumpCardSuit.DIAMONDS), + new TrumpCard(TrumpCardRank.NINE, TrumpCardSuit.DIAMONDS) + ) + ) + ), + new BlackjackPlayers( + List.of( + new BlackjackPlayer( + new BlackjackPlayerName("pobi"), + new TrumpCards( + List.of( + new TrumpCard(TrumpCardRank.JACK, TrumpCardSuit.CLUBS), + new TrumpCard(TrumpCardRank.KING, TrumpCardSuit.CLUBS) + ) + ) + ) + ) + ), + new FinalWinOrLose(new DealerWinOrLose(0, 1, 0), + List.of(new PlayerWinOrLose(new BlackjackPlayerName("pobi"), WinOrLose.WIN)) + ) + ) + ); + } + + @DisplayName("플레이어가 딜러보다 21에 더 가까우면 승리한다.") + @ParameterizedTest + @MethodSource(value = "blackjackDealerAndPlayersAndWinOrLoseParameters") + void decideTest(BlackjackDealer blackjackDealer, BlackjackPlayers blackjackPlayers, FinalWinOrLose finalWinOrLose) { + BlackjackGame blackjackGame = new BlackjackGame(BlackjackParticipants.of(blackjackDealer, blackjackPlayers), new TrumpCardDeck()); + BlackjackWinOrLoseDecider blackjackWinOrLoseDecider = new BlackjackWinOrLoseDecider(blackjackGame); + + FinalWinOrLose actual = blackjackWinOrLoseDecider.decide(); + + assertThat(actual).isEqualTo(finalWinOrLose); + } +} \ No newline at end of file diff --git a/src/test/java/domain/validator/ObjectsValidatorTest.java b/src/test/java/domain/validator/ObjectsValidatorTest.java new file mode 100644 index 00000000000..945871af5ff --- /dev/null +++ b/src/test/java/domain/validator/ObjectsValidatorTest.java @@ -0,0 +1,35 @@ +package domain.validator; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import util.ObjectUtils; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ObjectsValidatorTest { + + static Stream nullValuesParameter() { + return Stream.of( + Arguments.of(new Object[]{null}, true), + Arguments.of(new Object[]{1, 2, 3, null}, true), + Arguments.of(new Object[]{1, 2, 3}, false), + Arguments.of(new Object[]{}, false), + Arguments.of(null, true) + ); + } + + @ParameterizedTest + @MethodSource("nullValuesParameter") + void hasNull(Object[] input, boolean expected) { + boolean result = ObjectUtils.hasNull(input); + if (expected) { + assertTrue(result); + } else { + assertFalse(result); + } + } +} \ No newline at end of file diff --git a/src/test/java/view/input/dto/PlayersInputTest.java b/src/test/java/view/input/dto/PlayersInputTest.java new file mode 100644 index 00000000000..9178bc4dd2f --- /dev/null +++ b/src/test/java/view/input/dto/PlayersInputTest.java @@ -0,0 +1,27 @@ +package view.input.dto; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class PlayersInputTest { + + static Stream userInputParameter() { + return Stream.of( + Arguments.arguments("pobi//jason"), + Arguments.arguments("pobi-jason"), + Arguments.arguments("pobi;jason") + ); + } + + @ParameterizedTest + @MethodSource("userInputParameter") + @DisplayName("사용자 입력이 pobi,jason같은 형태가 아니면 IllegalArgumentException이 발생한다.") + void test_validate_user_input_pattern(String input) { + Assertions.assertThrows(IllegalArgumentException.class, () -> PlayersInput.from(input)); + } +} \ No newline at end of file