-
Notifications
You must be signed in to change notification settings - Fork 0
[어정윤] 야구 게임 미션 #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 54 commits
65fcc51
5733af7
1e07540
110b5ed
022bd03
65e128b
43d48ad
424022c
a09154e
056bf9f
25852e9
322bce9
1b60b38
61db2df
ba2a2b2
d0db6d4
26475e3
34f7849
24702fe
ee3b873
9165d5e
3e2e256
b8180de
b70d664
345cde2
fc343db
ca2f204
5b50396
dd2c2fa
48cb7f7
24bc5c2
7515fda
0200634
0e98bb9
2fdf749
9cd8ad3
f4d067e
7d22f2b
8a2c5ba
aca6e8c
80eedc1
daf96a5
97fb0d5
0e21ff4
cf98bb7
c7b0548
4734106
d25a2d5
fa66a55
3377e54
415979e
4a0d896
2cbce96
a6520b4
262cf0a
486f562
db89377
d8a9fed
d77ff08
19fa2c1
f671738
d0b3745
88647bf
c470951
885f156
8ae7d10
c8713d9
12ae9c8
eee7e78
38b3ef8
aaa90c8
88990f2
ef38005
39731ce
7e2d57d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # 숫자 야구 | ||
|
|
||
| ## 기능 요구사항 정리 | ||
|
|
||
| - [x] 게임 시작 문구 출력한다. | ||
| - [x] 랜덤 숫자를 생성한다. | ||
| - 숫자는 3자리다. | ||
| - 숫자는 중복되지 않아야 한다. | ||
| - [x] 숫자를 입력받는다. | ||
| - 숫자는 1~9 사이의 3자리 정수다. | ||
| - [x] 입력 값에서 공백을 제거한다. | ||
| - [x] 3자리가 아니면 에러가 발생한다. => `IllegalArgumentException` | ||
| - [x] 정수가 아닌 값을 포함하면 에러가 발생한다. => `IllegalArgumentException` | ||
| - [x] 1~9 범위를 벗어나면 에러가 발생한다. => `IllegalArgumentException` | ||
| - [x] 숫자는 중복되지 않아야 한다. => `IllegalArgumentException` | ||
| - [x] 랜덤 숫자와 입력받은 숫자를 비교한다. | ||
| - 같은 수가 같은 자리에 있으면 스트라이크, 같은 수가 다른 자리에 있으면 볼, 같은 수가 전혀 없으면 낫싱 | ||
| - [x] 비교 결과를 출력한다. | ||
| - 결과는 볼, 스트라이크 개수로 표시한다. | ||
| - [x] 3스트라이크이면 게임을 종료한다. | ||
| - [x] 3스트라이크가 아니면 게임으로 돌아간다. | ||
| - [x] 게임이 종료되면 재진행 여부를 묻는다. | ||
| - [x] 재진행 여부를 입력받는다. | ||
| - 재진행 여부는 1 또는 2인 정수다. | ||
| - [x] 입력 값에서 공백을 제거한다. | ||
| - [x] 정수가 아닌 값을 포함하면 에러가 발생한다. => `IllegalArgumentException` | ||
| - [x] 1~2 범위를 벗어나면 에러가 발생한다. => `IllegalArgumentException` | ||
| - [x] 게임을 재진행 또는 종료한다. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,12 @@ | ||
| package baseball; | ||
|
|
||
| import baseball.service.Game; | ||
| import baseball.service.NumberBaseballGame; | ||
|
|
||
| public class Application { | ||
|
|
||
| public static void main(String[] args) { | ||
| // TODO: 프로그램 구현 | ||
| Game game = new NumberBaseballGame(); | ||
| game.run(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package baseball.domain; | ||
|
|
||
| import java.util.Objects; | ||
|
|
||
| public class GameNumber { | ||
|
|
||
| private static final int MIN_VALUE = 1; | ||
| private static final int MAX_VALUE = 9; | ||
|
|
||
| private final int value; | ||
|
|
||
| public GameNumber(int value) { | ||
| validateRange(value); | ||
| this.value = value; | ||
| } | ||
|
|
||
| public int getValue() { | ||
| return value; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) return true; | ||
| if (o == null || getClass() != o.getClass()) return false; | ||
| GameNumber that = (GameNumber) o; | ||
| return value == that.value; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(value); | ||
| } | ||
|
|
||
| private static void validateRange(int value) { | ||
| if (value < MIN_VALUE || value > MAX_VALUE) { | ||
| throw new IllegalArgumentException("1~9 범위를 벗어나는 숫자가 포함되어 있습니다."); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| package baseball.domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
|
|
||
| public class GameNumbers { | ||
|
|
||
| private static final int THE_NUMBER_OF_GAME_NUMBER = 3; | ||
| private static final int BALL = 0; | ||
| private static final int STRIKE = 1; | ||
| private static final int NOTHING = 2; | ||
|
||
|
|
||
| private final List<GameNumber> gameNumbers; | ||
|
|
||
| public GameNumbers(List<GameNumber> gameNumbers) { | ||
| validateTheNumberOf(gameNumbers); | ||
| validateDuplication(gameNumbers); | ||
| this.gameNumbers = new ArrayList<>(gameNumbers); | ||
| } | ||
|
|
||
| public int[] getComparingResult(List<Integer> computer) { | ||
| int[] comparingResult = new int[3]; | ||
| for (int order = 0; order < 3; order++) { | ||
| int number = gameNumbers.get(order).getValue(); | ||
| if (isBall(computer, order, number)) { | ||
| comparingResult[BALL]++; | ||
| } else if (isStrike(computer, order, number)) { | ||
| comparingResult[STRIKE]++; | ||
| } else { | ||
| comparingResult[NOTHING]++; | ||
| } | ||
| } | ||
| return comparingResult; | ||
| } | ||
|
|
||
| private static void validateTheNumberOf(List<GameNumber> gameNumbers) { | ||
| if (gameNumbers.size() != THE_NUMBER_OF_GAME_NUMBER) { | ||
| throw new IllegalArgumentException("3자리 값이 아닙니다."); | ||
| } | ||
| } | ||
|
|
||
| private void validateDuplication(List<GameNumber> gameNumbers) { | ||
| if (gameNumbers.size() != new HashSet<>(gameNumbers).size()) { | ||
| throw new IllegalArgumentException("중복되는 값이 포함되어 있습니다."); | ||
| } | ||
| } | ||
|
|
||
| private boolean isStrike(List<Integer> computer, int order, int number) { | ||
| return computer.contains(number) && computer.indexOf(number) == order; | ||
| } | ||
|
|
||
| private boolean isBall(List<Integer> computer, int order, int number) { | ||
| return computer.contains(number) && computer.indexOf(number) != order; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package baseball.domain; | ||
|
|
||
| public class GameResult { | ||
|
|
||
| private static final int THE_NUMBER_OF_GAME_NUMBER = 3; | ||
| private static final int BALL = 0; | ||
| private static final int STRIKE = 1; | ||
| private static final int NOTHING = 2; | ||
| private static final String GAME_END_CONDITION = THE_NUMBER_OF_GAME_NUMBER + "스트라이크"; | ||
|
|
||
| private final String value; | ||
|
|
||
| private GameResult(String value) { | ||
| this.value = value; | ||
| } | ||
|
|
||
| public static GameResult from(int[] comparingResult) { | ||
| if (comparingResult[NOTHING] == THE_NUMBER_OF_GAME_NUMBER) { | ||
| return new GameResult("낫싱"); | ||
| } | ||
| StringBuilder result = new StringBuilder(); | ||
| if (comparingResult[BALL] != 0) { | ||
| result.append(comparingResult[BALL]).append("볼 "); | ||
| } | ||
| if (comparingResult[STRIKE] != 0) { | ||
| result.append(comparingResult[STRIKE]).append("스트라이크"); | ||
| } | ||
| if (comparingResult[STRIKE] == THE_NUMBER_OF_GAME_NUMBER) { | ||
| result.append("\n").append(THE_NUMBER_OF_GAME_NUMBER).append("개의 숫자를 모두 맞히셨습니다! 게임 종료"); | ||
| } | ||
| return new GameResult(result.toString()); | ||
| } | ||
|
|
||
| public String getValue() { | ||
| return value; | ||
| } | ||
|
|
||
| public boolean isEnd() { | ||
| return value.contains(GAME_END_CONDITION); | ||
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package baseball.domain; | ||
|
|
||
| public class GameStatus { | ||
|
|
||
| private static final int RESTART = 1; | ||
| private static final int END = 2; | ||
|
|
||
| private final int status; | ||
|
|
||
| public GameStatus(int status) { | ||
| validateRange(status); | ||
| this.status = status; | ||
| } | ||
|
|
||
| public boolean isRestart() { | ||
| return status == RESTART; | ||
| } | ||
|
|
||
| private static void validateRange(int status) { | ||
| if (status != RESTART && status != END) { | ||
| throw new IllegalArgumentException("1 또는 2 이외의 숫자가 포함되어 있습니다."); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package baseball.service; | ||
|
|
||
| public interface Game { | ||
|
|
||
| void run(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package baseball.service; | ||
|
|
||
| import baseball.domain.GameNumbers; | ||
| import baseball.domain.GameResult; | ||
| import baseball.domain.GameStatus; | ||
| import baseball.util.*; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class NumberBaseballGame implements Game { | ||
|
|
||
| public void run() { | ||
| Output.printStartMessage(); | ||
| start(); | ||
| } | ||
|
|
||
| private void start() { | ||
| play(RandomNumberGenerator.generate()); | ||
| askPlayAgain(); | ||
| } | ||
|
|
||
| private void play(List<Integer> computer) { | ||
| String input = Input.readGameNumber(); | ||
| GameNumbers gameNumbers = GameNumberGenerator.generate(input); | ||
| GameResult gameResult = GameResult.from(gameNumbers.getComparingResult(computer)); | ||
| Output.printResult(gameResult.getValue()); | ||
| if (gameResult.isEnd()) { | ||
| return; | ||
| } | ||
| play(computer); | ||
| } | ||
|
||
|
|
||
| private void askPlayAgain() { | ||
| GameStatus gameStatus = new GameStatus(Convertor.toInteger(Input.readGameStatus())); | ||
| if (gameStatus.isRestart()) { | ||
| start(); | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+33
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도 위와 비슷한 맥락으로,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 부분은 '게임 실행'과 '게임 재실행 여부 질문'의 역할을 분리했기 때문에 재귀 사용 방식을 유지했습니다! |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package baseball.util; | ||
|
|
||
| public class Convertor { | ||
|
|
||
| private Convertor() { | ||
| } | ||
|
|
||
| public static int toInteger(String value) { | ||
| try { | ||
| return Integer.parseInt(value); | ||
| } catch (NumberFormatException e) { | ||
| throw new IllegalArgumentException("정수가 아닌 값이 포함되어 있습니다."); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package baseball.util; | ||
|
|
||
| import baseball.domain.GameNumber; | ||
| import baseball.domain.GameNumbers; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public class GameNumberGenerator { | ||
|
|
||
| private static final String DELIMITER = ""; | ||
|
|
||
| private GameNumberGenerator() { | ||
| } | ||
|
|
||
| public static GameNumbers generate(String input) { | ||
| List<GameNumber> gameNumbers = Arrays.stream(input.split(DELIMITER)) | ||
| .map(each -> new GameNumber(Convertor.toInteger(each))) | ||
| .collect(Collectors.toList()); | ||
| return new GameNumbers(gameNumbers); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package baseball.util; | ||
|
|
||
| import camp.nextstep.edu.missionutils.Console; | ||
|
|
||
| public class Input { | ||
|
|
||
| private static final String BLANK = " "; | ||
| private static final String DELETE = ""; | ||
|
|
||
| private Input() { | ||
| } | ||
|
|
||
| public static String readGameNumber() { | ||
| System.out.print("숫자를 입력해주세요 : "); | ||
| return removeBlank(Console.readLine()); | ||
| } | ||
|
|
||
| public static String readGameStatus() { | ||
| System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); | ||
| return removeBlank(Console.readLine()); | ||
| } | ||
|
|
||
| private static String removeBlank(String input) { | ||
| return input.replaceAll(BLANK, DELETE); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 사용자가 1 23 이런식으로도 입력하는 경우를 방지하셨네요 |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package baseball.util; | ||
|
|
||
| public class Output { | ||
|
|
||
| private Output() { | ||
| } | ||
|
|
||
| public static void printStartMessage() { | ||
| System.out.println("숫자 야구 게임을 시작합니다."); | ||
| } | ||
|
|
||
| public static void printResult(String result) { | ||
| System.out.println(result); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package baseball.util; | ||
|
|
||
| import camp.nextstep.edu.missionutils.Randoms; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| public class RandomNumberGenerator { | ||
|
|
||
| private static final int THE_NUMBER_OF_RANDOM_NUMBER = 3; | ||
|
|
||
| private RandomNumberGenerator() { | ||
| } | ||
|
|
||
| public static List<Integer> generate() { | ||
| List<Integer> computer = new ArrayList<>(); | ||
| while (computer.size() < THE_NUMBER_OF_RANDOM_NUMBER) { | ||
| int randomNumber = Randoms.pickNumberInRange(1, 9); | ||
|
||
| if (!computer.contains(randomNumber)) { | ||
| computer.add(randomNumber); | ||
| } | ||
| } | ||
| return computer; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package baseball.util; | ||
|
|
||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.ValueSource; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThatCode; | ||
| import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
|
||
| public class GameNumberGeneratorTest { | ||
|
|
||
| @DisplayName("형식 외 입력 시 예외 발생") | ||
| @ParameterizedTest | ||
| @ValueSource(strings = {"1234", "12-", "120", "122"}) | ||
| void 잘못된_입력(String input) { | ||
| assertThatThrownBy(() -> GameNumberGenerator.generate(input)) | ||
| .isInstanceOf(IllegalArgumentException.class); | ||
| } | ||
|
|
||
| @DisplayName("3자리 아닌 수 입력 시 통과") | ||
| @ParameterizedTest | ||
| @ValueSource(strings = {"123", "483", "679"}) | ||
| void 올바른_입력(String input) { | ||
| assertThatCode(() -> GameNumberGenerator.generate(input)) | ||
| .doesNotThrowAnyException(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 안 지켜지고 있는 것 같아요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 조건은 랜덤으로 생성되는 숫자에만 적용했던 조건입니다.
사용자 입력 숫자는 중복체크를 안하고 있었는데, 필요할 것 같아 추가 구현했습니다!