Java언어를 활용하여 컴퓨터가 생성한 3자리 숫자를 사용자가 맞추는 게임을 만드는 과제를 수행했다.
과제 수행 목적
- 모듈화 : 각 클래스가 특정 책임을 담당하므로 코드 유지보수가 용이하다.
- 재사용성 : 클래스와 메서드가 독립적으로 설계되어 재사용이 가능합니다
- 확장성 : 새로운 기능이나 요구 사항이 추가되더라도 기존 코드를 수정하지 않고 새로운 클래스를 추가하거나
기존 클래스를 수정할 수 있다.
Lv.1 기본 게임 구현 🎮
- 정답 숫자 생성하기
- 정답은 서로 다른 3자리 수이다.
- 각 자리는 1~9 사이의 숫자이다. 0은 사용할 수 없다.
- 동일한 숫자는 사용될 수 없다. 즉, 숫자는 중복되지 않아야 한다.
ex) 333, 112, 119, 102 불가능
- 정답을 맞추기 위해 숫자를 입력하기
- 서로 다른 3자리 수를 입력할 수 있다.
- 동일한 숫자는 사용될 수 없다. 즉, 숫자는 중복되지 않아야 한다.
- 숫자만 입력 가능하며, 문자는 작성할 수 없다.
- 결과값 출력 및 게임 로직 적용하기
- 정답과 입력값을 비교해 힌트를 “볼, 스트라이크, 아웃”으로 표시한다.
- 스트라이크 : 입력값과 정답을 비교해 같은 자리에 같은 숫자가 있는 경우
- 볼 : 숫자는 같지만, 자리는 다른 경우
- 아웃 : 숫자도, 자리도 다른 경우
- 입력한 3자리 숫자가 정답과 같은 경우, 게임이 종료된다.
- '3 스트라이크'라면, 정답에 해당
- 정답일 때, "축하합니다! 정답입니다!" 출력
- 올바르지 않은 입력값에 대해서는 오류 문구를 보여준다.
- 입력값이 문자, 중복되는 값처럼 요구사항과 맞지 않을 경우
- 게임 이어서하기
- 정답을 맞출 때까지 계속해서 시도할 수 있어야 하며, 정답을 맞추면 축하 메시지를 출력
- 정답과 입력값을 비교해 힌트를 “볼, 스트라이크, 아웃”으로 표시한다.
🎯 트러블 슈팅 1
1. 배경
컴퓨터가 생성한 랜덤한 3자리 수의 값을 담을 자료구조가 필요했다.
자료구조의 종류는 다음과 같다.
- 순서가 있는 목록인 List형
- 순서가 중요하지 않은 목록인 Set형
- 먼저 들어온 것이 먼저 나가는 Queue형
- KEY-VALUE의 형태로 저장되는 Map형
숫자 야구 게임의 규칙 중에 숫자 중복을 허용하지 않는 부분이 있어서
쉽게 Set을 선택해서 저장할 값이 중복되지 않도록 했다.
Set 클래스에는 HashSet, TreeSet, LinkedHashSet이 있는데 HashSet이 가장 성능이 좋아서 HashSet으로 선택했다.
2. 발단
문제는 HashSet이 저장순서를 유지하지 않는다는 점이다.
List는 순서가 있는 목록이라 List로 바꿔야할 지.. 고민이 되기 시작했다.
그러면 중복된 값은 어떻게 처리할 것인지 문제가 많아보였다.
Set 클래스에는 HashSet말고도 다른 Set들이 있는데
그중에 LinkedHashSet이 저장순서를 유지하게 해준다는 사실을 알게 되었다.
3. 전개
LinkedHashSet이 잘 적용이 되었는지 확인을 해보려면 프로그램을 실행해준다.
기대하는 값은 생성된 3자리 수로 출력된 142의 값을 동일하게 142로 입력했을 때,
스트라이크 3이 출력되면서 "정답입니다!" 를 출력하는 것이었다.
놀랍게도 동일한 값을 입력했지만
스트라이크 : 1 볼 : 2 가 출력되었다.
이유를 모르겠다.
4. 위기
어디서부터 잘못된 것인지 모르겠다.
오류를 찾기 위해서
컴퓨터가 출력하는 값이 중복을 허용하지 않고
순서를 보장하면서 출력되는 지 확인하고 싶었다.
랜덤하게 생성된 3자리의 수가 정상적으로 출력되는 것을 확인했다.
5. 절정
문제가 되는 지점을 발견했다. LinkedHashSet이 모든 코드에 적용되지 않아서 순서를 보장해주지 않았고
정답을 판별할 수가 없었던 것이다.
6. 결말
코드 작성에 오타가 나오는 것처럼 아주 단순하고 쉬운 실수로 인해 오류가 발생했다.
오류를 발견하지 못했다면, 플레이어가 영원히 정답을 맞추기 위해 무한으로 숫자를 입력해야했을 것이다.
(정답을 입력했음에도 불구하고.)
이제 프로그램을 실행해서 오류가 잘 수정되었는지 확인해준다.
Lv.2 입력 및 출력 개선 ⚾️
- 입력값이 유효한지 검사하기
- 3자리 수인지 자릿수를 검사
- 중복된 숫자가 없는지 중복 숫자를 검사
- 입력값에 숫자만 포함되어 있는지 검사
- 유효하지 않은 값인 경우 오류 메시지를 출력
- 출력 개선 - 프로그램 시작 시 안내 문구 보여주기
- 1을 입력하면 “필수 구현 기능” 의 예시처럼 게임이 진행
- “2. 게임 기록 보기”는 Lv3에서 제시 -> "기능 구현 예정" 출력
- 3을 입력하면 게임이 종료
🎯 트러블 슈팅 2
1. 배경
숫자 야구 게임을 할 때 플레이어가 값을 입력할 때 예상하지 못하는 값을 입력하는 경우가 있다.
예를 들면, 3자리 수를 입력해야하는데 2자리 수나 4자리 수를 입력하여 올바르지 않은 자리 수의 값을 입력하는 경우.
또는, 중복된 수를 입력했을 경우.
(예외 처리를 위한 예외 클래스를 구현하지는 못하였다. try-catch)
예외에 해당하는 값을 입력받았을 때 오류 메시지를 출력하고,
continue로 다시 처음의 while 문으로 돌아가서 입력 값을 새로 받을 수 있도록 했다.
예외 처리 1. 다른 자리 수 입력 시
예외 처리 2. 중복된 숫자 입력 시
2. 발단
플레이어가 입력한 값은 String 문자열로 받았기 때문에,
중복된 숫자를 판별할 때 어려움이 있었다.
이를 해결하기 위해서 문자열로 받은 값을 문자 char 로 쪼개서 숫자 int 로 변환하기로 했다.
먼저, userInput.toCharArray() 메소드를 사용해 userInput 문자열을 문자 배열(char[])로 변환해준다. (char digit에 문자로 저장)
이때 getNumbericValue() 메소드를 사용했는데, 이는 Unicode의 값을 int value로 반환해준다.
만약 문자가 숫자 값을 갖지 않는다면 -1을 반환하고, 분수처럼 음이 아닌 정수로 표현할 수 없는 경우에는 -2를 반환한다.
getNumericValue()
Character.getNumericValue (숫자로 변환할 Character)
/**
*@param: Character
**/
Character.getNumericValue()
String digits = "132";
for (int i = 0; i < digits.length(); i++) {
int d1 = Character.getNumericValue(digits.charAt(i));
int d2 = digits.charAt(i);
System.out.println("d1 " + d1);
System.out.println("d2 " + d2);
}
// d1 1
// d2 49
// d1 3
// d2 51
// d1 2
// d2 50
char c = '3';
System.out.println((int) c); // 51
System.out.println(c - '0'); // 3
System.out.println(Character.getNumericValue(c)); // 3
3. 전개
그런데 문제가 생겼다.
플레이어가 실수로 숫자와 문자를 동시에 입력을 했는데,
getNumbericValue() 로 인해 잘못 입력된 문자를 유니코드 기반 숫자로 바꿔서 출력이 된 것이다.
그래서 잘못 입력된 문자가 어떤 값으로 변환되는지 확인하기 위해 출력했다.
4. 위기
문자를 숫자로 변환하는 일 때문에 문자를 잘못 입력해도 숫자로 자동 변환되어 strike인지 ball인지 출력을 해버리는 문제가 생겼다.
isDigit() 함수는 명시된 char 값이 숫자 인지 여부를 판단하여 true 또는 false 값으로 반환한다.
플레이어가 다시 숫자와 문자를 입력했다. 3d5
5. 절정
결국 이 오류를 해결하기 위해서는 무엇을 해야할까?
문자열을 문자로 분리한 후 그 문자를 userNumberSet에 넣기 전에
구별해서 다시 처음의 while문으로 돌아가 입력값을 다시 받는 것이다.
Character.isDigit(digit) 에 숫자가 아닌 값이 들어오면 "올바른 숫자를 입력하세요." 가 출력된 후 if 문이 종료된다.
실행을 해서 다시 확인해보니,
위 if 문으로 바로 넘어가는 것을 확인할 수 있었다.
아예 변수를 선언해서 해결하기로 하였다.
for문에서 문자를 숫자로 변환하기 전에 if문을 추가하기로 했다.
다시 해석해보면, char digit에 들어왔던 값이 숫자가 아닐 때, "올바른 숫자를 입력하세요." 가 출력된다.
그리고 boolean 변수를 선언해줬던 isDigit에 false값을 기록하면서 if 문이 종료된다.
이 과정을 해준 이유는...
isDigit이 false라면 continue를 사용해 이후의 로직을 건너뛴다.
다시 while문이 실행되면서 플레이어에게 값을 입력하라고 할 것이다.
6. 결말
아래에는 수정이 완료된 코드이다.
복잡한 과정이었지만, 차근차근 생각해보면 당연하게 필요한 코드들이 자리를 잡았다.
실행을 해보면 잘못된 문자 값이 들어왔을 때, if문의 조건이 참이 되고
오류 메시지를 잘 출력하는 것을 확인할 수 있었다.
그리고 다시 플레이어에게 입력 값을 새로 받는다.
Lv.3 추가 기능 및 개선 🎳
- 게임 기록 통계
- 지금 시도하는 게임이 몇 번째 게임인지 기록
- 사용자가 정답을 맞힐 때까지의 “시도 횟수”를 기록하고 게임이 끝났을 때, 총 시도 횟수를 출력
- 2를 입력하면 게임의 시도 횟수를 출력
- 출력 개선 - 실행 및 정답을 맞힌 경우, 표시되는 안내 문구 선택지 개선
- 3을 입력하면 게임이 종료
- 이전의 게임 기록들도 초기화
- 1, 2, 3 이외의 입력값에 대해서는 오류 메시지 노출
🎯 트러블 슈팅 3
1. 배경
Main 클래스를 보면, switch문으로 case 1, case 2, case 3으로
각각 1. 게임 시작하기 2. 게임 기록 보기 3. 게임 종료하기 를 선택할 수 있다.
2. 발단
2번을 선택하면 case "2" 에서 "게임 기록 보기 기능 보여줄 예정." 라는 문장이 출력된다
3. 전개
자료구조에 저장되자나. 어떤 자료구조를 써야하지? Map
4. 위기
잘 되는 것 같은데 뭐가 이상하다. 아... 1을 선택하면 바로 게임 결과가 출력되네.
5. 절정
case 1의 기록을 case 2로 옮겨야해. 그리고 게임을 한번도 안했을 때는? 그때는 아직 기록된 게임이 없다고 출력해.
6. 결말
사실 Lv. 4도 구현할 수 있는데.
switch문 수정하고,
level 변수로 자리수 하드코딩 부분에 모두 넣어서 값을 변경할 수 있게.
그러면 유동적으로 자리수 조절이 가능하지.
오류 해결을 위해 디버깅을 활용하기
객체 지향 고려하기.
클래스 묶어보기…
TIL 10월 24일
'TIL (ToDay I LearNEd) > K P T (keeP, pRoBlem, Try) & 트러블슈팅' 카테고리의 다른 글
뉴스피드 프로젝트_트러블 슈팅.TIL (0) | 2024.11.21 |
---|---|
순환 참조란? Circular Dependency (일정 관리 앱 서버 DEVELOP 트러블 슈팅) (1) | 2024.11.15 |
일정 관리 앱 서버 만들기_트러블 슈팅.TIL (1) | 2024.11.08 |
계산기 필수 기능 구현_트러블 슈팅.TIL (0) | 2024.10.16 |
[KPT] 첫 미니 프로젝트 (0) | 2024.10.07 |