숫자 야구 게임

SUADI·2022년 7월 20일
0

1. 문제 설명

임의의 세자리 수를 맞춰 나가는 게임. 3자리수를 입력해서 같은수가 같은 자리에 있으면 strike, 다른 자리에 있으면 ball, 같은 수가 없으면 nothing이다.

2. 임의의 수에 대한 조건

조건 1) 세자리 수
조건 2) 1~9 숫자만 사용(0은 포함안됨)
조건 3) 중복된 수 없어야 함

3. 프로그래밍 구현 제약 사항

조건 1) 메서드 하나의 크기가 10 line을 넘지 않아야한다.
조건 2) 메서드가 한가지의 일만 하도록 최대한 작게 만들어야 한다.
조건 3) indent가 2 이하여야 한다.
조건 4) 지역 변수만을 사용한다.

내풀이

import java.util.Scanner;

public class Baseball {

    private boolean chkNumber(String str) {
        if (str.length() != 3) return false;
        if (str.contains("0")) return false;
        char a = str.charAt(0);
        char b = str.charAt(1);
        char c = str.charAt(2);
        if (a != b && a != c && b != c) return true;
        else return false;
    }

    private String randomNumber() {
        String answer;
        while (true) {
            answer = (int) (Math.random() * 900) + 100 + "";
            if (chkNumber(answer)) break;
        }
        return answer;
    }

    private String input() {
        Scanner s = new Scanner(System.in);
        String input;
        while (true) {
            System.out.print("input(3자리 수, 중복 불가, 1-9) >> ");
            input = s.nextLine();
            if (chkNumber(input)) break;
        }
        return input;
    }

    private void compare(String input, String answer) {
        int strike = 0, ball = 0;
        for (int i = 0; i < input.length(); i++) {
            char c1 = input.charAt(i);
            if (c1 == answer.charAt(i)) strike++;
            else if (answer.contains(c1 + "")) ball++;
        }
        if (strike == 3) System.out.println("3 strike!! game success!");
        else if (strike == 0 && ball == 0) System.out.println("nothing");
        else System.out.printf("%2d strikes, %2d balls\n", strike, ball);
    }

    public void game() {
        int cnt = 0;
        String input = "";
        String answer = randomNumber();
        System.out.println("game start!");
        while (!input.equals(answer)) {
            input = input();
            compare(input, answer);
            System.out.println("도전 횟수 : " + ++cnt);
        }
    }
}
  • 5개의 메서드로 야구 게임을 구성했다. 5개의 메서드 중 실질적으로 게임을 실행하는데 필요한 메서드는 game 메서드이므로 접근제어자를 public으로 하고, 나머지는 game 메서드에 필요한 부수적인 메서드이므로 private로 숨겼다. 실행하는데 필요한 메인 메서드까지 합하면 총 6개의 메서드를 사용했다.

chkNumber 메서드

  • 첫번째 메서드인 chkNumber 메서드는 input(유저의 입력값)과 answer(맞춰야할 값)의 제약 조건을 맞추기 위해 생성했다. 조건이 맞지 않으면 조건이 맞을 때까지 반복해야 하기 때문에 리턴 타입을 boolean으로 하여 조건에 맞으면 true, 아니면 false를 리턴하도록 했다. 첫번 째 조건인 세자리 수를 맞추기 위해 3자리수가 아니면 false를 리턴하도록 했다. 두번째 조건인 1~9만 사용하도록 하기 위해 0이 포함되어 있으면 false를 리턴하도록 했다. 세번째 조건은 중복된 수가 없도록 하는건데 이 조건이 가장 까다로웠다. 더 좋은 방법이 있을 것도 같지만 생각이 나질 않아 세자리 수를 일일히 변수를 선언한 후 초기화하고 각각이 모두 다르면 true를 리턴하도록 했다.

이렇게 세자리수를 각각 변수에 저장하니까 문제가 발생했다. 처음에는 boolean 변수를 선언해서 각 조건문마다 변수에 true or false를 초기화시켜줬었는데 그렇게 하게 되면 변수를 지정해 준 것 때문에 오류가 난다.

    private boolean chkNumber(String str) {
     	boolean condition;
        if (str.length() != 3) condition = false;
        if (str.contains("0")) condition = false;
        char a = str.charAt(0);
        char b = str.charAt(1);
        char c = str.charAt(2);
        if (a != b && a != c && b != c) condition = true;
        else condition = false;
        return condition;
    }
  • 이런 방식으로 코드를 짜면 나중에 input값으로 3자리수보다 작은 수를 받았을 때 문제가 된다. 세자리수보다 작은 수를 받았을 때 바로 false를 리턴해서 아래문장을 실행시키지 않아야 하는데 변수를 초기화만 해주면 아래문장을 모두 실행하게 되서 미리 3자리 문자를 변수로 저장해 놓은 a,b,c에서 StringIndexOutOfBoundsException을 일으키게 된다.

randomNumber 메서드

  • answer값을 초기화하기 위한 메서드이다. 먼저 Math.random 메서드를 사용해서 100~999까지의 수를 랜덤으로 뽑아 문자열로 저장하도록 했다. 근데 이 범위의 수 중 100, 222, 990 등의 중복이 있거나 0이 포함되는 수는 answer값이 될 수 없으므로 chkNumber 메서드를 사용해서 false를 리턴할 경우 조건에 맞을 때 까지 다시 random메서드로 난수를 받을 수 있도록 했다.

input 메서드

  • 유저가 올바른 값을 입력할 수 있도록 제한하는 메서드이다. chkNumber 메서드를 이용해서 조건에 맞지 않으면 조건에 맞을 때까지 입력을 다시 하도록 하는 메서드이다.

compare 메서드

  • compare 메서드는 input값과 answer값을 비교하여 strike,ball,nothing을 판별하는 메서드이다. 이 메서드를 만드는데 시간을 가장 많이 쓴 것 같다. 먼저 for문을 이용해서 같은 인덱스에 같은 문자가 있으면 strike를 1 더하고, 다른 인덱시에 같은 문자가 있으면 ball에 1을 더했다. 그 후에, strike가 3이면 정답을 맞춘 것이므로 성공을 출력하고, strike, ball 둘 다 0이면 nothing을 출력, 그 이외는 strike와 ball수를 출력한다.

  • 처음에는 매게변수를 input값 하나만 두고, 메서드 내에 answer값을 randomNumber메서드로 초기화한 후, 비교하였다. 하지만 이렇게 하면 큰 오류가 생긴다. compare메서드는 최종적으로 game메서드에서 사용되서 input값과 answer값이 같아질 때까지 계속 반복해서 사용될텐데 compare메서드 내에 answer값을 초기화 해버리면 반복문이 돌아갈 때마다 answer값이 달라질 것이기 때문이다. 왜 이렇게 코드를 짰었는지 지금 생각해보니까 이해가 안된다.

    private void compare(String input, String answer) {
        int strike = 0, ball = 0;
        for (int i = 0; i < input.length(); i++) {
            char c1 = input.charAt(i);
            for (int j=0; j < answer.length(); j++) {
            	char c2 = input.char(j);
            	if (c1 == c2 && i == j) strike++;
                else if (c1 == c2) ball++;
         	}
		}
        
		...
        
    }
  • 또 한가지 처음에 코드를 짤 때는 위의 코드처럼 for문을 두개 사용해서 input값과 answer값을 비교하였다. 이렇게 비교해도 되지만 굳이 이중으로 반복문을 사용하여 퍼포먼스를 떨어뜨릴 이유는 없다. for문을 한번만 사용하고 strike를 판별할 땐 index가 어차피 같으므로 따로 for문을 이용할 필요가 없고, ball을 판별할 땐 contain 메서드를 사용하면 for문이 필요없다.

game 메서드

  • 마지막으로 game메서드에는 input 값은 input 메서드로 input값의 조건에 맞는 입력을 하도록 초기화 하고, answer 값은 randomNumber 메서드를 사용해서 조건에 맞는 난수를 초기화한다. 초기화한 후 while문을 이용해서 input값이 answer값과 같을 때까지 반복하도록 코드를 짰다. 거기에 플러스 알파로 입력한 횟수를 출력하는 기능까지 넣어봤다. 만약 입력값이 조건과 맞지 않는 입력을 할 경우 입력 횟수는 올라가지 않는다.

0개의 댓글