Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c0abb19
docs : add functional list
ray-yhc Mar 25, 2023
f449f83
test : add UserOutput test code
ray-yhc Mar 25, 2023
714d713
test : add UserInput test code
ray-yhc Mar 25, 2023
3ac7ecc
test : add SingleGame test code
ray-yhc Mar 25, 2023
292c25c
test : edit UserOutput test code
ray-yhc Mar 25, 2023
e4f003c
test : edit UserInput test code
ray-yhc Mar 25, 2023
6e8eec3
test : edit SingleGame test code
ray-yhc Mar 25, 2023
6b2fc50
feat : implement SingleGame
ray-yhc Mar 25, 2023
dcc759f
feat : implement UserInput
ray-yhc Mar 25, 2023
483d073
feat : implement UserOutput
ray-yhc Mar 25, 2023
6a580a1
feat : implement Application
ray-yhc Mar 25, 2023
9a837a4
feat : add regex to check user input
ray-yhc Mar 25, 2023
d6fb92a
feat : add code for checking exceptions in UserInput
ray-yhc Mar 25, 2023
756a633
test : add GameBall test
ray-yhc Mar 27, 2023
951f6b7
test : delete UserInputTest.java
ray-yhc Mar 27, 2023
2169607
chore : edit method name
ray-yhc Mar 27, 2023
3198cf6
refactor : edit output type
ray-yhc Mar 27, 2023
fec8802
refactor : edit UserInput return type
ray-yhc Mar 27, 2023
9336696
refactor : refactor game code
ray-yhc Mar 27, 2023
d5d7ade
test : add GameCommand test
ray-yhc Mar 27, 2023
4472181
feat : add GameCommand
ray-yhc Mar 27, 2023
cc19a60
feat : add GameBall
ray-yhc Mar 27, 2023
314e127
chore : edit method order of GameStatus
ray-yhc Mar 27, 2023
1b3f09b
refactor : refactor main method to GameService
ray-yhc Mar 27, 2023
7e9f562
feat : delete Regex code
ray-yhc Mar 27, 2023
6472f4a
feat : edit UserInput, UserOutput into static method
ray-yhc Mar 27, 2023
dc3d36f
chore : edit appearance
ray-yhc Mar 27, 2023
9d64f96
test : edit UserOutputTest
ray-yhc Mar 27, 2023
af4d49d
chore : edit method
ray-yhc 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
20 changes: 20 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 기능 목록

입출력
- [ ] 사용자 입력
- [ ] 숫자 입력
- [ ] 예외처리
- [ ] 게임 진행(1 or 2) 입력
- [ ] 예외처리
- [ ] 메시지 출력
- [ ] 시작 메시지
- [ ] (b, s) -> 볼/스트라이크 출력
- [ ] 종료 메시지

게임 진행
- [ ] main
- [ ] 게임 시작
- [ ] 게임 종료, 결과 출력
- [ ] singleGame
- [ ] init : 난수 생성
- [ ] singleTurn (xxx) -> 판정 후 결과 리턴
17 changes: 16 additions & 1 deletion src/main/java/baseball/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
package baseball;

import baseball.game.model.GameCommand;
import baseball.game.model.GameStatus;
import baseball.game.GameService;
import baseball.inout.UserInput;
import baseball.inout.UserOutput;

import java.util.List;
Comment on lines +3 to +9

Choose a reason for hiding this comment

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

안쓰는 임포트는 지워주세요!
인텔리제이를 사용하신다면, 맥 기준 ctrl+option+o(영어 오) / 윈도우 기준 ctrl+alt+o 임포트 정리 단축키를 사용해서 간편하게 정리할 수 있습니다ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

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

오오 꿀팁이네요


public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
GameCommand command = GameCommand.CONTINUE;

while (command.isContinue()){
GameService gameService = new GameService();
gameService.playSingleGame();
command = gameService.getCommand();
}
}

}
37 changes: 37 additions & 0 deletions src/main/java/baseball/game/GameService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package baseball.game;

import baseball.game.model.GameBall;
import baseball.game.model.GameCommand;
import baseball.game.model.GameStatus;
import baseball.inout.UserInput;
import baseball.inout.UserOutput;
import camp.nextstep.edu.missionutils.Randoms;

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

public class GameService {

GameBall computer;

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.

앗 놓쳤네요! 꼼꼼하게 봐주셔서 감사합니다~


public GameService() {
computer = GameBall.createRandomBall();
}

public void playSingleGame() {
boolean isContinue = true;

while (isContinue) {
GameBall playNum = GameBall.createBall(UserInput.getNum());
GameStatus gameStatus = computer.getStatus(playNum);
UserOutput.printStatus(gameStatus);
isContinue = gameStatus.isContinue();
}
UserOutput.printEndMessage();
}

public GameCommand getCommand() {
return GameCommand.getCommand(UserInput.isContinue());
}

}
74 changes: 74 additions & 0 deletions src/main/java/baseball/game/model/GameBall.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package baseball.game.model;

import camp.nextstep.edu.missionutils.Randoms;

import java.util.*;

public class GameBall {

private final static int BALL_MAX = 9;
private final static int BALL_MIN = 1;
private final static int BALL_NUM = 3;

private List<Integer> ballNumbers;

private GameBall(List<Integer> ballNumbers) {
this.ballNumbers = ballNumbers;
}

public static GameBall createBall(String userInput){
if (isInvalidLength(userInput))
throw new IllegalArgumentException("3자리의 숫자를 입력해야 합니다.");
if (isInvalidRange(userInput))
throw new IllegalArgumentException("숫자의 범위가 올바르지 않습니다.");
Comment on lines +20 to +23

Choose a reason for hiding this comment

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

볼을 만드는 메소드에서 검증 책임을 같이 수행하고 있네요. 검증하는 로직을 메소드로 분리하면 좋을 것 같아요!


List<Integer> ball = new ArrayList<>();
for (int i = 0; i < 3; i++)

Choose a reason for hiding this comment

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

Suggested change
for (int i = 0; i < 3; i++)
for (int i = 0; i < BALL_NUM; i++)

ball.add(userInput.charAt(i) - '0');

if (isDuplicated(ball))
throw new IllegalArgumentException("중복이 없는 3자리의 숫자를 입력해야 합니다.");

return new GameBall(ball);
}

public static GameBall createRandomBall (){
Set<Integer> ball = new LinkedHashSet<>();

while(ball.size() < BALL_NUM) {
int randomNumber = Randoms.pickNumberInRange(1, 9);
ball.add(randomNumber);
}

return new GameBall(new ArrayList<>(ball));
}

public GameStatus getStatus(GameBall other) {
int strike = 0;
int ball = 0;

for (int i = 0; i < this.ballNumbers.size(); i++) {
if (other.ballNumbers.get(i).equals(this.ballNumbers.get(i))) strike++;
else if (other.ballNumbers.contains(this.ballNumbers.get(i))) ball++;
}

return new GameStatus(ball, strike);
}

private static boolean isInvalidLength(String input) {
return input.length() != BALL_NUM;
}

private static boolean isDuplicated(List<Integer> input) {
return input.size() != (new HashSet<Integer>(input)).size();
}

private static boolean isInvalidRange(String input) {
for (int i = 0; i < input.length(); i++) {
int num = input.charAt(i) - '0';
if (num > BALL_MAX || num < BALL_MIN) return true;
}
return false;
}

}
35 changes: 35 additions & 0 deletions src/main/java/baseball/game/model/GameCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package baseball.game.model;

import java.util.Arrays;

public enum GameCommand {
QUIT(2),
CONTINUE(1);

private final int number;

GameCommand(int number) {
this.number = number;
}

public static GameCommand getCommand(String userInput) {
int commandNumber;
try {
commandNumber = Integer.parseInt(userInput);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("숫자를 입력해야 합니다.");
}
Comment on lines +16 to +21

Choose a reason for hiding this comment

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

파싱하는 로직도 분리하면 좋을 것 같아요!


GameCommand command = Arrays.stream(values())
.filter(value -> value.number == commandNumber)
.findAny()
.orElseThrow(() -> new IllegalArgumentException("올바른 숫자가 입력되지 않았습니다."));

return command;
}

public boolean isContinue() {
return this == CONTINUE;
}

}
31 changes: 31 additions & 0 deletions src/main/java/baseball/game/model/GameStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package baseball.game.model;

import java.util.Objects;

public class GameStatus {
public int ball;
public int strike;

public GameStatus(int ball, int strike) {
this.ball = ball;
this.strike = strike;
}

public boolean isContinue() {
return !(strike == 3 && ball == 0);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GameStatus that = (GameStatus) o;
return ball == that.ball && strike == that.strike;
}

@Override
public int hashCode() {
return Objects.hash(ball, strike);
}

}
29 changes: 29 additions & 0 deletions src/main/java/baseball/inout/UserInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package baseball.inout;


import camp.nextstep.edu.missionutils.Console;

public class UserInput {

private UserInput() {
}

/**
* 숫자 입력
*/
public static String getNum() {
System.out.print("숫자를 입력해주세요 : ");
String input = Console.readLine().trim();
return input;
}

/**
* 게임 진행여부 입력
*/
public static String isContinue() {

Choose a reason for hiding this comment

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

메소드 네이밍이 어색합니다. isContinue()(계속되는가)라고 물어보면 예/아니오(boolean값)이 반환될 것이 예상되는데 String이 반환되네요.

System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.");
String input = Console.readLine().trim();
return input;
}

}
30 changes: 30 additions & 0 deletions src/main/java/baseball/inout/UserOutput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package baseball.inout;

import baseball.game.model.GameStatus;

public class UserOutput {

private UserOutput() {
}

public static void initMessage() {
System.out.println("숫자 야구 게임을 시작합니다.");
}

public static void printStatus(GameStatus status) {
if (status.ball == 0 && status.strike == 0)
System.out.println("낫싱");
else if (status.ball == 0)
System.out.println(status.strike + "스트라이크");
else if (status.strike == 0)
System.out.println(status.ball + "볼");
else
System.out.println(status.ball + "볼 " + status.strike + "스트라이크");
Comment on lines +15 to +22

Choose a reason for hiding this comment

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

view에서 출력 외 로직이 동작하는 것이 어색합니다!

Copy link
Author

Choose a reason for hiding this comment

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

그렇군요..!! GameStatus로 옮기는 것을 고려해봐야겠네요
view에서는 단순히 문자열을 받아서 출력하는 것이 좋은가요?

Choose a reason for hiding this comment

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

저는 입출력 클래스는 메인 서비스 로직의 영향을 받지 않아야된다고 생각합니다.
그런데 지금처럼 출력이 아닌 로직을 포함할 경우, 볼과 스트라이크를 구하는 방식이 변경된다면 '게임 결과'가 변경되는 것인데 '게임 결과'가 아닌 '출력'의 로직을 수정해야 합니다. 이는 굉장히 어색한 책임 같습니다.
'출력'을 위한 객체가 출력이 아닌 책임까지 갖게 되면 책임이 모호해져서, 입출력에는 최대한 로직을 넣지 않는 것을 선호하는 편입니다!


}

public static void printEndMessage() {
System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료");
}

}
59 changes: 59 additions & 0 deletions src/test/java/baseball/game/model/GameBallTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package baseball.game.model;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

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

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

class GameBallTest {

@ParameterizedTest
@DisplayName("GameBall 생성 동작 테스트")
@ValueSource(strings = {"123", "145", "671"})
void createBall(String input) {
// given

// when & then
assertThatCode( () -> {
GameBall.createBall(input);
}).doesNotThrowAnyException();
}


@ParameterizedTest
@DisplayName("GameBall 생성 예외 테스트")
@ValueSource(strings = {"111", "122", "1325", "a11"})
void createBallErrors(String input) {
// given

// when & then
assertThatThrownBy( () -> {
GameBall.createBall(input);
}).isInstanceOf(IllegalArgumentException.class);
}

@ParameterizedTest
@DisplayName("getStatus 생성 동작 테스트")
@CsvSource(value = {"456:0:0",
"516:1:0", "156:0:1", "231:3:0", "123:0:3"}, delimiter = ':'
)
void getStatus(String input, int ball, int strike) {
// given
GameBall computer = GameBall.createBall("123");
GameBall player = GameBall.createBall(input);

// when
GameStatus gameStatus = computer.getStatus(player);

// then
assertThat(gameStatus).isEqualTo(new GameStatus(ball, strike));
}

}
Loading