diff --git a/README.md b/README.md index e7af80b4ff..df56327130 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,6 @@ while (computer.size() < 3) { ## ✏️ 과제 진행 요구 사항 -- 미션은 [java-baseball-6](https://github.com/woowacourse-precourse/java-baseball-6) 저장소를 Fork & Clone해 시작한다. +- 미션은 [java-baseball_oop-6](https://github.com/woowacourse-precourse/java-baseball-6) 저장소를 Fork & Clone해 시작한다. - **기능을 구현하기 전 `docs/README.md`에 구현할 기능 목록을 정리**해 추가한다. - 과제 진행 및 제출 방법은 [프리코스 과제 제출](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 문서를 참고한다. diff --git a/build.gradle b/build.gradle index 080ed8c12e..76352c0838 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ repositories { dependencies { implementation 'com.github.woowacourse-projects:mission-utils:1.1.0' + implementation 'org.projectlombok:lombok:1.18.22' } java { diff --git a/docs/README.md b/docs/README.md index e69de29bb2..f7f7291b69 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,48 @@ +# 요구사항 분석 +- 컴퓨터가 수를 선택한다. + - 수는 3자리 숫자이고, 랜덤으로 정해진다. +- 정답을 맞출 때 까지 반복한다. +- 컴퓨터는 입력한 숫자에 대한 결과를 출력한다. +- 컴퓨터수와 사용자의 수를 비교해 위치는 틀리지만 수가 맞으면 볼을 출력 +- 컴퓨터수와 사용자의 수를 비교해 위치도 맞고 수가 맞으면 스트라이크를 출력 +- 컴퓨터수와 사용자의 수를 비교해 위치도, 수도 맞은게 없다면 낫싱을 출력 +- 사용자가 수를 입력할 수 있다. +- 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생 후 종료 +- 컴퓨터의 수를 맞추면 게임 종료. 종료 후 한번 더 할지, 끝낼지 입력받는다. + +# 역할을 나눈다. + +## 답을 생성하는 역할 +- 컴퓨터가 수를 선택한다. + - 수는 서로 다른 3자리 숫자이고, 랜덤으로 정해진다. + +## 정답을 입력을 할 수 있는 역할 +- 사용자가 수를 입력할 수 있다. + - 수는 서로 다른 3자리 숫자이다. (검증필요) +- 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생 후 종료 + +## 정답이 맞는지 판단하고 힌트를 주는 역할 +- 컴퓨터수와 사용자의 수를 비교해 위치는 틀리지만 수가 맞으면 볼을 출력 +- 컴퓨터수와 사용자의 수를 비교해 위치도 맞고 수가 맞으면 스트라이크를 출력 +- 컴퓨터수와 사용자의 수를 비교해 위치도, 수도 맞은게 없다면 낫싱을 출력 + +## 게임을 제어하는 역할 +- 정답을 맞출 때 까지 반복한다. +- 정답을 맞추면 게임 종료. + - 게임을 종료 후 한번 더 할지, 끝낼지 입력받는다. + +# 도메인 설계 및 기능 목록 + +## BaseBallGame +- 게임을 제어하는 역할 + +## Hitter +- 답을 생성하는 역할 + (왜 컴퓨터가 Hitter냐? 플레이어가 포수에게 주는 볼을 잡아서 스트라이크를 면해야되기 때문에 타자입니다.) +## Pitcher +- 정답을 입력을 할 수 있는 역할 + (왜 플레이어가 Pitcher냐? 3스트라이크 즉, 3번 타자가 못치게끔 포수에게 잘 던져줘야 하기 때문에 투수입니다.) + +## Umpire +- 정답이 맞는지 판단하고 힌트를 주는 역할 + diff --git "a/docs/\353\217\204\353\251\224\354\235\270\354\204\244\352\263\204.drawio" "b/docs/\353\217\204\353\251\224\354\235\270\354\204\244\352\263\204.drawio" new file mode 100644 index 0000000000..9c24293507 --- /dev/null +++ "b/docs/\353\217\204\353\251\224\354\235\270\354\204\244\352\263\204.drawio" @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java deleted file mode 100644 index dd95a34214..0000000000 --- a/src/main/java/baseball/Application.java +++ /dev/null @@ -1,7 +0,0 @@ -package baseball; - -public class Application { - public static void main(String[] args) { - // TODO: 프로그램 구현 - } -} diff --git a/src/main/java/baseball_oop/Application.java b/src/main/java/baseball_oop/Application.java new file mode 100644 index 0000000000..0954a25088 --- /dev/null +++ b/src/main/java/baseball_oop/Application.java @@ -0,0 +1,19 @@ +package baseball_oop; + +import baseball_oop.domain.game.BaseBallGame; +import baseball_oop.domain.game.Game; +import baseball_oop.domain.participant.computer.Hitter; +import baseball_oop.domain.participant.player.Pitcher; +import baseball_oop.domain.participant.judgment.Umpire; + +public class Application { + + public static void main(String[] args) { + Game game = new BaseBallGame( + new Hitter(), + new Pitcher(), + new Umpire() + ); + game.start(); + } +} diff --git a/src/main/java/baseball_oop/domain/game/BaseBallGame.java b/src/main/java/baseball_oop/domain/game/BaseBallGame.java new file mode 100644 index 0000000000..923e66d78b --- /dev/null +++ b/src/main/java/baseball_oop/domain/game/BaseBallGame.java @@ -0,0 +1,114 @@ +package baseball_oop.domain.game; + +import baseball_oop.enums.ReplayOrNot; +import baseball_oop.vo.Answer; +import baseball_oop.domain.participant.computer.Computer; +import baseball_oop.domain.participant.judgment.Judgment; +import baseball_oop.domain.participant.player.Player; +import baseball_oop.vo.Result; + +import static camp.nextstep.edu.missionutils.Console.readLine; + +public class BaseBallGame implements Game { + private final Computer computer; + private final Judgment judgment; + private final Player player; + + public BaseBallGame(Computer computer, Player player, Judgment judgment) { + this.computer = computer; + this.judgment = judgment; + this.player = player; + } + @Override + public void start() { + String command; + PrintMessage.printGameStart(); + do { + play(); + } while (askReplay()); + } + + private void play() { + Answer answer = computer.generateAnswer(); + Answer input; + Result result = null; + while (!gameEnd(result)) { + input = selectInputToPlayer(); + printHint(result = judgment.judge(answer, input)); + } + } + + private static boolean askReplay() { + PrintMessage.printGameReplayOrNot(); + return readLine().equals(ReplayOrNot.REPLAY.getCode()); + } + + private static boolean gameEnd(Result result) { + if (result == null) { + return false; + } + + boolean isEnd = result.isWin(); + if (isEnd) { + PrintMessage.printWinAndEnd(); + } + return isEnd; + } + + private Answer selectInputToPlayer() { + PrintMessage.printRequestInputNumber(); + return player.generateAnswer(); + } + + private void printHint(Result result) { // TODO 코드리팩토링 필요 + if (result == null) { + return; + } + int strikeCount = result.getStrikeCount(); + int ballCount = result.getBallCount(); + + if (result.getBallCount() == 0 && result.getStrikeCount() == 0) { + PrintMessage.printResultNothing(); + return; + } + if (strikeCount != 0 && ballCount == 0) { + PrintMessage.printMessage(strikeCount + PrintMessage.STRIKE); + } + if (strikeCount == 0 && ballCount != 0) { + PrintMessage.printMessage(ballCount + PrintMessage.BALL); + } + if (strikeCount != 0 && ballCount != 0) { + PrintMessage.printMessage(ballCount + PrintMessage.BALL + " " + strikeCount + PrintMessage.STRIKE); + } + } + + private static class PrintMessage { + private final static String START = "숫자 야구 게임을 시작합니다."; + private final static String INPUT_NUMBER = "숫자를 입력해주세요 : "; + private final static String WIN = "3개의 숫자를 모두 맞히셨습니다! 게임 종료"; + private final static String PLAY_NEXT_GAME_OR_NOT = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + private final static String NOTHING = "낫싱"; + private final static String BALL = "볼"; + private final static String STRIKE = "스트라이크"; + public static void printRequestInputNumber() { + System.out.print(INPUT_NUMBER); + } + public static void printGameStart() { + System.out.println(START); + } + public static void printWinAndEnd() { + System.out.println(WIN); + } + public static void printGameReplayOrNot() { + System.out.println(PLAY_NEXT_GAME_OR_NOT); + } + + public static void printResultNothing() { + System.out.println(NOTHING); + } + + public static void printMessage(String message) { + System.out.println(message); + } + } +} diff --git a/src/main/java/baseball_oop/domain/game/Game.java b/src/main/java/baseball_oop/domain/game/Game.java new file mode 100644 index 0000000000..2309d09612 --- /dev/null +++ b/src/main/java/baseball_oop/domain/game/Game.java @@ -0,0 +1,5 @@ +package baseball_oop.domain.game; + +public interface Game { + void start(); +} diff --git a/src/main/java/baseball_oop/domain/participant/AnswerGeneratable.java b/src/main/java/baseball_oop/domain/participant/AnswerGeneratable.java new file mode 100644 index 0000000000..67da4a6a66 --- /dev/null +++ b/src/main/java/baseball_oop/domain/participant/AnswerGeneratable.java @@ -0,0 +1,7 @@ +package baseball_oop.domain.participant; + +import baseball_oop.vo.Answer; + +public interface AnswerGeneratable { + Answer generateAnswer(); +} diff --git a/src/main/java/baseball_oop/domain/participant/computer/Computer.java b/src/main/java/baseball_oop/domain/participant/computer/Computer.java new file mode 100644 index 0000000000..4040ace6f3 --- /dev/null +++ b/src/main/java/baseball_oop/domain/participant/computer/Computer.java @@ -0,0 +1,6 @@ +package baseball_oop.domain.participant.computer; + +import baseball_oop.domain.participant.AnswerGeneratable; + +public interface Computer extends AnswerGeneratable { +} diff --git a/src/main/java/baseball_oop/domain/participant/computer/Hitter.java b/src/main/java/baseball_oop/domain/participant/computer/Hitter.java new file mode 100644 index 0000000000..d24cf23efa --- /dev/null +++ b/src/main/java/baseball_oop/domain/participant/computer/Hitter.java @@ -0,0 +1,11 @@ +package baseball_oop.domain.participant.computer; + +import baseball_oop.vo.Answer; + +public class Hitter implements Computer { + + @Override + public Answer generateAnswer() { + return new Answer(); + } +} diff --git a/src/main/java/baseball_oop/domain/participant/judgment/Judgment.java b/src/main/java/baseball_oop/domain/participant/judgment/Judgment.java new file mode 100644 index 0000000000..79d4eb887c --- /dev/null +++ b/src/main/java/baseball_oop/domain/participant/judgment/Judgment.java @@ -0,0 +1,8 @@ +package baseball_oop.domain.participant.judgment; + +import baseball_oop.vo.Answer; +import baseball_oop.vo.Result; + +public interface Judgment { + Result judge(Answer answer, Answer input); +} diff --git a/src/main/java/baseball_oop/domain/participant/judgment/Umpire.java b/src/main/java/baseball_oop/domain/participant/judgment/Umpire.java new file mode 100644 index 0000000000..32a6295396 --- /dev/null +++ b/src/main/java/baseball_oop/domain/participant/judgment/Umpire.java @@ -0,0 +1,15 @@ +package baseball_oop.domain.participant.judgment; + +import baseball_oop.vo.Answer; +import baseball_oop.vo.Result; + +public class Umpire implements Judgment { + @Override + public Result judge(Answer answer, Answer input) { + int strikeCount = Answer.countSameElementAndPosition(answer.getValues(), input.getValues()); + int ballCount = Answer.countContainElement(answer.getValues(), input.getValues()) - strikeCount; + + return new Result(strikeCount, ballCount); + } + +} diff --git a/src/main/java/baseball_oop/domain/participant/player/Pitcher.java b/src/main/java/baseball_oop/domain/participant/player/Pitcher.java new file mode 100644 index 0000000000..05038e9169 --- /dev/null +++ b/src/main/java/baseball_oop/domain/participant/player/Pitcher.java @@ -0,0 +1,10 @@ +package baseball_oop.domain.participant.player; + +import baseball_oop.vo.Answer; +import static camp.nextstep.edu.missionutils.Console.readLine; +public class Pitcher implements Player { + @Override + public Answer generateAnswer() { + return new Answer(readLine()); + } +} diff --git a/src/main/java/baseball_oop/domain/participant/player/Player.java b/src/main/java/baseball_oop/domain/participant/player/Player.java new file mode 100644 index 0000000000..4e7de6ab38 --- /dev/null +++ b/src/main/java/baseball_oop/domain/participant/player/Player.java @@ -0,0 +1,7 @@ +package baseball_oop.domain.participant.player; + +import baseball_oop.domain.participant.AnswerGeneratable; + +public interface Player extends AnswerGeneratable { + +} diff --git a/src/main/java/baseball_oop/enums/ExceptionMessage.java b/src/main/java/baseball_oop/enums/ExceptionMessage.java new file mode 100644 index 0000000000..c768746fe8 --- /dev/null +++ b/src/main/java/baseball_oop/enums/ExceptionMessage.java @@ -0,0 +1,17 @@ +package baseball_oop.enums; + +public enum ExceptionMessage { + WRONG_INPUT_DEFAULT("잘못된 입력입니다."), + WRONG_INPUT_NOT_THREE_DIGITS("3자리만 입력 할 수 있습니다."), + WRONG_INPUT_NOT_NUMBER("숫자가 아닙니다."), + WRONG_INPUT_DUPLICATE_VALUE("숫자가 중복되어 있습니다."); + String message; + + ExceptionMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/baseball_oop/enums/ReplayOrNot.java b/src/main/java/baseball_oop/enums/ReplayOrNot.java new file mode 100644 index 0000000000..028d268888 --- /dev/null +++ b/src/main/java/baseball_oop/enums/ReplayOrNot.java @@ -0,0 +1,16 @@ +package baseball_oop.enums; + +public enum ReplayOrNot { + REPLAY("1"), + END("2"); + + public final String code; + + ReplayOrNot(String code) { + this.code = code; + } + + public String getCode() { + return code; + } +} diff --git a/src/main/java/baseball_oop/vo/Answer.java b/src/main/java/baseball_oop/vo/Answer.java new file mode 100644 index 0000000000..3623101b49 --- /dev/null +++ b/src/main/java/baseball_oop/vo/Answer.java @@ -0,0 +1,97 @@ +package baseball_oop.vo; + +import baseball_oop.enums.ExceptionMessage; + +import java.util.*; +import java.util.stream.Collectors; + +import static camp.nextstep.edu.missionutils.Randoms.pickNumberInRange; + +public class Answer { + List values = new ArrayList<>(); + private final static int LENGTH_LIMIT = 3; + + public Answer() { + Set set = new HashSet<>(); + while (set.size() < LENGTH_LIMIT) { + set.add(pickNumberInRange(1, 9)); + } + this.values = set.stream().toList(); + } + + + public Answer(String input) { + checkValidation(input); + this.values = Arrays.stream(input.split("")) + .map(Integer::parseInt) + .collect(Collectors.toList()); + } + + private static void checkValidation(String value) { + checkLength(value); + checkDuplicate(value); + checkNumber(value); + } + + private static void checkDuplicate(String value) { + if (hasDuplicate(value)) { + throw new IllegalArgumentException(ExceptionMessage.WRONG_INPUT_DUPLICATE_VALUE.getMessage()); + } + } + + private static void checkLength(String value) { + if (!isLimitedLength(value)) { + throw new IllegalArgumentException(ExceptionMessage.WRONG_INPUT_NOT_THREE_DIGITS.getMessage()); + } + } + + private static void checkNumber(String value) { + if (!isNumber(value)) { + throw new IllegalArgumentException(ExceptionMessage.WRONG_INPUT_NOT_NUMBER.getMessage()); + } + } + + private static boolean isLimitedLength(String value) { + return value.length() == LENGTH_LIMIT; + } + + private static boolean hasDuplicate(String value) { + Set set = new HashSet<>(); + for (String str : value.split("")) { + set.add(str); + } + return set.size() != value.length(); + } + + private static boolean isNumber(String value) { + return value != null && value.matches("[-+]?\\d*\\.?\\d+"); + } + + public static int countSameElementAndPosition(List answerValues, List inputValues) { + int count = 0; + int index = 0; + for (Integer value : answerValues) { + if (value.equals(inputValues.get(index))) { + count++; + } + index++; + } + return count; + } + + public static int countContainElement(List answerValues, List inputValues) { + int count = 0; + int index = 0; + for (Integer value : answerValues) { + if (inputValues.contains(value)) { + count++; + } + index++; + } + return count; + } + + public List getValues() { + return this.values; + } +} diff --git a/src/main/java/baseball_oop/vo/Result.java b/src/main/java/baseball_oop/vo/Result.java new file mode 100644 index 0000000000..f6673e0803 --- /dev/null +++ b/src/main/java/baseball_oop/vo/Result.java @@ -0,0 +1,26 @@ +package baseball_oop.vo; + +public class Result { + private int strikeCount; + private int ballCount; + + private final int LENGTH_LIMIT = 3; + + + public Result(int strikeCount, int ballCount) { + this.strikeCount = strikeCount; + this.ballCount = ballCount; + } + + public boolean isWin() { + return this.strikeCount == LENGTH_LIMIT; + } + + public int getStrikeCount() { + return strikeCount; + } + + public int getBallCount() { + return ballCount; + } +} diff --git a/src/test/java/baseball/ApplicationTest.java b/src/test/java/baseball_oop/ApplicationTest.java similarity index 87% rename from src/test/java/baseball/ApplicationTest.java rename to src/test/java/baseball_oop/ApplicationTest.java index 3fa29fa67b..a044acc8fc 100644 --- a/src/test/java/baseball/ApplicationTest.java +++ b/src/test/java/baseball_oop/ApplicationTest.java @@ -1,4 +1,4 @@ -package baseball; +package baseball_oop; import camp.nextstep.edu.missionutils.test.NsTest; import org.junit.jupiter.api.Test; @@ -14,7 +14,7 @@ class ApplicationTest extends NsTest { assertRandomNumberInRangeTest( () -> { run("246", "135", "1", "597", "589", "2"); - assertThat(output()).contains("낫싱", "3스트라이크", "1볼 1스트라이크", "3스트라이크", "게임 종료"); + assertThat(output()).contains("낫싱", "3스트라이크", "1볼 1스트라이크", "3스트라이크", "3개의 숫자를 모두 맞히셨습니다! 게임 종료"); }, 1, 3, 5, 5, 8, 9 );