ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [내배캠, Lv3] 단어 맞추기 게임
    알고리즘 문제/Java 2024. 6. 19. 19:14

    문제

    문제1 : 단어를 주어진 기회 안에 맞추는 게임을 만들어보기

    • 컴퓨터가 랜덤으로 영어단어를 선택합니다 .
      • 영어단어의 자리수를 알려줍니다 ( 7자리 = "_ _ _ _ _ _ _"
    • 사용자는 A 부터 Z 까지의 알파벳 중에서 하나를 입력합니다.
      • 입력값이 A-Z 사이의 알파벳이 아니라면 다시 입력을 받습니다.
      • 입력값이 한 글자가 아니라면 다시 입력을 받습니다.
      • 이미 입력했던 알파벳이라면 다시 입력을 받습니다.
      • 입력값이 정답에 포함된 알파벳일 경우 해당 알파벳이 들어간 자리를 전부 보여주고, 다시 입력을 받습니다.
        • 정답이 eyes 인 경우에 E 를 입력했을 때 ( "E _ E _ "
      • 입력값이 정답에 포함되지 않은 알파벳일 경우 기회가 하나 차감되고, 다시 입력을 받습니다.
      • 사용자가 9번 틀리면 게임오버됩니다.
      • 게임오버 되기 전에 영어단어의 모든 자리를 알아내면 플레이어의 승리입니다.

    문제 접근 

    계획

    • List를 활용한 문제 관리
    • 문제에 대해서는 문제의 알파벳 길이 만큼 '_' 삽입
    • 정답을 맞출 수 있는 남은 기회가 0이거나 공란이 0이 될 때 까지 반복
    • 플레이어가 알파벳을 제대로 입력했을 경우, 문자열을 앞에서부터 순차적으로 탐색
      • 유저가 대/소문자 구분 없이 입력할 것을 감안하여, 대문자로 치환
      • 정답인 경우 해당 위치를 _ 에서 알파벳으로 변경
      • 정답이 아닌 경우 기회를 1 차감

    필요 내용 

    해당 계획대로 구현하기 위해 나는 다음과 같은 내용을 학습해야할 필요가 있었다.

    1. 특정 문자가 반복적으로 나오는 문자열 생성
    2. char형 변수가 알파벳인지 판단 및 대문자로 치환
    3. String의 특정 인덱스 값 변경

     

    구현 중 발생한 문제

    1. 한글을 입력할 경우 예외처리가 되지 않고 기회가 차감되는 문제

    문제 1 : isAlphabetic() 함수를 통하여 영어인 경우만 처리를 하게 하였지만, 한글을 입력해도 처리가 되는 현상

    isAlphabetic()함수의 정의를 열어 보니, 영어/영어 이외의 문자로 구분 짓는 것이 아닌,  유니코드 표준에 의해 정의된 다른 나라 문자 또한 구분하고 있었다. 

     

    isAlphabetic 함수 내용

    public static boolean isAlphabetic(int codePoint) {
            return (((((1 << Character.UPPERCASE_LETTER) |
                (1 << Character.LOWERCASE_LETTER) |
                (1 << Character.TITLECASE_LETTER) |
                (1 << Character.MODIFIER_LETTER) |
                (1 << Character.OTHER_LETTER) |
                (1 << Character.LETTER_NUMBER)) >> getType(codePoint)) & 1) != 0) ||
                CharacterData.of(codePoint).isOtherAlphabetic(codePoint);
        }

    해결 1 : 해당 문자가 영어 알파벳이 맞는지 확인하는 isAlpha함수를 별도로 구현

    해당 문자가 'a' ~ 'z' 사이거나 'A' ~ 'Z'사이일 경우 True를 반환하는 함수를 별도로 구현하여,

    입력받은 문자 길이가 1인 경우, 판단하게 하였다.

     

    isAlpha 함수

    public static boolean isAlpha(char c)
    {
        return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ;
    }

     

    2. Call by value / Call by reference

    문제1. 함수내에 들어온 값을 변경하여도 실제 값은 변하지 않았던 문제

    입력받은 문자가 정답에 있는지 확인하는 함수 match함수를 구현하였다.

    해당 함수는 다음과 같은 동작을 했다.

    1. 해당 문자가 존재하는 위치 확인, 저장
    2. 문제에서 해당 위치에 존재하는 문자 '_'를 알파벳으로 변환
    3. 변환한 수 만큼, 공란의 수를 저장하는 변수 blankCount 감소

     하지만, 해당 함수에서 값을 변경하여도 실제 main함수의 원본 값에는 아무런 변화가 일어나지 않았고,

    정답을 맞춰도 문제는 여전히 '_ _ _ _ _ _'로 표시되었다. 

    해결1. 함수의 인자를 new를 통해 생성된 객체로 변환

    C++에서 &가 있듯, C#에서 ref 키워드가 있듯이 Java 또한 Call By Reference를 사용할 수 있는 키워드가 존재할 줄 알았으나, Java에는 Call By Reference가 존재하지 않는다고 한다. 그와 비슷하게 동작을 할 뿐, 실제로는 Call By Value라고 한다.

     

    그래서 match함수에서 값이 변경될 인자에 대해서는 int는 Integer[]로 String은 StringBuilder처럼 각 변수를 new 키워드를 사용하는 자료형으로 바꿔, 이 문제를 해결하였다.

     

    match 함수

    public static void match(StringBuilder word, String original, Character input, Integer[] chance, Integer[] blankCount)
    {
        int matchCount = 0;
        ArrayList<Integer> matchIndices = new ArrayList<>();
    
        for(int i = 0; i < word.length(); ++i) {
        	// 입력받은 알파벳이 정답에 존재할 경우
            if(original.charAt(i) == input) {
    	    // 이미 체크된 문자열일 경우
    	    if(original.charAt[i] == word.charAt[i])  return;
    			
    	    ++matchCount;
    	    matchIndices.add(i);
            }
        }
    
        for (Integer i : matchIndices) {
            word.setCharAt(i, input);
        }
    
        blankCount[0] -= matchIndices.size();
    
        if(matchCount == 0 ) {
            --chance[0];
        }
    }

     

    코드

    전체 코드

    java.app

    더보기
    import java.util.ArrayList;
    import java.util.Random;
    import java.util.Scanner;
    
    public class App {
    
        public static void match(StringBuilder word, String original, Character input, Integer[] chance, Integer[] blankCount)
        {
            int matchCount = 0;
            ArrayList<Integer> matchIndices = new ArrayList<>();
    
            for(int i = 0; i < word.length(); ++i)
            {
                if(original.charAt(i) == input)
                {
                    ++matchCount;
                    matchIndices.add(i);
                }
            }
    
            for (Integer i : matchIndices) {
                word.setCharAt(i, input);
            }
    
            blankCount[0] -= matchIndices.size();
    
            if(matchCount == 0 )
            {
                --chance[0];
            }
        }
        
        public static boolean isAlpha(char c)
        {
            return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ;
        }
    
        public static void main(String[] args) throws Exception {
            String[] words = {"PICTURE", "LIKE", "CREATIVE", "PROGRAMMER" };
            
            Random rand = new Random();
            
            int index = rand.nextInt(words.length);
            Integer[] chance = new Integer[]{ 9 };
            
            Integer[] blankCount = new Integer[]{ words[index].length() };
    
            StringBuilder word = new StringBuilder(new String("_").repeat(blankCount[0]));
            Scanner sc = new Scanner(System.in);
    
            while(chance[0] > 0 && blankCount[0] > 0)
            {
                System.out.println("현재 상태 : " + word.toString());
                System.out.println("남은 기회 : " + chance[0]);
                System.out.print("정답 : ");
    
                String answer = sc.nextLine();
                
                if(answer.length() == 1 && isAlpha(answer.charAt(0)))
                {
                    match(word, words[index], Character.toUpperCase(answer.charAt(0)), chance, blankCount);
                }
                else
                {
                    System.out.println("알파벳 한 글자만 입력해주세요 ! \n");
                }
                
            }
    
            if(blankCount[0] == 0)
            {
                System.out.println("정답 : " + words[index]);
            }
            else
            {
                System.out.println("실패.. 정답은 " + words[index]);
            }
    
            sc.close();
        }
    }

     

     

    실행화면

    그리고 코드를 실행한 결과 다음과 같이 동작하였다.

    [ 실행 화면 ]

     

Designed by Tistory.