diff --git a/README.md b/README.md
index 04dc4f6ae..4310d2236 100644
--- a/README.md
+++ b/README.md
@@ -1,129 +1,80 @@
-# 과제 - 숫자 야구 게임
-
-## 🔍 진행 방식
-
-- 과제는 **기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항** 세 가지로 구성되어 있다.
-- 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다.
-- 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.
-
----
+# ⚾️ 숫자 야구 게임
+
-## 📈 과제 진행 및 제출 방법
+## 🗒 구현할 기능 목록
-- 과제는 [java-baseball](https://github.com/LandvibeDev/java-baseball) 저장소를 Fork/Clone해 시작한다.
-- **기능을 구현하기 전에 java-baseball-precourse/README.md 파일에 구현할 기능 목록을 정리**해 추가한다.
-- **Git의 커밋 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위**로 추가한다.
- - [AngularJS Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 참고해 commit log를 남긴다.
-- 과제 진행 및 제출 방법은 [우아한코스 과제 제출 문서](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 를 참고한다.
- - base repository를 `LandvibeDev/java-baseball`로 지정해서 PR 생성하면됨
+### 1️⃣ 랜덤 숫자 생성
+- 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 생성
+- createRandomNumber()
-### 테스트 실행 가이드
-
-- 터미널에서 `java -version`을 실행하여 Java 버전이 14인지 확인한다. 또는 Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 14로 실행되는지 확인한다.
-- 터미널에서 Mac 또는 Linux 사용자의 경우 `./gradlew clean test` 명령을 실행 하고,
- Windows 사용자의 경우 `gradlew.bat clean test` 명령을 실행할 때 동작 하는지 만 확인(테스트는 실패).
+### 2️⃣ 숫자 입력
+- 사용자로부터 숫자를 입력받는다.
+- 올바른 값을 입력받았는지 유효성을 체크한다.
+- inputUserNumber()
----
+
-## 🚀 기능 요구사항
+### 3️⃣ 정확도 체크
+- 두 수를 비교하여 스트라이크의 수와 볼의 수를 계산한다.
+- checkAccuracy()
-기본적으로 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다.
+
-- 같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 같은 수가 전혀 없으면 포볼 또는 낫싱이란 힌트를 얻고, 그 힌트를 이용해서 먼저 상대방(컴퓨터)의 수를 맞추면 승리한다.
- - 예) 상대방(컴퓨터)의 수가 425일 때
- - 123을 제시한 경우 : 1스트라이크
- - 456을 제시한 경우 : 1볼 1스트라이크
- - 789를 제시한 경우 : 낫싱
-- 위 숫자 야구 게임에서 상대방의 역할을 컴퓨터가 한다. 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택한다. 게임 플레이어는 컴퓨터가 생각하고 있는 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한 결과를 출력한다.
-- 이 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임이 종료된다.
-- 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다.
-- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시킨 후 애플리케이션은 종료되어야 한다.
-- 아래의 프로그래밍 실행 결과 예시와 동일하게 입력과 출력이 이루어져야 한다.
+### 4️⃣ 정확도 출력
+- 계산된 스트라이크의 수와 볼의 수를 조건대로 출력한다.
+- printAccuracy()
-## ✍🏻 입출력 요구사항
+### 5️⃣ 게임 진행
+- 랜덤으로 생성된 컴퓨터의 숫자와, 입력받는 사용자의 숫자가 맞을 때까지 위의 2-4 과정을 반복한다.
+- playNumberBaseBallGame()
-### ⌨️ 입력
+
-- 3자리의 수
-- 게임이 끝난 경우 재시작/종료를 구분하는 1과 2 중 하나의 수
+### 6️⃣ 게임 종료 여부
+- 맞춘 경우, 종료 여부를 사용자에게 물어본다.
+- 입력 받은 수가 1과 2가 맞는지 유효성을 체크한다.
+- 사용자로부터 1 입력받을 시 -> 재시작
+- 사용자로부터 2 입력받을 시 -> 종료
+- isEndGame()
-### 🖥 출력
+
-- 입력한 수에 대한 결과를 볼, 스트라이크 개수로 표시
+### 7️⃣ 게임 시작
+- 게임 종료까지 게임 진행을 반복한다.
+- Application의 main에서 호출한다.
+- startNumberBaseBallGame()
-```
-1볼 1스트라이크
-```
+
-- 하나도 없는 경우
+------
-```
-낫싱
-```
+
-- 3개의 숫자를 모두 맞힐 경우
+## 🗂 폴더링
```
-3스트라이크
-3개의 숫자를 모두 맞히셨습니다! 게임 종료
-```
-
-### 💻 실행 결과 예시
+📦 baseball/
+|
++ 🗂 Application/
+|
++ 🗂 Constants/
+│
++-------🗂 Number
+| : 게임에 사용되는 숫자 상수들을 모아놓은 클래스
+|
++-------🗂 Symbol
+| : 게임에 사용되는 문자 상수들을 모아놓은 클래스
+|
++-------🗂 GameMessage
+| : 게임에 사용되는 안내 메세지들을 모아놓은 클래스
+|
++-------🗂 ExceptionNumber
+| : 예외에 사용되는 메세지들을 모아놓은 클래스
+
```
-숫자를 입력해주세요 : 123
-1볼 1스트라이크
-숫자를 입력해주세요 : 145
-1볼
-숫자를 입력해주세요 : 671
-2볼
-숫자를 입력해주세요 : 216
-1스트라이크
-숫자를 입력해주세요 : 713
-3스트라이크
-3개의 숫자를 모두 맞히셨습니다! 게임 종료
-게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.
-1
-숫자를 입력해주세요 : 123
-1볼
-…
-```
-
-
----
-
-## 🎱 프로그래밍 요구사항
--
-- JDK 14 버전에서 실행 가능해야 한다.
-- 프로그램을 실행하는 시작점은 `Application`의 `main()`이다.
-- `build.gradle` 파일을 변경할 수 없고, 외부 라이브러리를 사용하지 않는다.
-- [Java 코드 컨벤션](https://naver.github.io/hackday-conventions-java) 가이드를 준수하며 프로그래밍한다.
-- 프로그램 종료 시 `System.exit()`를 호출하지 않는다.
-- 프로그램 구현이 완료되면 `ApplicationTest`의 모든 테스트가 성공해야 한다.
-- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 이름을 수정하거나 이동하지 않는다.
-- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
- - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
-- 3항 연산자를 쓰지 않는다.
-- 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라.
-- JUnit 5와 AssertJ를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다.
-- 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다.
- - 함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다.
-- else 예약어를 쓰지 않는다.
- - 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
- - else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
-- Java Enum을 적용한다.
-- 도메인 로직에 단위 테스트를 구현해야 한다. 단, UI(System.out, System.in, Scanner) 로직은 제외한다.
- - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다.
-
-### 라이브러리 - Randoms, Console
-
-- JDK에서 기본 제공하는 Random, Scanner API 대신 [`camp.nextstep.edu.missionutils`](https://github.com/woowacourse-projects/mission-utils)에서 제공하는 `Randoms`, `Console` API를 활용해 구현해야 한다.
- - Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInRange()`를 활용한다.
- - 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다.
-- 프로그램 구현을 완료했을 때 `src/test/java` 디렉터리의 `ApplicationTest`에 있는 모든 테스트 케이스가 성공해야 한다. **테스트가 실패할 경우 0점 처리한다.**
diff --git a/build.gradle b/build.gradle
index 12ae76bcd..16820b2b3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ dependencies {
java {
toolchain {
- languageVersion = JavaLanguageVersion.of(14)
+ languageVersion = JavaLanguageVersion.of(15)
}
}
diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java
index 7f1901b8b..4dacfa3ed 100644
--- a/src/main/java/baseball/Application.java
+++ b/src/main/java/baseball/Application.java
@@ -2,6 +2,7 @@
public class Application {
public static void main(String[] args) {
- //TODO: 숫자 야구 게임 구현
+ NumberBaseballGame game = new NumberBaseballGame();
+ game.startNumberBaseBallGame();
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/baseball/Constants.java b/src/main/java/baseball/Constants.java
new file mode 100644
index 000000000..72bce0222
--- /dev/null
+++ b/src/main/java/baseball/Constants.java
@@ -0,0 +1,30 @@
+package baseball;
+
+public final class Constants {
+ public final class Number {
+ public static final int LENGTH = 3;
+ public static final int MIN_VALUE = 1;
+ public static final int MAX_VALUE = 9;
+ }
+
+ public final class Symbol {
+ public static final String END_GAME = "2";
+ public static final String RESTART_GAME = "1";
+ }
+
+ public final class GameMessage {
+ public static final String INPUT_NUMBER = "숫자를 입력해주세요 : ";
+ public static final String END_GAME = "개의 숫자를 모두 맞히셨습니다! 게임 종료";
+ public static final String RE_GAME = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.";
+ public static final String STRIKES = "스트라이크";
+ public static final String BALLS = "볼 ";
+ public static final String NOTHING_ = "낫싱";
+ }
+
+ public final class ExceptionMessage {
+ public static final String USER_INPUT = "1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 입력해주세요.";
+ public static final String STATE_OF_GAME = "1 또는 2를 입력해주세요.";
+ }
+
+ public static final String REGEX_PATTERN = "^[1-9](?!.*([1-9]).*\1)[1-9](?!.*\2)[1-9]$";
+}
\ No newline at end of file
diff --git a/src/main/java/baseball/NumberBaseballGame.java b/src/main/java/baseball/NumberBaseballGame.java
new file mode 100644
index 000000000..7496c8d61
--- /dev/null
+++ b/src/main/java/baseball/NumberBaseballGame.java
@@ -0,0 +1,109 @@
+package baseball;
+
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+import camp.nextstep.edu.missionutils.Console;
+import camp.nextstep.edu.missionutils.Randoms;
+
+public class NumberBaseballGame {
+ private int strikes;
+ private int balls;
+ private Vector userNumber;
+ private Vector computerNumber;
+
+ private void createRandomNumber() {
+ computerNumber = new Vector<>();
+ while(computerNumber.size() < Constants.Number.LENGTH) {
+ int newNumber = Randoms.pickNumberInRange(Constants.Number.MIN_VALUE, Constants.Number.MAX_VALUE);
+ if(computerNumber.contains(newNumber)) {
+ continue;
+ }
+ computerNumber.add(newNumber);
+ }
+ }
+
+ private void inputUserNumber() {
+ userNumber = new Vector<>();
+ System.out.println(Constants.GameMessage.INPUT_NUMBER);
+ String input = Console.readLine();
+ validTestInputUserNumber(input);
+ for(char c : input.toCharArray()) {
+ String letter = String.valueOf(c);
+ userNumber.add(Integer.parseInt(letter));
+ }
+ }
+
+ private void validTestInputUserNumber(String input) {
+ boolean isValid = Pattern.matches(Constants.REGEX_PATTERN, input);
+ if(!isValid) {
+ throw new IllegalArgumentException(Constants.ExceptionMessage.USER_INPUT);
+ }
+ }
+
+ private void checkAccuracy() {
+ strikes = 0;
+ balls = 0;
+ for(int i = 0; i < 3; i++) {
+ if(userNumber.get(i).equals(computerNumber.get(i))) {
+ strikes++;
+ continue;
+ }
+ if(computerNumber.contains(userNumber.get(i))) {
+ balls++;
+ }
+ }
+ }
+
+ private void printAccuracy() {
+ if(balls == 0 && strikes == 0) {
+ System.out.println(Constants.GameMessage.NOTHING_);
+ return;
+ }
+ if(strikes == 3) {
+ System.out.println(Constants.Number.LENGTH + Constants.GameMessage.END_GAME);
+ return;
+ }
+ if(balls != 0) {
+ System.out.print(balls + Constants.GameMessage.BALLS);
+ }
+ if(strikes != 0) {
+ System.out.print(strikes + Constants.GameMessage.STRIKES);
+ }
+ System.out.println();
+ }
+
+ private void playNumberBaseBallGame() {
+ createRandomNumber();
+ while(true) {
+ inputUserNumber();
+ checkAccuracy();
+ printAccuracy();
+ if(userNumber.equals(computerNumber)) {
+ break;
+ }
+ }
+ }
+
+ public void startNumberBaseBallGame() {
+ while(true) {
+ playNumberBaseBallGame();
+ if(isEndGame()) {
+ return;
+ }
+ }
+ }
+
+ private boolean isEndGame() {
+ System.out.println(Constants.GameMessage.RE_GAME);
+ String stateOfGame = Console.readLine();
+ validTestStateOfGame(stateOfGame);
+ return stateOfGame.equals(Constants.Symbol.END_GAME);
+ }
+
+ private void validTestStateOfGame(String state) {
+ if(!state.equals(Constants.Symbol.END_GAME) && !state.equals(Constants.Symbol.RESTART_GAME)) {
+ throw new IllegalArgumentException(Constants.ExceptionMessage.STATE_OF_GAME);
+ }
+ }
+}