-
Notifications
You must be signed in to change notification settings - Fork 0
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
[1주차] 변해빈 #1
base: main
Are you sure you want to change the base?
[1주차] 변해빈 #1
Changes from 45 commits
5e5ff7e
285df98
87fa078
403e53a
cd65c41
29a7d86
d5e2f95
d01607d
45e0473
1447d6f
02ff445
3cd75ee
f65861b
641d1d3
dbcdc54
2bd8d97
8306983
f837542
daad19e
e16b264
058853e
c672022
dd8cba7
990b7c7
f5fa1bc
3e84307
6a46e8c
04d9229
5e9b6c2
8962557
b84f780
7acc36b
ba6bd2c
b1dd4e1
fa90c35
3f7bb2e
7f7d7e8
ac8b5bd
4a03375
febd594
1999b2c
99aa219
22f1aef
575e4a5
a1780f0
26561ff
7ac0f58
473e3cd
ab42278
0755554
c56887c
a1307d6
9de442b
fecb933
2b505a0
f9a297c
5fdb512
3fc9cd2
caa0bd3
1fa1b0b
0422d7b
a51764b
5d9eaf2
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,100 @@ | ||
# ⚾ Precourse-Week1 Mission **[숫자 야구]** | ||
|
||
## 💌 목차 | ||
|
||
- [💌 목차](#목차) | ||
- [📦 패키지 구조](#패키지-구조) | ||
- [✨ 기능 구현 목록](#기능-구현-목록) | ||
|
||
--- | ||
|
||
## 📦 패키지 구조 | ||
|
||
<div align="center"> | ||
<table> | ||
<tr> | ||
<th align="center">Package</th> | ||
<th align="center">Class</th> | ||
<th align="center">Description</th> | ||
</tr> | ||
<tr> | ||
<td><b>📃 config</b></td> | ||
<td>GlobalConfig</td> | ||
<td>전역으로 작용하는 설정<br/>(숫자 길이 제약, 뽑을 숫자 범위) Enum 파일</td> | ||
</tr> | ||
<tr><td colspan="3"></td></tr> | ||
<tr> | ||
<td><b>🕹 controller</b></td> | ||
<td>Game</td> | ||
<td>게임 로직을 메인으로 동작하는 컨트롤러 클래스</td> | ||
</tr> | ||
<tr><td colspan="3"></td></tr> | ||
<tr> | ||
<td rowspan="3"><b>💻 domain</b></td> | ||
<td>Number</td> | ||
<td>사용자에게 입력받는 숫자와, 컴퓨터가 생성하는 숫자 클래스 | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Result</td> | ||
<td>Ball Count와 Strike Count에 대한 결과 클래스</td> | ||
</tr> | ||
<tr> | ||
<td>ResultType</td> | ||
<td>볼, 스트라이크, 볼&스트라이크 상황에 대한 <br/>출력 방법이 정의된 Enum 클래스</td> | ||
</tr> | ||
<tr><td colspan="3"></td></tr> | ||
<tr> | ||
<td><b>✅ validator</b></td> | ||
<td>NumberValidator</td> | ||
<td>사용자가 입력하는 숫자에 대한 제약조건 클래스</td> | ||
</tr> | ||
<tr><td colspan="3"></td></tr> | ||
<tr> | ||
<td rowspan="3"><b>💬 view</b></td> | ||
<td>InputView</td> | ||
<td>사용자 요청을 받기 위한 Input View 클래스</td> | ||
</tr> | ||
<tr> | ||
<td>OutputView</td> | ||
<td>사용자에게 다양한 응답을 출력하기 위한 Output View 클래스</td> | ||
</tr> | ||
<tr> | ||
<td>PrintMessage</td> | ||
<td>사용자에게 응답할 정적 메세지를 담은 열거형 Enum 클래스</td> | ||
</tr> | ||
<tr><td colspan="3"></td></tr> | ||
<tr> | ||
<td colspan="3"><center><b>Image</b></center></td> | ||
</tr> | ||
<tr> | ||
<td colspan="3"><img src="https://github.com/woowacourse-precourse/java-baseball-6/assets/112257466/9cf30277-2b69-4781-a80e-dfa9325598d7" width="99%"></td> | ||
</tr> | ||
|
||
</table> | ||
</div> | ||
|
||
--- | ||
|
||
## ✨ 기능 구현 목록 | ||
|
||
- ✅1~9 사이의 서로 값이 다른 3자리의 정수를 랜덤으로 생성한다. | ||
- ✅서로 중복되지 않는 숫자 3개 | ||
- ✅게임 시작 문구 출력 | ||
- `숫자 야구 게임을 시작합니다.` | ||
- ✅사용자에게 `1-9 사이의 서로 값이 다른 3자리의 정수`를 입력 받고 저장한다. | ||
- ✅입력받은 input이 비어있을 경우(isEmpty) | ||
- ✅입력받은 input이 3자리가 아닐 경우 예외처리 | ||
- ✅입력받은 input이 숫자가 아닌 문자가 포함될 경우 예외처리 | ||
- ✅입력받은 input에 중복된 숫자가 있을 경우 예외처리 | ||
- ✅사용자 input과 랜덤 생성 정수를 비교해 출력할 힌트를 계산한다. | ||
- ✅다른 자리 같은 숫자의 갯수 n개 : `n볼` | ||
- ✅같은 자리 같은 숫자 n개: `n스트라이크` | ||
- ✅스트라이크와 볼이 같이 존재할 때 : `n볼 n스트라이크` | ||
- ✅같은 숫자가 1개도 없으면 : `낫싱` | ||
- ✅게임 클리어 여부 판단 | ||
- ✅3스트라이크 상황 메시지 출력: | ||
- `3개의 숫자를 모두 맞히셨습니다! 게임 종료` | ||
- `게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.` | ||
- ✅사용자에게 게임 재시작 여부를 입력받고, 입력값에 따라 프로그램 반복 실행 여부를 결정한다. | ||
- ✅3스트라이크가 아니라면, 다시 사용자에게 입력을 숫자를 받고, 힌트를 출력한다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,10 @@ | ||
package baseball; | ||
|
||
import baseball.controller.Game; | ||
|
||
public class Application { | ||
public static void main(String[] args) { | ||
// TODO: 프로그램 구현 | ||
Game game = new Game(); | ||
game.start(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package baseball.config; | ||
|
||
public enum GlobalConfig { | ||
NUMBER_LENGTH(3), | ||
RANDOM_NUMBER_MINIMUM(1), | ||
RANDOM_NUMBER_MAXIMUM(9), | ||
GAME_RESTART_FLAG(2); | ||
|
||
private final int value; | ||
|
||
GlobalConfig(int value) { | ||
this.value = value; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.valueOf(value); | ||
} | ||
|
||
public int getValue() { | ||
return value; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package baseball.controller; | ||
|
||
import baseball.domain.Number; | ||
import baseball.domain.Result; | ||
import baseball.view.InputView; | ||
import baseball.view.OutputView; | ||
|
||
import java.util.Objects; | ||
|
||
import static baseball.config.GlobalConfig.GAME_RESTART_FLAG; | ||
import static baseball.view.constants.PrintMessage.GAME_START; | ||
|
||
public class Game { | ||
public void start() { | ||
OutputView.printInformation(GAME_START); | ||
do { | ||
play(Number.generateRandomNumbers()); | ||
} while (!isRestart()); | ||
} | ||
|
||
private void play(Number computerNumber) { | ||
while (true) { | ||
Number playerNumber = Number.inputPlayerNumbers(); | ||
Result result = Result.create(playerNumber, computerNumber); | ||
result.print(); | ||
if (result.checkGameOver()) { | ||
break; | ||
} | ||
} | ||
} | ||
|
||
private static boolean isRestart() { | ||
return Objects.equals(InputView.askRestartOrExit(), GAME_RESTART_FLAG.toString()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package baseball.domain; | ||
|
||
import baseball.view.InputView; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.IntStream; | ||
|
||
import static baseball.config.GlobalConfig.*; | ||
import static baseball.validator.NumberValidator.*; | ||
import static camp.nextstep.edu.missionutils.Randoms.pickNumberInRange; | ||
|
||
public class Number { | ||
private final List<Integer> numbers; | ||
|
||
// Player Number Constructor | ||
private Number(String input) { | ||
validateEmpty(input); | ||
validateNumberLength(input); | ||
validateContainOnlyNumber(input); | ||
validateContainDuplicatedNumber(input); | ||
|
||
this.numbers = convertInputNumber(input); | ||
} | ||
|
||
// Computer Number Constructor | ||
private Number(List<Integer> computerNumber) { | ||
this.numbers = computerNumber; | ||
} | ||
|
||
// Player Number Static Factory Method | ||
public static Number inputPlayerNumbers() { | ||
String playerNumbers = InputView.askUserNumbers(); | ||
return new Number(playerNumbers); | ||
} | ||
|
||
// Computer Number Static Factory Method | ||
public static Number generateRandomNumbers() { | ||
List<Integer> randomNumbers = new ArrayList<>(); | ||
while (randomNumbers.size() < NUMBER_LENGTH.getValue()) { | ||
int number = pickNumberInRange(RANDOM_NUMBER_MAXIMUM.getValue(), RANDOM_NUMBER_MINIMUM.getValue()); | ||
if (!randomNumbers.contains(number)) { | ||
randomNumbers.add(number); | ||
} | ||
} | ||
return new Number(randomNumbers); | ||
} | ||
|
||
private List<Integer> convertInputNumber(String input) { | ||
return input.chars() | ||
.mapToObj(Character::getNumericValue) | ||
.toList(); | ||
} | ||
Comment on lines
+53
to
+57
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. 이런 식으로 문자열을 리스트로 바꾸는 거는 생각을 못 했는데 간결하고 좋은 것 같습니다👍 |
||
|
||
public int countBallCount(final Number comparableNumber) { | ||
return (int) IntStream.range(0, numbers.size()) | ||
.filter(i -> comparableNumber.isBall(numbers.get(i), i)) | ||
.count(); | ||
} | ||
|
||
public int countStrikeCount(final Number comparableNumber) { | ||
return (int) IntStream.range(0, numbers.size()) | ||
.filter(i -> comparableNumber.isStrike(numbers.get(i), i)) | ||
.count(); | ||
} | ||
|
||
// Ball : Contain their number at other position | ||
private boolean isBall(int number, int digit) { | ||
return !isStrike(number, digit) && numbers.contains(number); | ||
} | ||
|
||
// Strike : Contain their number at same digit | ||
private boolean isStrike(int number, int digit) { | ||
return number == numbers.get(digit); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,62 @@ | ||||||||||||||||||||||||||||
package baseball.domain; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import baseball.domain.constants.ResultType; | ||||||||||||||||||||||||||||
import baseball.view.OutputView; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import static baseball.config.GlobalConfig.NUMBER_LENGTH; | ||||||||||||||||||||||||||||
import static baseball.domain.constants.ResultType.*; | ||||||||||||||||||||||||||||
import static java.lang.String.format; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public class Result { | ||||||||||||||||||||||||||||
private final int ballCount; | ||||||||||||||||||||||||||||
private final int strikeCount; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
private Result(final Number playerNumber, final Number computerNumber) { | ||||||||||||||||||||||||||||
ballCount = playerNumber.countBallCount(computerNumber); | ||||||||||||||||||||||||||||
strikeCount = playerNumber.countStrikeCount(computerNumber); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public static Result create(final Number playerNumber, final Number computerNumber) { | ||||||||||||||||||||||||||||
return new Result(playerNumber, computerNumber); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
private ResultType inspectResultType() { | ||||||||||||||||||||||||||||
if (!hasBall() && !hasStrike()) { | ||||||||||||||||||||||||||||
return NOTHING; | ||||||||||||||||||||||||||||
} else if (hasBall() && hasStrike()) { | ||||||||||||||||||||||||||||
return BALL_AND_STRIKE; | ||||||||||||||||||||||||||||
} else if (hasBall()) { | ||||||||||||||||||||||||||||
return ONLY_BALL; | ||||||||||||||||||||||||||||
} else if (hasStrike()) { | ||||||||||||||||||||||||||||
return ONLY_STRIKE; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
throw new IllegalArgumentException("result type error"); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
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. 지금도 간결한 구조이긴 한데 else if는 빼고 if만 4번 넣어도 될 것 같습니당 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. 껄껄 else 없는 삶..서럽 |
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
private String generateResultMessage(ResultType resultType) { | ||||||||||||||||||||||||||||
return switch (resultType) { | ||||||||||||||||||||||||||||
case NOTHING -> resultType.getValue(); | ||||||||||||||||||||||||||||
case BALL_AND_STRIKE -> format(BALL_AND_STRIKE.getValue(), ballCount, strikeCount); | ||||||||||||||||||||||||||||
case ONLY_BALL -> format(ONLY_BALL.getValue(), ballCount); | ||||||||||||||||||||||||||||
case ONLY_STRIKE -> format(ONLY_STRIKE.getValue(), strikeCount); | ||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public void print() { | ||||||||||||||||||||||||||||
ResultType resultType = inspectResultType(); | ||||||||||||||||||||||||||||
String message = generateResultMessage(resultType); | ||||||||||||||||||||||||||||
OutputView.printResult(message); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
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. 제 생각에 mvc 구조에서는 OutputView가 도메인 계층보다는 컨트롤러 계층에서 쓰이면 좋을 것 같아서 print() 메소드가 String을 반환하도록 한다음 컨트롤러에서 처리하면 더 좋을 것 같습니다!
Suggested change
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. 정말 좋은 의견인 것 같아요! |
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public boolean hasBall() { | ||||||||||||||||||||||||||||
return ballCount > 0; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public boolean hasStrike() { | ||||||||||||||||||||||||||||
return strikeCount > 0; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public boolean checkGameOver() { | ||||||||||||||||||||||||||||
return strikeCount == NUMBER_LENGTH.getValue(); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package baseball.domain.constants; | ||
|
||
public enum ResultType { | ||
|
||
NOTHING("낫싱"), | ||
ONLY_BALL("%d볼"), | ||
ONLY_STRIKE("%d스트라이크"), | ||
BALL_AND_STRIKE("%d볼 %d스트라이크"); | ||
|
||
private final String value; | ||
|
||
ResultType(String value) { | ||
this.value = value; | ||
} | ||
|
||
public String getValue() { | ||
return value; | ||
} | ||
} | ||
Comment on lines
+3
to
+19
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. Enum 타입은 쓸 생각을 못 했는데 저렇게 %d까지 넣어서 처리하는 건 좀 sexy한 것 같습니다👍 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package baseball.validator; | ||
|
||
|
||
public class NumberValidator { | ||
public static void validateNumberLength(final String number) { | ||
if (!isValidLength(number)) { | ||
throw new IllegalArgumentException("number Length is different from the system setting"); | ||
} | ||
} | ||
|
||
public static void validateContainDuplicatedNumber(final String number) { | ||
if (!isUniqueNumber(number)) { | ||
throw new IllegalArgumentException("number cannot contain duplicated numbers"); | ||
} | ||
} | ||
|
||
public static void validateContainOnlyNumber(final String number) { | ||
if (!isValidNumber(number)) { | ||
throw new IllegalArgumentException("number cannot contain any letters"); | ||
} | ||
} | ||
|
||
public static void validateEmpty(final String number) { | ||
if (number.isEmpty()) { | ||
throw new IllegalArgumentException("number cannot be empty"); | ||
} | ||
} | ||
|
||
private static boolean isValidLength(final String number) { | ||
return number.length() == 3; | ||
} | ||
|
||
private static boolean isValidNumber(final String number) { | ||
return number | ||
.chars() | ||
.allMatch(c -> Character.isDigit(c) && c >= '1' && c <= '9'); | ||
} | ||
|
||
private static boolean isUniqueNumber(final String number) { | ||
return number.chars() | ||
.distinct() | ||
.count() == number.length(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package baseball.view; | ||
|
||
import camp.nextstep.edu.missionutils.Console; | ||
|
||
import static baseball.view.constants.PrintMessage.*; | ||
|
||
public class InputView { | ||
public static String askUserNumbers() { | ||
OutputView.printInformation(ASK_PLAYER_NUMBER); | ||
return Console.readLine(); | ||
} | ||
|
||
public static String askRestartOrExit() { | ||
OutputView.printInformation(GAME_OVER); | ||
OutputView.printInformation(ASK_RESTART_OR_EXIT); | ||
return Console.readLine(); | ||
} | ||
} |
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.
여기서 검증 메소드를 다 사용 안 한 이유가 있나요? 분명 7개 만든 것 같은데 4개 밖에 없어서ㅋㅋㅋ
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.
isValid 메소드는 실제 검증을 수행한 결과를 boolean으로 리턴합니다.
그리고 이 메소드 호출 결과에 따른 포스트 컨디션으로
validate 메소드를 활용해는 검증 -> 예외를 던지는 역할로 분리했습니다.
따라서 상기 4개의 메소드를 호출하면
7개의 메소드를 전부 활용한 검증이 가능한거죠!