[JAVA] 숫자 야구 게임 개인과제 회고

2024. 10. 28. 19:48·❄️ 내일배움캠프 7기
728x90
반응형

자바 개인 과제로 숫자 야구 게임을 하게 되었다.

최종 목표는 말 그대로 숫자 야구 게임을 구현하는 것이었다.


숫자 야구 게임의 규칙은 임의의 숫자(정답) 3개를 이용자가 숫자를 3개씩 던져가며 힌트를 통해 맞추는 것이다.

우선 처음에는 주어진 조건보고, 무턱대고 혼자 박치기하며 만든 코드가 있고,

코드 제출 후 피드백을 받고, 리팩토링을 다시 진행 중이다 :) 


우선 Lv1 - 4까지 있는데 1부터 차근차근 진행하였다.

 

레벨 1 요구사항 - 정답 숫자 생성

  • 정답은 서로 다른 3자리 수이다.
  • 각 자리는 1~9 사이의 숫자이다. 0은 사용할 수 없습니다.
  • 동일한 숫자는 사용될 수 없다. 즉, 숫자는 중복되지 않아야 한다.
  • Random 클래스, HashSet 클래스
import java.util.*;

public class BaseBallGame {
    HashSet<String> answerSet = new LinkedHashSet<>(); // 정답 숫자를 담을 컬렉션
    Scanner sc = new Scanner(System.in); // 입력값
    Random random = new Random(); // 랜덤 수 객체 생성

    int strikeCount; // 스트라이크 수
    int ballCount; // 볼 수

    // 기본 생성자 -> 객체 생성시 바로 정답값 생성
    public BaseBallGame() {
        while (answerSet.size() < 3) { // answerSet 의 길이가 3미만이면 무한 반복
            int rNum = random.nextInt(9) + 1;
            String rNumString = "" + rNum;
            answerSet.add(rNumString); // 랜덤 값 추가
        }
    }

아직 클래스랑 객체에 대해서 어떤식으로 분리하고 나눠야할지 잘 몰라서, 객체를 생성하고 실행하면 바로 랜덤 숫자가 생성되도록 기본 생성자에 랜덤 값을 넣었다.

 

기본적으로 조건에 hashSet(중복허용x)과 Random 클래스를 사용해야한다고 적혀있어, hashSet을 사용하였고,

나중에 정답 입력값이랑 비교할 때 인덱스로 뽑아서 비교하는게 더 편할 것 같아 문자열로 변환해주고,
또한 hashset을 문자열 hashset으로 값을 받아, 문자열 자체 길이가 3이 넘지 않도록해 3자리 수라는 제한을 걸어주었다.


 

레벨 1 요구사항 - 정답을 맞추기 위해 숫자를 입력하기

  • 서로 다른 3자리 수를 입력할 수 있다.
  • 동일한 숫자는 사용될 수 없다. 즉, 숫자는 중복되지 않아야 한다.
  • 숫자만 입력 가능하며, 문자는 작성할 수 없다.
  • 입력값이 문자, 중복되는 값처럼 요구사항과 맞지 않을 경우 오류 문구 생성
  • 정답을 맞출 때 까지 계속 시도할 수 있어야함, 맞추면 축하 메세지 출력

우선 마지막 요구사항을 보면 계속 시도할 수 있어야 하기 때문에 while문을 계속 돌려주었다.

 

그리고, 문자열에 숫자를 입력 받는데, 문자가 들어가도 안되고 요구사항과 맞지 않을 경우도 오류가 뜨는 걸로 만들어야 하기에 직접 강제로 에러를 발생시켜주면서, 예외처리를 해주었다.

 

또한 입력받은 값은 문자열 형식으로 "253" 이렇게 저장이 되기때문에, 인덱스로 하나씩 문자를 뽑아 HashSet에 저장해주었다.

// 게임 시작 시, 입력 받을 값
    public void gamePlay() {
        HashSet<String> inputSet = new LinkedHashSet<>();

        while (true) {
            // 입력값 숫자를 담을 컬렉션
            System.out.println("< 게임을 시작합니다 >");
            System.out.println("숫자를 입력하세요");

            try {
                String inputNumber = sc.nextLine(); // 문자열(숫자) 입력
                if (inputNumber.length() > 3) {
                    // 만약 inputNumber 의 인덱스가 3보다 큰 값을 입력하면 강제로 에러 발생 시키기
                    throw new ArithmeticException("올바르지 않은 입력값입니다.");
                }
                // inputNumber 의 값을 하나씩 추출
                for (int i = 0; i < inputNumber.length(); i++) {
                    String inNum = inputNumber.substring(i, i + 1);
                    inputSet.add(inNum);
                }
            } catch (ArithmeticException | InputMismatchException e) {
                // int가 아닌 다른 값을 입력했을 시
                System.out.println("올바르지 않은 입력값입니다.");
            }
            // 입력값을 받았는지 검증후, 게임 진행 횟수 증가
            gameNumber++;

하지만, 다시 보았을 때 강제로 에러를 발생시키지 않아도, 올바르지 않은 입력값이라고 문구 뜨도록 할 수 있었다,

클린한 코드를 생각하면, 예외처리를 많이 하지 않아도 되는 상황을 만드는게 좋을 것 같다.


 

 

레벨 1 요구사항 - 결과값 출력 및 게임 로직 적용

  • 정답과 입력값을 비교해 힌트를 “볼, 스트라이크, 아웃”으로 표시합니다.
  • 스트라이크 : 입력값과 정답을 비교해 같은 자리에 같은 숫자가 있는 경우
  • 볼 : 숫자는 같지만, 자리는 다른 경우
  • 아웃 : 숫자도, 자리도 다른 경우
  • 입력한 3자리 숫자가 정답과 같은 경우, 게임이 종료됩니다.
  • ‘3 스트라이크’라면, 정답에 해당합니다.
  • 정답일 때, 표시하고 싶은 메세지는 여러분이 지정해주세요.
  • 올바르지 않은 입력값에 대해서는 오류 문구를 보여주세요.
  • 입력값이 문자, 중복되는 값처럼 요구사항과 맞지 않을 경우
  • 정답을 맞출 때까지 계속해서 시도할 수 있어야 하며, 정답을 맞추면 축하 메시지를 출력한다.

여기서 문제가 가장 많이 발생하고, 머리를 싸맸던 부분이다.

중복값을 받지 않기위해 hashset을 사용하였는데, hashset에는 get을 사용하여 값을 꺼내올 수 있는 방법이 없다.

hashset에서 값을 꺼내올땐 Iterator를 사용해서 값을 하나씩 다 빼와야한다.

 

그래서 우선 값을 다 빼오는 함수를 사용하였고, 입력한 값이 있을 때까지만 반복하도록 while 조건을 정했다.

그러고 값을 인덱스 기준으로 하나씩 비교를 했다.

 

strike 기준 : inputIterString의 인덱스 + 값과 answerIterString의 인덱스 + 값이 같아야 하기에, 문자열을 추출하는 charAt을 사용하였다.

ball 기준 : inputIterString의 값이 answerIterString에 포함만 되어있으면 되는 조건이었기에, 인덱스는 다르면서 값이 포함이 되었는지 확인하는 contains를 사용하였다.


//         스트라이크 : 입력값과 정답값을 비교해, 같은 자리에 같은 숫자가 있는 경우
//         set 컬렉션은 get 메소드가 없음 => Iterator 사용
            Iterator<String> inputIter = inputSet.iterator();
            Iterator<String> answerIter = answerSet.iterator();


            // hasNext = inputIter에 값이 있는지 true or false로 나옴
            while (inputIter.hasNext()) {
                // next()는 inputIter에 값 순서대로 출력
                String inputIterString = inputIter.next();
                String answerIterString = answerIter.next();

                // 문자 비교 : inputSet의 길이만큼 증가하고, 0번째 ~ 마지막 글자까지 비교하기
                for (int inputStringIdx = 0; inputStringIdx < inputIterString.length(); inputStringIdx++) {
                    for (int answerStringIdx = 0; answerStringIdx < answerIterString.length(); answerStringIdx++) {
                        if (inputIterString.charAt(inputStringIdx) == answerIterString.charAt(answerStringIdx)) {
                            strikeCount += 1;
                        } else if (inputIterString.charAt(inputStringIdx) != answerIterString.charAt(answerStringIdx) && answerSet.contains(inputIterString)) {
                            ballCount += 1;
                        } else {
                            break;
                        }
                    }
                }
            }


            if (strikeCount == 3) {
                        System.out.println("정답입니다!");
                        break;
                    } else if (strikeCount == 0 && ballCount == 0){
                        System.out.println("아웃입니다!");
                    } else {
                        System.out.println(strikeCount + "스트라이크 " + ballCount + "볼");
                        inputSet.clear(); // 입력했던 값 삭제
                        this.strikeCount = 0; // strikeCount 값 초기화
                        this.ballCount = 0; // ballCount 값 초기화
                    }

        }

    }
}

또한 3자리 수의 값이 모두 동일 할 때 정답을 출력하고,

3자리 수에 해당하는 값이 하나도 없을 때 아웃하게 되도록 구성하였다.

또한 결과를 보여줄 때는 항상, 입력했던 값이 초기화되고 스트라이크와 볼의 값도 초기화 시켜준다.

초기화를 시켜주지 않았을 때, 값이 계속 쌓여서 출력이 되는 불상사가 발생할 수 있다.

 

2단계 요구사항은 예외처리 위주였기 때문에, 따로 정리는 하지 않겠다.


BaseBallGame 클래스

package Lv2;

import java.util.*;

public class BaseBallGame {
    HashSet<String> answerSet = new LinkedHashSet<>(); // 정답 숫자를 담을 컬렉션
    Scanner sc = new Scanner(System.in); // 입력값
    Random random = new Random(); // 랜덤 수 객체 생성

    int strikeCount; // 스트라이크 수
    int ballCount; // 볼 수
    int gameNumber; // 게임 횟수
    int tryNumber; // 시도 횟수

    // 기본 생성자 -> 객체 생성시 바로 정답값 생성
    public BaseBallGame() {
        while (answerSet.size() < 3) { // answerSet 의 길이가 3미만이면 무한 반복
            int rNum = random.nextInt(9) + 1;
            String rNumString = "" + rNum;
            answerSet.add(rNumString); // 랜덤 값 추가
        }
    }

    // 게임 시작 시, 입력 받을 값
    public void gamePlay() {
        HashSet<String> inputSet = new LinkedHashSet<>();

        System.out.println("환영합니다! 원하시는 번호를 입력해주세요");
        System.out.println( "1. 게임시작하기 " + "2. 게임 기록 보기 " + "3. 종료하기");
        char gameStart = sc.next().charAt(0);

        switch (gameStart){
            case '1':
                // 입력값 숫자를 담을 컬렉션
                System.out.println("< 게임을 시작합니다 >");
                while(true){
                    System.out.println("숫자를 입력하세요");
                    try {
                        String inputNumber = sc.next(); // 문자열(숫자) 입력
                        if (inputNumber.length() > 3) {
                            // 만약 inputNumber 의 인덱스가 3보다 큰 값을 입력하면 강제로 에러 발생 시키기
                            throw new ArithmeticException("올바르지 않은 입력값입니다.");
                        }
                        // inputNumber 의 값을 하나씩 추출
                        for (int i = 0; i < inputNumber.length(); i++) {
                            String inNum = inputNumber.substring(i, i + 1);
                            inputSet.add(inNum);
                        }
                    } catch (ArithmeticException | InputMismatchException e) {
                        // int가 아닌 다른 값을 입력했을 시
                        System.out.println("올바르지 않은 입력값입니다. 숫자를 입력해주세요.");
                        break;
                    }

                    // 스트라이크 : 입력값과 정답값을 비교해, 같은 자리에 같은 숫자가 있는 경우
                    // set 컬렉션은 get 메소드가 없음 => Iterator 사용
                    Iterator<String> inputIter = inputSet.iterator();
                    Iterator<String> answerIter = answerSet.iterator();

                    // hasNext = inputIter에 값이 있는지 true or false로 나옴
                    while (inputIter.hasNext()) {
                        // next()는 inputIter에 값 순서대로 출력
                        String inputIterString = inputIter.next();
                        String answerIterString = answerIter.next();

                        // 문자 비교 : inputSet의 길이만큼 증가하고, 0번째 ~ 마지막 글자까지 비교하기
                        for (int inputStringIdx = 0; inputStringIdx < inputIterString.length(); inputStringIdx++) {
                            for (int answerStringIdx = 0; answerStringIdx < answerIterString.length(); answerStringIdx++) {
                                if (inputIterString.charAt(inputStringIdx) == answerIterString.charAt(answerStringIdx)) {
                                    strikeCount++;
                                } else if (inputIterString.charAt(inputStringIdx) != answerIterString.charAt(answerStringIdx) && answerSet.contains(inputIterString)) {
                                    ballCount++;
                                } else {
                                    break;
                                }
                            }
                        }
                    }

                    if (strikeCount == 3) {
                        System.out.println("정답입니다!");
                        break;
                    } else if (strikeCount == 0 && ballCount == 0){
                        System.out.println("아웃입니다!");
                    } else {
                        System.out.println(strikeCount + "스트라이크 " + ballCount + "볼");
                        inputSet.clear(); // 입력했던 값 삭제
                        this.strikeCount = 0; // strikeCount 값 초기화
                        this.ballCount = 0; // ballCount 값 초기화
                    }
                }
                gameNumber++;


            case '2':
                System.out.println("< 게임 기록 보기 >");
                System.out.println(gameNumber + "번째 게임 : 시도 횟수 - " + tryNumber);
                break;

            case '3':
                System.out.println("< 숫자 야구 게임을 종료합니다 >");
                answerSet.clear();
                break;

            default:
                System.out.println("올바른 숫자를 입력해주세요 !");
        }
    }
}

 

Main 클래스

public class Main {
    public static void main(String[] args) {

        BaseBallGame baseBallGame = new BaseBallGame();
        baseBallGame.gamePlay();
    }
}

아쉬웠던 점

1. 메서드 분리 : 별도 함수에서 값을 생성한 후 리턴을 받아 생성자에서는 값을 할당만 해주는 것.

2. 기본 생성자 :  생성자 입장에서는 값이 어떻게 만들어지는지 알 필요가 없다.

3. 요구사항 설계 : 무작정 시작한 것이 아쉬움, 앞으로는 차근차근 요구사항을 정리 -> 주석으로 상세히 정리 -> 코드짜기

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'❄️ 내일배움캠프 7기' 카테고리의 다른 글

[Trouble Shooting] 일정 관리 앱 만들기  (1) 2024.11.08
[내배캠/Spring] 1주차(용어 모음집)  (0) 2024.10.31
[Trouble Shooting] 계산기 만들기  (0) 2024.10.16
2024.10.14 본 캠프 15일차  (0) 2024.10.14
인텔리제이(IntelliJ) 폴더 안보이는 버그  (0) 2024.10.14
'❄️ 내일배움캠프 7기' 카테고리의 다른 글
  • [Trouble Shooting] 일정 관리 앱 만들기
  • [내배캠/Spring] 1주차(용어 모음집)
  • [Trouble Shooting] 계산기 만들기
  • 2024.10.14 본 캠프 15일차
Genie_.
Genie_.
내가 공부하는 지식을 복습하고 기록하는 공간입니다 💬
  • Genie_.
    Geine Dev
    Genie_.
  • 전체
    오늘
    어제
    • 전체 (153)
      • 🕹️ 코딩테스트 (37)
        • 코드트리 (13)
        • 백준 (17)
        • 프로그래머스 (7)
      • 🧡 JavaScript (9)
      • 💙 React (0)
      • 💜 HTML,CSS (38)
      • 💚 Spring (6)
      • ❤️ JAVA (13)
        • Basic (13)
      • 🌟 CS (1)
      • ❄️ 내일배움캠프 7기 (41)
      • 🏅 자격증 (6)
        • 정보처리기사 (0)
        • SQLD (3)
        • 컴퓨터활용능력2급 (3)
      • 🗨 GIT (1)
      • 🌰 스터디 (1)
  • 블로그 메뉴

    • 홈
    • 글쓰기
  • 공지사항

    • 메모
  • 반응형
  • hELLO· Designed By정상우.v4.10.0
Genie_.
[JAVA] 숫자 야구 게임 개인과제 회고
상단으로

티스토리툴바