[Java] 숫자 게임 만들기

김민범·2024년 10월 22일

Java

목록 보기
6/8

이번 과제는 숫자 야구 게임 만들기!

어렸을 때 다들 한번쯤 해봤을 게임이다.
젊은 사람들은 아닐지도...

대충 룰을 설명하자면 이렇다

  1. 임의의 숫자 3자리를 정함, 중복된 숫자0은 사용 불가 (예시 123)
  2. 상대방이 숫자를 말함 (예시 453)
  3. 같은 자리에 같은 숫자가 있을 경우 Strike!
  4. 다른 자리에 같은 숫자가 있을 경우 Ball! (예시 1S 1B)
  5. Strike, Ball 로 추론하며 정답을 맞출 때 까지 위 과정 반복

이러한 룰을 기본으로 컴퓨터에 맞게 추가적으로 요구되는 기능들을 넣어주면 되는 과제였다.

추가 기능은 다음과 같다.

  1. 각 입력의 양식에 맞게 입력되었는지 확인
  2. 난이도(자리수) 조절 가능
  3. 각 게임 시작 전 난이도 선택, 게임시작, 기록 확인, 종료하기 선택 가능

나는 다음과 같이 구현하였다.

App.java

package advanced;

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

public class App {
    public static void main(String[] args) {
        Input input = new Input();
        Confirm confirm = new Confirm();

        //  매 게임 별 count 저장 List
        List<Integer> games = new ArrayList<>();

        //  자리수 입력 여부 저장 변수
        boolean isInputLevel = false;

        //  난이도
        int level = 3;

        label:
        while (true) {
            String gameType = "";
            //  gameType 입력 (자리수 설정이 됐을 경우 패스)
            while (!isInputLevel) {
                System.out.println("환영합니다! 원하시는 번호를 입력해주세요");
                System.out.println("0. 자리수 설정 1. 게임 시작하기 2. 게임 기록 보기 3. 종료하기");
                gameType = input.stringInput();
                try {
                    confirm.isGameType(gameType);
                    break;
                } catch (InputMismatchException e) {
                    System.out.println("올바르지 않은 입력값입니다.");
                }
            }

            //  자리수 입력 시 바로 시작
            if (isInputLevel) {
                gameType = "1";
            }

            switch (gameType) {
                case "1":
                    System.out.println("< 게임을 시작합니다 >");

                    //  level 자리수에 맞는 난수 생성
                    CreateNum createNum = new CreateNum(level);
                    String number = createNum.getNumber();

                    //  게임 진행
                    Game game = new Game();
                    game.player(number, level);

                    games.add(game.getCount());

                    //  난이도 설정 초기화
                    isInputLevel = false;
                    break;
                case "2":
                    for (int i = 0; i < games.size(); i++) {
                        System.out.println((i + 1) + "번째 게임 : 시도 횟수 - " + games.get(i));
                    }
                    break;
                case "0":
                    while (true) {
                        System.out.println("설정하고자 하는 자리수를 입력하세요.");
                        String in = input.stringInput();
                        try {
                            //  input값 유효성 확인
                            confirm.isValidLevel(in);

                            level = Integer.parseInt(in);

                            //  난이도 설정 여부
                            isInputLevel = true;
                            break;
                        } catch (InputMismatchException e) {
                            System.out.println("올바르지 않은 입력값입니다.");
                        }
                    }
                    break;
                case "3":
                    System.out.println("종료합니다.");
                    break label;
            }
        }
    }
}

CreateNum.java

package advanced;

public class CreateNum {
    private String number = "";

    public CreateNum(int level) {
        //  길이를 기준으로 중복되지 않은 값만 추가
        while (this.number.length() < level) {
            String num = (int) (Math.random() * 9 + 1) + "";

            if (!this.number.contains(num)) {
                this.number += num;
            }

            // TODO: 작동 시 정답 보기 위함 -> 추후 삭제
            System.out.println(number);
        }
    }

    //  number Getter
    public String getNumber() {
        return this.number;
    }
}

Input.java

package advanced;

import java.util.Scanner;

public class Input {
    private final Scanner sc = new Scanner(System.in);

    public String stringInput() {
        String str = sc.next().trim();
        sc.nextLine();
        return str;
    }
}

Game.java

package advanced;

import java.util.InputMismatchException;

public class Game {
    //  맞출때까지 걸린 횟수 count
    private int count = 0;

    //  count Getter
    public int getCount() {
        return count;
    }

    private int[] checkAnswer(String inputAns, String answer) throws Exception {
        int[] answerArr = {0, 0};
        //  정답일 경우 throw -> catch 로 player while 종료
        if (inputAns.equals(answer)) {
            throw new Exception();
        }

        for (int i = 0; i < inputAns.length(); i++) {
            //  같은 자리에 같은 숫자 -> strike(answerArr[0])++
            if (inputAns.charAt(i) == answer.charAt(i)) {
                answerArr[0]++;
            } else if (answer.contains(Character.toString(inputAns.charAt(i)))) {
                //  다른 자리에 같은 숫자 -> ball(answerArr[1])++
                answerArr[1]++;
            }
        }
        return answerArr;
    }

    public void player(String number, int level) {
        Input input = new Input();
        Confirm confirm = new Confirm();

        while (true) {
            System.out.print("숫자를 입력하세요: ");
            String inputAnswer = input.stringInput();
            try {
                //  숫자 유효성 검사
                confirm.isValidNumber(inputAnswer, level);
            } catch (InputMismatchException e) {
                System.out.println("올바르지 않은 입력값입니다.");
                continue;
            }
            //  입력된 숫자 문제 없을 시 입력횟수 추가
            this.count += 1;

            int[] result = {0, 0};

            try {
                //  strike, ball 을 담을 result에 checkAnswer로 결과값 대입
                result = checkAnswer(inputAnswer, number);
                System.out.println(result[0] + "S " + result[1] + "B");
            } catch (Exception e) {
                //  정답인 경우 throw 를 catch
                System.out.println("정답");
                break;
            }
        }
    }
}

Confirm.java

package advanced;

import java.util.HashSet;
import java.util.InputMismatchException;
import java.util.Set;
import java.util.regex.Pattern;

public class Confirm {
    private static final String TYPE_NUMBER_REG = "^[0-3]*$";
    private static final String NUMBER_REG = "^[1-9]*$";
    private static final String LEVEL_NUMBER_REG = "^[3-5]*$";

    public void isGameType(String gameType) throws InputMismatchException {
        //  0-4 가 아닐 경우
        if (!Pattern.matches(TYPE_NUMBER_REG, gameType)) {
            throw new InputMismatchException();
        }
    }

    public void isValidNumber(String number, int level) throws InputMismatchException {
        //  자리수가 맞지 않을 경우
        if (number.length() != level) {
            throw new InputMismatchException();
        }
        //  중복된 숫자 검증을 위한 Set
        Set<Character> set = new HashSet<>();

        //  숫자로 이루어지지 않은 경우
        if (!Pattern.matches(NUMBER_REG, number)) {
            throw new InputMismatchException();
        }
        //  Set에 각 자리수 추가 (중복 숫자 자동 삭제로 size가 다름)
        for (int i = 0; i < number.length(); i++) {
            set.add(number.charAt(i));
        }
        if (set.size() != level) {
            throw new InputMismatchException();
        }
    }

    public void isValidLevel(String input) throws InputMismatchException {
        //  숫자가 아닌 값이 입력된 경우
        if (!Pattern.matches(LEVEL_NUMBER_REG, input)) {
            throw new InputMismatchException();
        }
        
        int level = Integer.parseInt(input);
        //  난이도의 범위가 맞지 않는 경우
        if (level > 5 || level < 3) {
            throw new InputMismatchException();
        }
    }
}

각 코드에 주석이 추가되어 있지만 간단하게 설명하자면

App.java

  • 기본적인 동작을 수행
  • List에 각 게임별 count 저장
  • 반복문을 이용한 게임 계속하기

CreateNum.java

  • 난이도(자리수)를 전달받아 호출되며 난수 생성
  • Getter 를 통한 난수 접근 가능

Game.java

  • count 정보 저장 및 Getter를 통한 접근 가능
  • checkAnswer 메서드를 통해 정답 여부 확인 및 Strike, Ball 리턴
  • player 를 통해 게임 플레이

Input.java

  • Scanner를 이용해 입력 받기

Confirm.java

  • isGameType : 게임 타입 유효성 검사
  • isValidNumber : 입력된 정답 유효성 검사
  • isValidLevel : 입력된 난이도 유효성 검사

계산기와 크게 난이도 차이가 없어 어렵지 않게 진행했다.
리팩토링할 부분을 찾아보도록 해야겠다.

0개의 댓글