Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d3a43d2
docs(README): list requirements and feature specifications
SangBeom-Hahn Jun 28, 2023
79f3e7b
feat(Computer): 상대방 컴퓨터 객체 구현
SangBeom-Hahn Jun 28, 2023
bf40464
test(Computer): 상대방 컴퓨터 객체 테스트
SangBeom-Hahn Jun 29, 2023
3861edb
refactor(Computer): 상대방 컴퓨터 랜덤 수 문자열이었던 것 List로 리팩토링
SangBeom-Hahn Jun 29, 2023
ff86b26
feat(User): 사용자 객체 구현
SangBeom-Hahn Jun 29, 2023
7dd776f
feat(User): 숫자를 대입하는 setNumber() 기능 추가
SangBeom-Hahn Jun 30, 2023
6e1f6e5
test(User): 사용자 객체 테스트
SangBeom-Hahn Jun 30, 2023
b072ddf
feat(BaseballController): 사용자가 랜덤 수를 맞추면 재시작(1) / 종료(2) 기능 구현
SangBeom-Hahn Jun 30, 2023
cd05f8e
test(BaseballController): 사용자가 랜덤 수를 맞추면 재시작(1) / 종료(2) 기능 테스트
SangBeom-Hahn Jun 30, 2023
c9f6dab
refactor(BaseballController): 사용자가 랜덤 수를 맞추면 재시작(1) / 종류(2) 기능 리팩토링
SangBeom-Hahn Jul 1, 2023
968677a
feat(BaseballController): 사용자 숫자 입력 시 결과 반환(스트라이크, 볼, 낫싱) 기능 구현
SangBeom-Hahn Jul 1, 2023
8b975fc
test(BaseballController): 사용자 숫자 입력 시 결과 반환(스트라이크, 볼, 낫싱) 기능 테스트
SangBeom-Hahn Jul 1, 2023
d498845
feat(BaseballController): 게임 시작 기능 구현
SangBeom-Hahn Jul 1, 2023
8ba8368
feat(BaseballController): 사용자 3자리 숫자 입력 기능 구현
SangBeom-Hahn Jul 1, 2023
3e23707
test(BaseballController): 사용자 3자리 숫자 입력 기능 테스트
SangBeom-Hahn Jul 1, 2023
63e284e
feat(BaseballController): 사용자 3자리 숫자 입력에 따른 결과 반환 기능 구현
SangBeom-Hahn Jul 1, 2023
6faf838
test(BaseballController): 사용자 3자리 숫자 입력에 따른 결과 반환 기능 테스트
SangBeom-Hahn Jul 1, 2023
79a73e2
feat(BaseballController) : 게임 종료 기능 구현
SangBeom-Hahn Jul 1, 2023
45901c9
test(BaseballController) : 게임 종료 기능 테스트
SangBeom-Hahn Jul 1, 2023
7ccf4e9
refactor(BaseballController) : 게임 시나리오를 위한 run 메서드 리팩토링
SangBeom-Hahn Jul 1, 2023
53e2e2f
refactor(OutputView) : 하드코딩 했던 것 view 분리 및 상수로 리팩토링
SangBeom-Hahn Jul 3, 2023
4c24258
refactor(InputView) : 하드코딩 했던 것 view 분리 및 상수로 리팩토링
SangBeom-Hahn Jul 3, 2023
c4321d5
refactor(BaseballController) : 하드코딩 했던 것 상수로 리팩토링
SangBeom-Hahn Jul 3, 2023
4aa97f2
refactor(User) : 하드코딩 했던 것 상수로 리팩토링
SangBeom-Hahn Jul 3, 2023
c133bc3
refactor(BaseballController) : User, Computer 지역변수에서 전역 변수로 리팩토링
SangBeom-Hahn Jul 3, 2023
787d1e7
refactor(BaseballController) : strikeCount, ballCount 지역변수에서 전역 변수로 리팩토링
SangBeom-Hahn Jul 3, 2023
4e39001
refactor(BaseballController) : strike, ball 카운트 메서드 stream으로 리팩토링
SangBeom-Hahn Jul 3, 2023
fbf0807
fix(OutputView) : strike, ball의 갯수가 0일 때 0도 출력되는 버그 수정
SangBeom-Hahn Jul 3, 2023
f4391a2
refactor : 접근 지정자 리팩토링
SangBeom-Hahn Jul 3, 2023
0fe1f31
refactor : 검증 추가 리팩토링
SangBeom-Hahn Jul 3, 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
39 changes: 39 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 요구사항 명세서
### 카테고리
1. 컴퓨터
2. 사용자
3. 시스템

### 요구사항 내용
1. 컴퓨터
- 1 ~ 9까지 서로 다른 임의의 수 3개를 선택한다.
- 사용자가 입력한 숫자에 대한 결과를 출력한다.
Copy link
Collaborator

Choose a reason for hiding this comment

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

일상적인 관점에서는 컴퓨터가 결과를 출력하지만 이 게임상에서는 컴퓨터 & 사용자를 Player로 보고 결과를 도출하는 것은 다른 컴포넌트에게 위임하는게 좋지 않을까요?




2. 사용자
- 서로 다른 3개의 숫자를 입력한다.
- 게임을 종료할 때 완전 종료와 다시 시작을 선택한다.


3. 시스템
- 게임 시작 문구를 출력한다.
- 사용자와 컴퓨터의 동작을 반복한다.
- 3개의 숫자를 모두 맞히면 게임을 종료한다.
게임이 종료할 때 사용자의 선택에 따라서 완전 종료하거나 다시 시작한다.
- 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 App을 종료한다.

# 기능 목록
1. 게임 시작
- 게임 시작 문구 출력


2. 게임 진행
- 컴퓨터는 1 ~ 9까지 서로 다른 임의의 수 선택
- 사용자가 서로 다른 3개의 숫자를 입력
- 같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 전혀 없으면 낫싱 출력


3. 게임 종료
- 사용자가 컴퓨터의 수를 맞추면 게임 종료 출력
- 재시작(1) / 종료(2)
3 changes: 3 additions & 0 deletions src/main/java/baseball/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package baseball;

import baseball.controller.BaseballController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
new BaseballController().run();
}
}
85 changes: 85 additions & 0 deletions src/main/java/baseball/controller/BaseballController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package baseball.controller;

import baseball.model.Computer;
import baseball.model.User;
import baseball.view.InputView;
import camp.nextstep.edu.missionutils.Console;
import java.util.List;
import java.util.stream.Stream;

import static baseball.view.InputView.inputNumber;
import static baseball.view.OutputView.*;

public class BaseballController {
private static final String RESTART_STATUS = "1";
private static final int THREE_STRIKE = 3;
private static final boolean NOT_THREE_STRIKE = true;

private User user;
private Computer computer;
private int strikeCount;
private int ballCount;

public BaseballController() {
this.strikeCount = 0;
this.ballCount = 0;
}

public void run() {
printGameStartMessage();
do {
computer = gameStart();
playGame();
} while (gameOver());
}

private Computer gameStart() {
return new Computer();
}

private void playGame() {
user = new User();

while (NOT_THREE_STRIKE) {
user.setNumber(inputNumber());
strikeCount = getStrikeCount(user, computer);
ballCount = getBallCount(user, computer);

printGameResultMessage(strikeCount, ballCount);
if (strikeCount == THREE_STRIKE) {
break;
}
}
}

private boolean gameOver() {
return !isTerminate(InputView.terminateSignUserInput());
}

private int getStrikeCount(final User user, final Computer computer) {
List<Integer> computerRandomNumber = computer.getRandomNumber();
List<Integer> userNumber = user.getNumber();

return Stream.iterate(0, n -> n + 1).limit(computerRandomNumber.size())
.filter(i -> computerRandomNumber.get(i) == userNumber.get(i))
.reduce(0, (cnt, b) -> cnt + 1);
}

private int getBallCount(final User user, final Computer computer) {
List<Integer> computerRandomNumber = computer.getRandomNumber();
List<Integer> userNumber = user.getNumber();

return Stream.iterate(1, n -> n + 1).limit(9)
.filter(i -> computerRandomNumber.contains(i) && userNumber.contains(i))
.filter(i -> computerRandomNumber.indexOf(i) != userNumber.indexOf(i))
.reduce(0, (cnt, b) -> cnt + 1);
}

private boolean isTerminate(final String restartStatus) {
if(restartStatus.equals(RESTART_STATUS)){
return false;
} else {
return true;
}
}
}
55 changes: 55 additions & 0 deletions src/main/java/baseball/model/Computer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package baseball.model;

import camp.nextstep.edu.missionutils.Randoms;

import java.util.ArrayList;
import java.util.List;

public class Computer {
public static final int NUMBER_LENGTH = 3;
public static final int MIN_RANGE = 1;
public static final int MAX_RANGE = 9;
private List<Integer> randomNumber;

public Computer() {
saveRandomNumberWithGameStart();
}
Comment on lines +14 to +16
Copy link
Collaborator

Choose a reason for hiding this comment

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

save를 통해서 randomNumber에 대한 init을 한다는거는 알겠는데
위의 코드보다는

Suggested change
public Computer() {
saveRandomNumberWithGameStart();
}
public Computer() {
this.randomNumber = generate~~();
}

와 같이 수정하는게 더 가독성이 좋을거 같은데 어떻게 생각하시나요?


public List<Integer> getRandomNumber() {
return randomNumber;
}
Comment on lines +18 to +20
Copy link
Collaborator

@sjiwon sjiwon Jul 1, 2023

Choose a reason for hiding this comment

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

일반적으로 getter/setter/equals와 같은 메소드들은 클래스 가장 하단에 위치하는게 관례입니다

  • 외부에서 변경할 이유가 없다면 mutable -> immutable


private void saveRandomNumberWithGameStart() {
randomNumber = new ArrayList<>();
Integer digit;

while (checkLengthSmallThanThree()) {
digit = getRandomDigit();
if (!hasDuplicateDigitInRandomNumber(digit)) {
randomNumber.add(digit);
}
}
}

private boolean checkLengthSmallThanThree() {
if (randomNumber.size() < NUMBER_LENGTH) {
return true;
}
return false;
}

private Integer getRandomDigit() {
return Randoms.pickNumberInRange(MIN_RANGE, MAX_RANGE);
}

private boolean hasDuplicateDigitInRandomNumber(final Integer digit) {
if (randomNumber.contains(digit)) {
return true;
}
return false;
}

public void setRandomNumber(final List<Integer> randomNumber) {
this.randomNumber = randomNumber;
}
}
48 changes: 48 additions & 0 deletions src/main/java/baseball/model/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package baseball.model;


import java.util.List;
import java.util.Set;

public class User {

private static final String INPUT_NUMBER_LENGTH_NOT_THREE_EXCEPTION = "입력 숫자는 3자리입니다.";
private static final String INPUT_DIGIT_LENGTH_NOT_ONE_EXCEPTION = "입력 숫자는 1 ~ 9입니다.";
public static final int NUMBER_SIZE = 3;
public static final int MIN_DIGIT = 1;
public static final int MAX_DIGIT = 9;
public static final String INPUT_NUMBER_DUPLICATE_EXCEPTION = "입력숫자의 각 자리는 중복될 수 없습니다.";

public enum RestartStatus { RESTART, TERMINATE }

private List<Integer> number;

public void setNumber(List<Integer> number) {
validateNumberSize(number);
validateNumberRange(number);
validateDuplicateDigit(number);
this.number = number;
}

private void validateNumberSize(final List<Integer> number) {
if (number.size() > NUMBER_SIZE || number.size() < NUMBER_SIZE) {
throw new IllegalArgumentException(INPUT_NUMBER_LENGTH_NOT_THREE_EXCEPTION);
}
}

private void validateNumberRange(final List<Integer> number) {
if (!number.stream().allMatch(digit -> digit >= MIN_DIGIT && digit <= MAX_DIGIT)) {
throw new IllegalArgumentException(INPUT_DIGIT_LENGTH_NOT_ONE_EXCEPTION);
}
}

private void validateDuplicateDigit(final List<Integer> number) {
if (Set.copyOf(number).size() != number.size()) {
throw new IllegalArgumentException(INPUT_NUMBER_DUPLICATE_EXCEPTION);
}
}

public List<Integer> getNumber() {
return number;
}
}
67 changes: 67 additions & 0 deletions src/main/java/baseball/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package baseball.view;

import camp.nextstep.edu.missionutils.Console;

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

public class InputView {

private static final String INPUT_NUMBER_MESSAGE = "숫자를 입력해주세요 : ";
private static final String CHARACTER_REGEX = "[-+]?\\d*\\.?\\d+";
private static final String RESTART_STATUS = "1";
private static final String NOT_RESTART_STATUS = "2";
private static final String RESTART_OR_END_ONE_OR_TWO_EXCEPTION = "재시작은 1, 완전 종료는 2 입니다.";
private static final String RESTART_OR_END_NOT_NUMBER_BECAUSE_STRING_EXCEPTION = "재시작은 1, 완전 종료는 2인 정수입니다.";
private static final String RESTART_OR_END_NOT_NUMBER_BECAUSE_DOUBLE_EXCEPTION = "재시작은 1, 완전 종료는 2인 정수로 소수를 입력할 수 없습니다.";
public static final String INPUT_NUMBER_SPECIAL_CHARACTER_EXCEPTION = "특수 문자는 입력할 수 없습니다.";

public static List<Integer> inputNumber() {
System.out.print(INPUT_NUMBER_MESSAGE);
String inputNumber = Console.readLine();
validateNotSpecialCharacterInUserNumber(inputNumber);

return Arrays.stream(inputNumber.split(""))
.map(Integer::parseInt)
.collect(Collectors.toList());
}

public static String terminateSignUserInput() {
String inputRestartStatus = Console.readLine();
validateRestartStatus(inputRestartStatus);
return inputRestartStatus;
}

private static void validateNotSpecialCharacterInUserNumber(String inputNumber) {
if (!Arrays.stream(inputNumber.split("")).allMatch(digit -> digit.matches(CHARACTER_REGEX))) {
throw new IllegalArgumentException(INPUT_NUMBER_SPECIAL_CHARACTER_EXCEPTION);
}
}

private static void validateRestartStatus(final String restartStatus) {
validateNotStringRestartStatus(restartStatus);
validateNotDoubleRestartStatus(restartStatus);
validateRangeRestartStatus(restartStatus);
}

private static void validateRangeRestartStatus(final String restartStatus) {
if (!restartStatus.equals(RESTART_STATUS) && !restartStatus.equals(NOT_RESTART_STATUS)) {
throw new IllegalArgumentException(RESTART_OR_END_ONE_OR_TWO_EXCEPTION);
}
}

private static void validateNotStringRestartStatus(final String restartStatus) {
if (!(restartStatus != null && restartStatus.matches(CHARACTER_REGEX))) {
throw new IllegalArgumentException(RESTART_OR_END_NOT_NUMBER_BECAUSE_STRING_EXCEPTION);
}
}

private static void validateNotDoubleRestartStatus(final String restartStatus) {
if (!restartStatus.chars().allMatch(Character::isDigit)) {
throw new IllegalArgumentException(RESTART_OR_END_NOT_NUMBER_BECAUSE_DOUBLE_EXCEPTION);
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/baseball/view/OutputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package baseball.view;

public class OutputView {

private static final String GAME_START_MESSAGE = "숫자 야구 게임을 시작합니다.";
private static final String STRIKE_MESSAGE = "스트라이크";
private static final String GAME_OVER_MESSAGE = "3개의 숫자를 모두 맞히셨습니다! 게임종료";
private static final String GAME_RESTART_OR_END_MESSAGE = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요";
private static final String NOTHING_MESSAGE = "낫싱";
private static final String BALL_MESSAGE = "볼 ";
public static final int THREE_STRIKE = 3;
public static final int ZERO = 0;

public static void printGameStartMessage() {
System.out.println(GAME_START_MESSAGE);
}

public static void printGameResultMessage(final int strikeCount, final int ballCount) {
if (strikeCount == THREE_STRIKE) {
System.out.println(strikeCount + STRIKE_MESSAGE);
System.out.println(GAME_OVER_MESSAGE);
System.out.println(GAME_RESTART_OR_END_MESSAGE);
} else if (strikeCount == ZERO && ballCount == ZERO) {
System.out.println(NOTHING_MESSAGE);
} else if (strikeCount != ZERO && ballCount == ZERO) {
System.out.println(strikeCount + STRIKE_MESSAGE);
} else if (strikeCount == ZERO && ballCount != ZERO) {
System.out.println(ballCount + BALL_MESSAGE);
} else {
System.out.println(ballCount + BALL_MESSAGE + strikeCount + STRIKE_MESSAGE);
}
}
}
Loading