diff --git a/src/main/java/baseball/README.md b/src/main/java/baseball/README.md new file mode 100644 index 000000000..e81ff95f2 --- /dev/null +++ b/src/main/java/baseball/README.md @@ -0,0 +1,37 @@ +# 숫자야구게임 +### 룰 +1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임 + +같은 수가 같은 자리에 있으면 `스트라이크` + +같은 수가 다른 자리에 있으면 `볼` + +같은 수가 전혀 없으면 `포볼 또는 낫싱` + +여러번 반복해 정보를 얻으며 상대방(컴퓨터)의 수를 맞추면 승리 + +e.g. 상대방(컴퓨터)의 수가 425일 때 123을 제시한 경우 : `1스트라이크`, 456을 제시한 경우 : `1볼 1스트라이크`, 789를 제시한 경우 : `낫싱` + +### 작동 방식 + +1) 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택 +2) 사용자는 컴퓨터가 생각하고 있는 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한 결과 출력 +3) 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임 종료 +4) 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다 + +### 기능 목록 +엔티티 +- [x] Ball: 세자리 숫자 저장하는 엔티티 클래스 +- [x] BallStatus: 유저 Ball과 랜덤 Ball 비교한 결과를 저장하는 엔티티 클래스 +- [x] Action: 동작들을 담는 enum 클래스 + +서비스 +- [x] BaseballService + - isBall: 볼 여부 판별 + - isStrike: 스트라이크 여부 판별 + - compare: RandomNum, UserNum 비교해 BallStatus 반환 +- [x] RandomNumGenerator: 임의의 서로다른 3자리 수 생성 + + +- [x] InputView: 사용자로부터 입력 받는 뷰 담당하는 클래스 +- [x] OutputView: 출력 하는 뷰 담당하는 클래스 \ No newline at end of file diff --git a/src/main/java/baseball/application.java b/src/main/java/baseball/application.java new file mode 100644 index 000000000..c40103916 --- /dev/null +++ b/src/main/java/baseball/application.java @@ -0,0 +1,31 @@ +package baseball; + +import baseball.domain.Ball; +import baseball.domain.Baseball; +import baseball.domain.BaseballStatus; +import baseball.service.BaseballService; +import baseball.service.RandomBallGenerator; +import baseball.view.InputView; +import baseball.view.OutputView; + +import java.util.stream.Collectors; + +public class application { + public static void main(String[] args) throws Exception { + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + RandomBallGenerator randomBallGenerator = new RandomBallGenerator(); + BaseballService baseballService = new BaseballService(); + + do { + Baseball randomBaseball = new Baseball(randomBallGenerator.makeNum()); + BaseballStatus baseballStatus; + do { + Baseball userBaseball = inputView.inputBall(); + baseballStatus = baseballService.compare(userBaseball, randomBaseball); + outputView.printBaseballStatus(baseballStatus); + } while (!outputView.exitGame(baseballStatus)); + + }while(inputView.resumeGame()); + } +} diff --git a/src/main/java/baseball/domain/Action.java b/src/main/java/baseball/domain/Action.java new file mode 100644 index 000000000..a138e213d --- /dev/null +++ b/src/main/java/baseball/domain/Action.java @@ -0,0 +1,5 @@ +package baseball.domain; + +public enum Action { + 볼, 스트라이크, 낫싱 +} diff --git a/src/main/java/baseball/domain/Ball.java b/src/main/java/baseball/domain/Ball.java new file mode 100644 index 000000000..6670ec54b --- /dev/null +++ b/src/main/java/baseball/domain/Ball.java @@ -0,0 +1,13 @@ +package baseball.domain; + +public class Ball { + private final int number; + + public Ball(final int number) { + this.number = number; + } + + public int getNumber() { + return number; + } +} diff --git a/src/main/java/baseball/domain/Baseball.java b/src/main/java/baseball/domain/Baseball.java new file mode 100644 index 000000000..82531ee08 --- /dev/null +++ b/src/main/java/baseball/domain/Baseball.java @@ -0,0 +1,28 @@ +package baseball.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Baseball { + private final List baseballs; + + public Baseball(final List ballList){ + this.baseballs= new ArrayList<>(ballList); + isDistinct(); + } + + public List getBaseballs() { + return Collections.unmodifiableList(baseballs); + } + + public void isDistinct(){ + for(int i=0;i ball.getNumber()>0 && ball.getNumber()<=9)){ + throw new RuntimeException("범위를 벗어납니다."); + } + } + + public Baseball inputBall() throws RuntimeException { + Scanner scanner = new Scanner(System.in); + System.out.print("숫자를 입력해 주세요 : "); + String input = scanner.next(); + List list = Arrays.stream(input.split("")).mapToInt(Integer::parseInt).mapToObj(Ball::new).collect(Collectors.toList()); + isValidInput(list); + return new Baseball(list); + } + + public boolean resumeGame() { + Scanner scanner = new Scanner(System.in); + System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + int input = scanner.nextInt(); + return input == 1; + } + + +} diff --git a/src/main/java/baseball/view/OutputView.java b/src/main/java/baseball/view/OutputView.java new file mode 100644 index 000000000..1ed7c6d1e --- /dev/null +++ b/src/main/java/baseball/view/OutputView.java @@ -0,0 +1,33 @@ +package baseball.view; + +import baseball.domain.Action; +import baseball.domain.BaseballStatus; + +public class OutputView { + public String outputBaseballStatus(BaseballStatus baseballStatus) { + String result = ""; + + if (baseballStatus.existsBall()) { + result += baseballStatus.getBall() + Action.볼.toString() + " "; + } + if (baseballStatus.existsStrike()) { + result += baseballStatus.getStrike() + Action.스트라이크.toString(); + } + if (baseballStatus.nothing()) { + return Action.낫싱.toString(); + } + return result; + } + + public void printBaseballStatus(BaseballStatus baseballStatus) { + System.out.println(outputBaseballStatus(baseballStatus)); + } + + public boolean exitGame(BaseballStatus ballStatus) { + if (ballStatus.exitGame()) { + System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); + return true; + } + return false; + } +} diff --git a/src/test/java/baseball/service/BaseballServiceTest.java b/src/test/java/baseball/service/BaseballServiceTest.java new file mode 100644 index 000000000..f3ec8422a --- /dev/null +++ b/src/test/java/baseball/service/BaseballServiceTest.java @@ -0,0 +1,48 @@ +package baseball.service; + +import baseball.domain.Ball; +import baseball.domain.Baseball; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BaseballServiceTest { + BaseballService baseballService; + + @BeforeEach + void setUp(){ + baseballService= new BaseballService(); + } + + @Test + void isStrikeTest(){ + Baseball userBall = new Baseball(Arrays.asList(new Ball(1),new Ball(2),new Ball(3))); + Baseball randomBall = new Baseball(Arrays.asList(new Ball(1),new Ball(2),new Ball(3))); + for(int i=0;i<3;i++){ + assertThat(baseballService.isStrike(userBall.getBaseballs().get(i), randomBall.getBaseballs().get(i))).isTrue(); + } + } + + @Test + void isBallTest(){ + Baseball userBall = new Baseball(Arrays.asList(new Ball(1),new Ball(3),new Ball(2))); + Baseball randomBall = new Baseball(Arrays.asList(new Ball(2),new Ball(1),new Ball(3))); + for(int i=0;i<3;i++){ + assertThat(baseballService.isBall(userBall.getBaseballs().get(i), randomBall)).isTrue(); + } + } + + @ParameterizedTest + @CsvSource({"1,2,6,1,9,2,1,1", "3,7,5,7,3,9,2,0", "1,2,3,4,5,6,0,0", "3,4,8,3,4,2,0,2"}) + void compareTest(int u1,int u2,int u3,int r1,int r2,int r3, int e1, int e2) throws Exception { + Baseball userBall = new Baseball(Arrays.asList(new Ball(u1),new Ball(u2),new Ball(u3))); + Baseball randomBall = new Baseball(Arrays.asList(new Ball(r1),new Ball(r2),new Ball(r3))); + + assertThat(new int[]{baseballService.compare(userBall, randomBall).getBall(), baseballService.compare(userBall, randomBall).getStrike()}).isEqualTo(new int[]{e1, e2}); + } +} diff --git a/src/test/java/baseball/service/RandomBallTest.java b/src/test/java/baseball/service/RandomBallTest.java new file mode 100644 index 000000000..27182bc5c --- /dev/null +++ b/src/test/java/baseball/service/RandomBallTest.java @@ -0,0 +1,37 @@ +package baseball.service; + +import baseball.domain.Ball; +import baseball.domain.Baseball; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RandomBallTest { + RandomBallGenerator randomBallGenerator; + + @BeforeEach + public void setUp() { + randomBallGenerator = new RandomBallGenerator(); + } + + @Test + @DisplayName("3개의 서로다른 숫자가 생성되는지 확인하는 테스트입니다.") + public void makeNumTest() { + List list = randomBallGenerator.makeNum(); + + Baseball randomNum = new Baseball(list); + assertThat(randomNum.getBaseballs().size()).isEqualTo(3); + } + + @Test + @DisplayName("1부터 9 사이의 값인지 확인하는 테스트입니다.") + public void rangeTest() { + List list = randomBallGenerator.makeNum(); + + assertThat(list.stream().filter(ball -> ball.getNumber() >= 1 && ball.getNumber() <= 9).count()).isEqualTo(3); + } +} diff --git a/src/test/java/baseball/view/InputViewTest.java b/src/test/java/baseball/view/InputViewTest.java new file mode 100644 index 000000000..dc0ad2009 --- /dev/null +++ b/src/test/java/baseball/view/InputViewTest.java @@ -0,0 +1,44 @@ +package baseball.view; + +import baseball.domain.Baseball; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.*; + +import static org.assertj.core.api.Assertions.assertThat; + +public class InputViewTest { + InputView inputView; + Baseball baseball; + + @BeforeEach + void setUp() { + inputView = new InputView(); + String input = "349"; + OutputStream out = new ByteArrayOutputStream(); + System.setOut(new PrintStream(out)); + InputStream in = new ByteArrayInputStream(input.getBytes()); + System.setIn(in); + } + + @Test + void countTest() throws RuntimeException { + baseball = inputView.inputBall(); + assertThat(baseball.getBaseballs().size()).isEqualTo(3); + } + + @Test + void rangeTest() throws RuntimeException { + baseball = inputView.inputBall(); + assertThat(baseball.getBaseballs().stream().filter(b -> b.getNumber() < 10 && b.getNumber() > 0).count()).isEqualTo(3); + } + + @Test + void resumeTest() { + String data = "1"; + InputStream in = new ByteArrayInputStream(data.getBytes()); + System.setIn(in); + assertThat(inputView.resumeGame()).isTrue(); + } +} diff --git a/src/test/java/baseball/view/OutputViewTest.java b/src/test/java/baseball/view/OutputViewTest.java new file mode 100644 index 000000000..788fcbedf --- /dev/null +++ b/src/test/java/baseball/view/OutputViewTest.java @@ -0,0 +1,40 @@ +package baseball.view; + +import baseball.domain.BaseballStatus; +import org.junit.jupiter.api.BeforeEach; +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 static org.assertj.core.api.Assertions.assertThat; + +public class OutputViewTest { + OutputView outputView; + + @BeforeEach + void setUp() { + outputView = new OutputView(); + } + + @ParameterizedTest + @CsvSource({"1,2,1볼 2스트라이크", "0,0,낫싱", "0,2,2스트라이크"}) + void outputViewTest(int ball, int strike, String expected) throws Exception { + BaseballStatus baseballStatus = new BaseballStatus(ball, strike); + assertThat(outputView.outputBaseballStatus(baseballStatus)).isEqualTo(expected); + } + + @Test + @DisplayName("조건에 만족하면 게임을 끝내는 메소드 테스트") + void exitGameTest() throws Exception { + BaseballStatus baseballStatus = new BaseballStatus(0,3); + assertThat(outputView.exitGame(baseballStatus)).isTrue(); + } + + @Test + @DisplayName("조건에 만족하지 않으면 게임 계속하는 메소드 테스트") + void exitGameFalseTest() throws Exception { + BaseballStatus baseballStatus = new BaseballStatus(1,2); + assertThat(outputView.exitGame(baseballStatus)).isFalse(); + } +}