Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
65fcc51
docs: 기능 요구사항 정리 추가
jeongyuneo Mar 25, 2023
5733af7
feat: 게임 시작 문구 출력 기능 추가
jeongyuneo Mar 25, 2023
1e07540
feat: 랜덤 숫자 생성 기능 추가
jeongyuneo Mar 25, 2023
110b5ed
feat: 숫자 입력 기능 추가
jeongyuneo Mar 25, 2023
022bd03
feat: 입력 값 공백 제거 기능 추가
jeongyuneo Mar 25, 2023
65e128b
feat: 입력 값 3자리 검증 기능 추가
jeongyuneo Mar 25, 2023
43d48ad
feat: 입력 값 정수 검증 기능 추가
jeongyuneo Mar 25, 2023
424022c
feat: 입력 값 정수 범위 검증 기능 추가
jeongyuneo Mar 25, 2023
a09154e
feat: 랜덤 숫자와 입력 숫자 비교 기능 추가
jeongyuneo Mar 25, 2023
056bf9f
feat: 비교 결과 출력 기능 추가
jeongyuneo Mar 25, 2023
25852e9
feat: 게임 이어하기 및 종료 기능 추가
jeongyuneo Mar 25, 2023
322bce9
feat: 게임 재진행 여부 질문 기능 추가
jeongyuneo Mar 25, 2023
1b60b38
feat: 게임 재진행 여부 입력 기능 추가
jeongyuneo Mar 25, 2023
61db2df
feat: 게임 재진행 여부 입력 값 공백 제거 기능 추가
jeongyuneo Mar 25, 2023
ba2a2b2
feat: 게임 재진행 여부 입력 값 1자리 검증 기능 추가
jeongyuneo Mar 25, 2023
d0db6d4
feat: 게임 재진행 여부 입력 값 정수 검증 기능 추가
jeongyuneo Mar 25, 2023
26475e3
feat: 게임 재진행 여부 입력 값 정수 범위 검증 기능 추가
jeongyuneo Mar 25, 2023
34f7849
docs: 기능 요구사항 정리 수정
jeongyuneo Mar 25, 2023
24702fe
feat: 게임 재진행 및 종료 기능 추가
jeongyuneo Mar 25, 2023
ee3b873
feat: 숫자 입력 메시지 추가
jeongyuneo Mar 25, 2023
9165d5e
refactor: 게임 시작 문구 출력 책임 분리
jeongyuneo Mar 25, 2023
3e2e256
refactor: 게임 실행 책임 분리
jeongyuneo Mar 25, 2023
b8180de
refactor: io 패키지 네이밍 수정
jeongyuneo Mar 25, 2023
b70d664
refactor: 랜덤 숫자 생성 책임 분리
jeongyuneo Mar 25, 2023
345cde2
refactor: RandomNumberGenerator - 랜덤 숫자 개수 상수화
jeongyuneo Mar 25, 2023
fc343db
refactor: 게임 재진행 질문 책임 분리
jeongyuneo Mar 25, 2023
ca2f204
refactor: 숫자 입력 책임 분리
jeongyuneo Mar 25, 2023
5b50396
refactor: Input - 공백 및 대체문자 상수화
jeongyuneo Mar 25, 2023
dd2c2fa
refactor: 게임 숫자 생성 책임 분리
jeongyuneo Mar 25, 2023
48cb7f7
refactor: 게임 숫자 비교 책임 분리
jeongyuneo Mar 25, 2023
24bc5c2
style: GameNumbers - 오타 수정
jeongyuneo Mar 25, 2023
7515fda
refactor: 게임 결과 생성 책임 분리
jeongyuneo Mar 25, 2023
0200634
refactor: GameNumberGenerator - 메소드 네이밍 수정
jeongyuneo Mar 25, 2023
0e98bb9
refactor: 게임 결과 출력 책임 분리
jeongyuneo Mar 25, 2023
2fdf749
refactor: 게임 재진행 여부 입력 책임 분리
jeongyuneo Mar 25, 2023
9cd8ad3
refactor: 게임 재진행 책임 분리
jeongyuneo Mar 25, 2023
f4d067e
refactor: GameResultGenerator - 생성 반환 타입 수정
jeongyuneo Mar 25, 2023
7d22f2b
refactor: GameNumberGenerator - 생성 반환 타입 수정
jeongyuneo Mar 25, 2023
8a2c5ba
refactor: GameNumberGenerator - 게임 숫자 상수로 수정
jeongyuneo Mar 25, 2023
aca6e8c
fix: 게임 종료 시 재진행 여부 질문 에러 수정
jeongyuneo Mar 25, 2023
80eedc1
refactor: 정수 전환 책임 분리
jeongyuneo Mar 25, 2023
daf96a5
test: GameNumberGeneratorTest 추가
jeongyuneo Mar 25, 2023
97fb0d5
test: GameResultGeneratorTest 추가
jeongyuneo Mar 25, 2023
0e21ff4
test: GameStatusGeneratorTest 추가
jeongyuneo Mar 25, 2023
cf98bb7
refactor: GameStatus - 검증 로직 추가
jeongyuneo Mar 26, 2023
c7b0548
test: GameStatusGeneratorTest 삭제
jeongyuneo Mar 26, 2023
4734106
refactor: GameNumbers - 검증 로직 추가
jeongyuneo Mar 26, 2023
d25a2d5
refactor: GameNumber - 검증 로직 추가
jeongyuneo Mar 26, 2023
fa66a55
refactor: GameResult - 정적 팩토리 메소드 추가
jeongyuneo Mar 26, 2023
3377e54
test: GameResultGeneratorTest - GameResult 생성 로직 수정
jeongyuneo Mar 26, 2023
415979e
test: GameNumberGeneratorTest - @ParameterizedTest 적용
jeongyuneo Mar 26, 2023
4a0d896
docs: 기능 요구사항 정리 수정
jeongyuneo Mar 26, 2023
2cbce96
feat: 입력 값 중복 검증 기능 추가
jeongyuneo Mar 26, 2023
a6520b4
test: GameNumberGeneratorTest - 입력 값 중복 검증 테스트 케이스 추가
jeongyuneo Mar 26, 2023
262cf0a
test: GameResultTest - 클래스 네이밍 수정 및 패키지 이동
jeongyuneo Mar 27, 2023
486f562
refactor: GameNumber - 예외 발생 메시지 포매팅
jeongyuneo Mar 27, 2023
db89377
refactor: GameNumbers - 예외 발생 메시지 포매팅
jeongyuneo Mar 27, 2023
d8a9fed
refactor: GameNumberGenerator 삭제
jeongyuneo Mar 27, 2023
d77ff08
refactor: GameNumberGenerator 추가
jeongyuneo Mar 27, 2023
19fa2c1
refactor: RandomNumberGenerator - 패키지 및 로직 수정
jeongyuneo Mar 27, 2023
f671738
refactor: UserNumberGenerator - 클래스 네이밍, 패키지 및 로직 수정
jeongyuneo Mar 27, 2023
d0b3745
refactor: GameResult - 비교 로직 수정
jeongyuneo Mar 27, 2023
88647bf
refactor: GameResult - 게임 종료 여부 필드 추가
jeongyuneo Mar 27, 2023
c470951
refactor: GameStatus - 정적 팩토리 메소드 추가
jeongyuneo Mar 27, 2023
885f156
style: RandomNumberGenerator - 오타 삭제
jeongyuneo Mar 27, 2023
8ae7d10
refactor: GameNumber - 정적 팩토리 메소드 추가
jeongyuneo Mar 27, 2023
c8713d9
refactor: GameNumbers - 비교 로직 수정
jeongyuneo Mar 27, 2023
12ae9c8
refactor: NumberBaseballGame - 로직 수정
jeongyuneo Mar 27, 2023
eee7e78
test: UserNumberGeneratorTest 클래스 네이밍 수정
jeongyuneo Mar 27, 2023
38b3ef8
refactor: GameNumbers - 중복 검증 로직 수정
jeongyuneo Mar 27, 2023
aaa90c8
test: UserNumberGeneratorTest - 패키지 이동 및 테스트 코드 수정
jeongyuneo Mar 27, 2023
88990f2
test: GameResultTest - 테스트 코드 수정
jeongyuneo Mar 27, 2023
ef38005
test: GameNumbersTest 추가
jeongyuneo Mar 27, 2023
39731ce
test: GameStatusTest 추가
jeongyuneo Mar 27, 2023
7e2d57d
refactor: GameNumber - 미사용 메소드 삭제
jeongyuneo Mar 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 숫자 야구

## 기능 요구사항 정리

- [x] 게임 시작 문구 출력한다.
- [x] 랜덤 숫자를 생성한다.
- 숫자는 3자리다.
- 숫자는 중복되지 않아야 한다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 안 지켜지고 있는 것 같아요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 조건은 랜덤으로 생성되는 숫자에만 적용했던 조건입니다.
사용자 입력 숫자는 중복체크를 안하고 있었는데, 필요할 것 같아 추가 구현했습니다!

- [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] 게임을 재진행 또는 종료한다.
7 changes: 6 additions & 1 deletion src/main/java/baseball/Application.java
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();
}
}
45 changes: 45 additions & 0 deletions src/main/java/baseball/domain/GameNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package baseball.domain;

import camp.nextstep.edu.missionutils.Randoms;

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;

private GameNumber(int value) {
validateRange(value);
this.value = value;
}

public static GameNumber from(int input) {
return new GameNumber(input);
}

public static GameNumber create() {
return new GameNumber(Randoms.pickNumberInRange(MIN_VALUE, MAX_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(String.format("%d~%d 범위를 벗어나는 숫자가 포함되어 있습니다.", MIN_VALUE, MAX_VALUE));
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/baseball/domain/GameNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package baseball.domain;

import java.util.List;

public interface GameNumberGenerator {

List<GameNumber> generate(int limit);
}
55 changes: 55 additions & 0 deletions src/main/java/baseball/domain/GameNumbers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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 final List<GameNumber> gameNumbers;

private GameNumbers(List<GameNumber> gameNumbers) {
validateTheNumberOf(gameNumbers);
validateDuplication(gameNumbers);
this.gameNumbers = new ArrayList<>(gameNumbers);
}

public static GameNumbers from(GameNumberGenerator gameNumberGenerator) {
return new GameNumbers(gameNumberGenerator.generate(THE_NUMBER_OF_GAME_NUMBER));
}

public GameResult compare(GameNumbers other) {
int strike = 0;
int ball = 0;
for (int order = 0; order < THE_NUMBER_OF_GAME_NUMBER; order++) {
if (isStrike(other, order)) {
strike++;
} else if (isBall(other, gameNumbers.get(order))) {
ball++;
}
}
return GameResult.from(strike, ball, THE_NUMBER_OF_GAME_NUMBER);
}

private static void validateTheNumberOf(List<GameNumber> gameNumbers) {
if (gameNumbers.size() != THE_NUMBER_OF_GAME_NUMBER) {
throw new IllegalArgumentException(String.format("%d자리 값이 아닙니다.", THE_NUMBER_OF_GAME_NUMBER));
}
}

private void validateDuplication(List<GameNumber> gameNumbers) {
if (new HashSet<>(gameNumbers).size() != THE_NUMBER_OF_GAME_NUMBER) {
throw new IllegalArgumentException("중복되는 값이 포함되어 있습니다.");
}
}

private boolean isStrike(GameNumbers other, int order) {
return this.gameNumbers.get(order).equals(other.gameNumbers.get(order));
}

private boolean isBall(GameNumbers computer, GameNumber gameNumber) {
return computer.gameNumbers.contains(gameNumber);
}
}
39 changes: 39 additions & 0 deletions src/main/java/baseball/domain/GameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package baseball.domain;

public class GameResult {

private static final int THE_NUMBER_OF_GAME_NUMBER = 3;

private final String value;
private final boolean isEnd;

private GameResult(String value, boolean isEnd) {
this.value = value;
this.isEnd = isEnd;
}

public static GameResult from(int strike, int ball, int theNumberOfGameNumber) {
if (strike == 0 && ball == 0) {
return new GameResult("낫싱", false);
}
StringBuilder result = new StringBuilder();
if (ball != 0) {
result.append(ball).append("볼 ");
}
if (strike != 0) {
result.append(strike).append("스트라이크");
}
if (strike == theNumberOfGameNumber) {
result.append("\n").append(theNumberOfGameNumber).append("개의 숫자를 모두 맞히셨습니다! 게임 종료");
}
return new GameResult(result.toString(), strike == THE_NUMBER_OF_GAME_NUMBER);
}

public String getValue() {
return value;
}

public boolean isEnd() {
return isEnd;
}
}
30 changes: 30 additions & 0 deletions src/main/java/baseball/domain/GameStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package baseball.domain;

import baseball.util.Convertor;

public class GameStatus {

private static final int RESTART = 1;
private static final int END = 2;

private final int status;

private GameStatus(int status) {
validateRange(status);
this.status = status;
}

public static GameStatus from(String input) {
return new GameStatus(Convertor.toInteger(input));
}

public boolean isRestart() {
return status == RESTART;
}

private static void validateRange(int status) {
if (status != RESTART && status != END) {
throw new IllegalArgumentException("1 또는 2 이외의 숫자가 포함되어 있습니다.");
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/baseball/domain/RandomNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package baseball.domain;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class RandomNumberGenerator implements GameNumberGenerator {

public List<GameNumber> generate(int limit) {
return Stream.generate(GameNumber::create)
.distinct()
.limit(limit)
.collect(Collectors.toList());
}
}
33 changes: 33 additions & 0 deletions src/main/java/baseball/domain/UserNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package baseball.domain;

import baseball.util.Convertor;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class UserNumberGenerator implements GameNumberGenerator {

private static final String DELIMITER = "";

private final String input;

public UserNumberGenerator(String input) {
this.input = input;
}

public List<GameNumber> generate(int limit) {
String[] inputNumbers = input.split(DELIMITER);
validateTheNumberOf(inputNumbers, limit);
return Arrays.stream(inputNumbers)
.map(Convertor::toInteger)
.map(GameNumber::from)
.collect(Collectors.toList());
}

private void validateTheNumberOf(String[] inputNumbers, int limit) {
if (inputNumbers.length != limit) {
throw new IllegalArgumentException(String.format("%d자리 값이 아닙니다.", limit));
}
}
}
6 changes: 6 additions & 0 deletions src/main/java/baseball/service/Game.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package baseball.service;

public interface Game {

void run();
}
38 changes: 38 additions & 0 deletions src/main/java/baseball/service/NumberBaseballGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package baseball.service;

import baseball.domain.*;
import baseball.util.Input;
import baseball.util.Output;

public class NumberBaseballGame implements Game {

private static final GameNumberGenerator randomNumberGenerator = new RandomNumberGenerator();

public void run() {
Output.printStartMessage();
start();
}

private void start() {
play(GameNumbers.from(randomNumberGenerator));
askPlayAgain();
}

private void play(GameNumbers computer) {
while (true) {
GameNumberGenerator userNumberGenerator = new UserNumberGenerator(Input.readGameNumber());
GameNumbers user = GameNumbers.from(userNumberGenerator);
GameResult gameResult = user.compare(computer);
Output.printResult(gameResult.getValue());
if (gameResult.isEnd()) {
break;
}
}
}

private void askPlayAgain() {
if (GameStatus.from(Input.readGameStatus()).isRestart()) {
start();
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/baseball/util/Convertor.java
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("정수가 아닌 값이 포함되어 있습니다.");
}
}
}
26 changes: 26 additions & 0 deletions src/main/java/baseball/util/Input.java
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 사용자가 1 23 이런식으로도 입력하는 경우를 방지하셨네요
놓쳤던 부분입니다 ㅎ

}
}
15 changes: 15 additions & 0 deletions src/main/java/baseball/util/Output.java
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);
}
}
Loading