Boot Camp/[우테코] 프리코스

[2주차] baseball game 미션

계란💕 2022. 11. 9. 14:42

 

  • baseball game 요구사항
<hide/>
미션 - 숫자 야구
🔍 진행 방식
미션은 기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항 세 가지로 구성되어 있다.
세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다.
기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.
📮 미션 제출 방법
미션 구현을 완료한 후 GitHub을 통해 제출해야 한다.
GitHub을 활용한 제출 방법은 프리코스 과제 제출 문서를 참고해 제출한다.
GitHub에 미션을 제출한 후 우아한테크코스 지원 사이트에 접속하여 프리코스 과제를 제출한다.
자세한 방법은 제출 가이드 참고
Pull Request만 보내고 지원 플랫폼에서 과제를 제출하지 않으면 최종 제출하지 않은 것으로 처리되니 주의한다.
🚨 과제 제출 전 체크 리스트 - 0점 방지
기능 구현을 모두 정상적으로 했더라도 요구 사항에 명시된 출력값 형식을 지키지 않을 경우 0점으로 처리한다.
기능 구현을 완료한 뒤 아래 가이드에 따라 테스트를 실행했을 때 모든 테스트가 성공하는지 확인한다.
테스트가 실패할 경우 0점으로 처리되므로, 반드시 확인 후 제출한다.
테스트 실행 가이드
터미널에서 java -version을 실행하여 Java 버전이 11인지 확인한다. 또는 Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 11로 실행되는지 확인한다.
터미널에서 Mac 또는 Linux 사용자의 경우 ./gradlew clean test 명령을 실행하고,
Windows 사용자의 경우 gradlew.bat clean test 명령을 실행할 때 모든 테스트가 아래와 같이 통과하는지 확인한다.
BUILD SUCCESSFUL in 0s
🚀 기능 요구 사항
기본적으로 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다.

같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 같은 수가 전혀 없으면 낫싱이란 힌트를 얻고, 그 힌트를 이용해서 먼저 상대방(컴퓨터)의 수를 맞추면 승리한다.
예) 상대방(컴퓨터)의 수가 425일 때
123을 제시한 경우 : 1스트라이크
456을 제시한 경우 : 1볼 1스트라이크
789를 제시한 경우 : 낫싱
위 숫자 야구 게임에서 상대방의 역할을 컴퓨터가 한다. 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택한다. 게임 플레이어는 컴퓨터가 생각하고 있는 서로 다른 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한 결과를 출력한다.
이 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임이 종료된다.
게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다.
사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.
입출력 요구 사항
입력
서로 다른 3자리의 수
게임이 끝난 경우 재시작/종료를 구분하는 1과 2 중 하나의 수
출력
입력한 수에 대한 결과를 볼, 스트라이크 개수로 표시
1볼 1스트라이크
하나도 없는 경우
낫싱
3개의 숫자를 모두 맞힐 경우
3스트라이크
3개의 숫자를 모두 맞히셨습니다! 게임 종료
게임 시작 문구 출력
숫자 야구 게임을 시작합니다.
실행 결과 예시
숫자 야구 게임을 시작합니다.
숫자를 입력해주세요 : 123
1볼 1스트라이크
숫자를 입력해주세요 : 145
1볼
숫자를 입력해주세요 : 671
2볼
숫자를 입력해주세요 : 216
1스트라이크
숫자를 입력해주세요 : 713
3스트라이크
3개의 숫자를 모두 맞히셨습니다! 게임 종료
게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.
1
숫자를 입력해주세요 : 123
1볼
...
🎯 프로그래밍 요구 사항
JDK 11 버전에서 실행 가능해야 한다. JDK 11에서 정상적으로 동작하지 않을 경우 0점 처리한다.
프로그램 실행의 시작점은 Application의 main()이다.
build.gradle 파일을 변경할 수 없고, 외부 라이브러리를 사용하지 않는다.
Java 코드 컨벤션 가이드를 준수하며 프로그래밍한다.
프로그램 종료 시 System.exit()를 호출하지 않는다.
프로그램 구현이 완료되면 ApplicationTest의 모든 테스트가 성공해야 한다. 테스트가 실패할 경우 0점 처리한다.
프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 이름을 수정하거나 이동하지 않는다.
추가된 요구 사항
indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
3항 연산자를 쓰지 않는다.
함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
JUnit 5와 AssertJ를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다.
테스트 도구 사용법이 익숙하지 않다면 test/java/study를 참고하여 학습한 후 테스트를 구현한다.
라이브러리
camp.nextstep.edu.missionutils에서 제공하는 Randoms 및 Console API를 사용하여 구현해야 한다.
Random 값 추출은 camp.nextstep.edu.missionutils.Randoms의 pickNumberInRange()를 활용한다.
사용자가 입력하는 값은 camp.nextstep.edu.missionutils.Console의 readLine()을 활용한다.
사용 예시
List<Integer> computer = new ArrayList<>();
while (computer.size() < 3) {
    int randomNumber = Randoms.pickNumberInRange(1, 9);
    if (!computer.contains(randomNumber)) {
        computer.add(randomNumber);
    }
}
✏️ 과제 진행 요구 사항
미션은 java-baseball 저장소를 Fork & Clone해 시작한다.
기능을 구현하기 전 docs/README.md에 구현할 기능 목록을 정리해 추가한다.
Git의 커밋 단위는 앞 단계에서 docs/README.md에 정리한 기능 목록 단위로 추가한다.
커밋 메시지 컨벤션 가이드를 참고해 커밋 메시지를 작성한다.
과제 진행 및 제출 방법은 프리코스 과제 제출 문서를 참고한다.

 

 

  • README
<hide/>
## 구현 기능 목록

1. 컴퓨터가 세 자리 수 target을 랜덤으로 생성한다.
2. 사용자로 부터 3자리 숫자를 입력 받는다.
   - 사용자의 입력값이 유효한지 체크한다.
   - 세 자리 숫자 여부, 중복 여부, 0 포함 여부
   - 위 조건을 만족하지 않으면 IllegalArgumentException 으로 예외 처리 한다.
3. 완전 탐색을 통해 볼, 스트라이크를 체크해서 문장을 출력한다.
4. 사용자가 입력한 수를 체크하면서 3스트라이크가 나올 때까지 반복한다.
  - 3스트라이크가 나오면 게임 종료하거나 다음 상태를 선택할 수 있다.
  - 1 또는 2를 입력해야 한다. 다른 값을 입력하는 경우, IllegalArgumentException로 예외 처리한다. 
5. 게임 다시 시작 또는 종료

 

 

  • MySol)
<hide/>
package baseball;

import camp.nextstep.edu.missionutils.Console;
import camp.nextstep.edu.missionutils.Randoms;
import java.util.ArrayList;
import java.util.List;

class Node {

    String num;
    int strike;
    int ball;

    public Node(String num, int strike, int ball) {
        this.num = num;
        this.strike = strike;
        this.ball = ball;
    }
}

public class Application {


    static boolean restartGameYn = false;

    static boolean exitGameYn = false;


    public static String makeTargetNum() {

        List<Integer> target = new ArrayList<>();

        while (target.size() < 3) {
            int n = Randoms.pickNumberInRange(1, 9);
            if (!target.contains(n)) {
                target.add(n);
            }
        }

        String stringOfTargetNum = "";
        for (int n : target) {
            stringOfTargetNum += n;
        }

        return stringOfTargetNum;

    }


    public static void isValidate(String input) {

        char[] inputToArr = input.toCharArray();

        for (char c : inputToArr) {
            if (!Character.isDigit(c)) {
                throw new IllegalArgumentException("오직 숫자만 입력 가능합니다.");
            }
        }

        if (input.length() != 3) {
            throw new IllegalArgumentException("세 자리수만 입력 가능합니다.");
        }

        if (Integer.parseInt(input) < 123 || 987 < Integer.parseInt(input)) {
            throw new IllegalArgumentException("중복 없는 세 자리수만 입력 가능합니다.");
        }

        if (input.contains("0")) {
            throw new IllegalArgumentException("0을 제외한 세 자리 수를 입력하세요.");
        }

        if (inputToArr[0] == inputToArr[1] || inputToArr[1] == inputToArr[2]
            || inputToArr[0] == inputToArr[2]) {
            throw new IllegalArgumentException("중복이 없는 세 자리 수를 입력하세요.");
        }

    }


    public static Node baseball(String target, String num) {

        Node node = new Node(num, 0, 0);

        for (int i = 0; i < 9; ++i) {

            int x = i / 3;
            int y = i % 3;

            if (x == y && target.charAt(x) == num.charAt(y)) {
                ++node.strike;
            } else if (target.charAt(x) == num.charAt(y)) {
                ++node.ball;
            }

        }

        return node;
    }


    public static void restartOrExit(String str) {

        if (str.equals("2")) {
            exitGameYn = true;
            return;
        }

        if (str.equals("1")) {
            restartGameYn = true;
            return;
        }

        throw new IllegalArgumentException("1 또는 2만 입력 가능합니다.");

    }


    public static void printStrikeAndBall(Node node) {

        if (node.strike == 3) {
            System.out.printf("%d스트라이크%n", node.strike);
            System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료\n" + "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요");
            String exitYn = Console.readLine();
            restartOrExit(exitYn);

        } else if (node.strike + node.ball == 0) {
            System.out.println("낫싱");

        } else {

            if (node.ball == 0) {
                System.out.printf("%d스트라이크%n", node.strike);

            } else if (node.strike == 0) {
                System.out.printf("%d볼%n", node.ball, node.strike);

            } else {
                System.out.printf("%d볼 %d스트라이크%n", node.ball, node.strike);

            }

        }

    }


    public static void main(String[] args) {
        // TODO: 프로그램 구현

        String target = makeTargetNum();
        System.out.println("숫자 야구 게임을 시작합니다.");

        while (true) {

            System.out.println("숫자를 입력해주세요 : ");
            String input = Console.readLine();
            isValidate(input);
            Node node = baseball(target, input);
            printStrikeAndBall(node);

            if (exitGameYn) {
                break;
            }
            if (restartGameYn) {
                restartGameYn = false;
                target = makeTargetNum();
            }

        }

    }

}

 

  • 노드를 이용해서 target number와 하나씩 비교하면서 스트라이크와 볼을 체크할 수 있는 baseball() 메서드를 이용했다.
  • 요구사항을 맞추기 위해 메서드를 모두 분리했다.
  • 들여쓰기 요구 사항을 충족시키는 것도 어려웠다.
    • 그래서 이중 for문에 if문만 들어가도 3 depth만큼 들어갔기 때문에 for문을 하나 제거했다.
    • for(i = 0 ; i < 3) 안에 for(j = 0 ; j < 3) 에 있는 구조였지만 단일 for문으로 바꿨다.
    • for(i = 0 ; i < 9) 로 바꾸고 x = i / 3, y = i % 3 이라는 새로운 변수를 선언했다.
    • 좋은 방법이라고 하기는 어렵다. 들여쓰기를 잘 할 수 있는 방법을 생각해봐야겠다.
  • restartOrExit() 라는 메서드를 만들 때 문제가 있었다.
    • if(str.equals("2")) 인 경우보다 if(!str.equals("2") || !str.equals("3"))를 먼저 예외처리하면 2, 3을 입력해도 예외처리가 돼버렸다. 
    • 이유를 아직도 모르겠다.
  • 테스트 코드 작성을 연습하도록 예시가 주어졌다. 그런데 이게 왜 안 돌아가나 했더니 인텔리제이가 열려있는 폴더의 상위 폴더명 중 한글 이름이 있었다. 그래서 테스트 코드가 모두 실패로 떴다.
    • 앞으로는 영문으로 폴더명을 지어야겠다.
  • 전에 스터디에서 풀었던 백준의 숫자 야구 라는 문제와 유사하다.

참고) https://www.acmicpc.net/problem/2503

 

2503번: 숫자 야구

첫째 줄에는 민혁이가 영수에게 몇 번이나 질문을 했는지를 나타내는 1 이상 100 이하의 자연수 N이 주어진다. 이어지는 N개의 줄에는 각 줄마다 민혁이가 질문한 세 자리 수와 영수가 답한 스트

www.acmicpc.net

 

 

 

 

 

느낀 점

  • 테스트 코드 작성 연습을 꼭 해야겠다.
  • 커밋 쪼개는 연습을 해야겠다.

 

'Boot Camp > [우테코] 프리코스' 카테고리의 다른 글

[4주차] bridge 미션  (0) 2022.11.20
[3주차] lotto 미션  (1) 2022.11.16
[1주차] on-boarding 미션  (0) 2022.11.01