<우아한 테크코스 5기 프리코스 진행사항 - 2주차>
2주차 과제는 하나의 주제를 가지고 기능별로 구현을 하는 알고리즘 문제였다.
1주차와 마찬가지로 다양한 기능 요구 사항, 제한사항이 주어지면 그것에 맞게 알고리즘을 구현해내면 되는 것이었다.
<백엔드 - Java> 기준으로 과제를 수행하였다.
<숫자 야구>
- 기능 요구 사항
- 기본적으로 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다.
- 제한 사항
- 구현과정
구현할때 요구사항이 1주차보다 많아지고 다양해져서 새롭게 느껴졌다. 프리코스의 라이브러리 사용 또한 생소했다.
일반적으로 자바에서 입력을 받을때 Scanner 클래스를 사용하거나 BufferedReader 클래스로 입력을 받아오는데,
프리코스에서 입력에 사용하는 라이브러리는 'camp.nextstep.edu.missionutils' 이며 Random 을 사용시에도 이 라이브러리를 import해서 사용한다.
또한 IllegalArgumentException 을 통해 사용자가 잘못된 입력을 했을 경우 예외를 발생시킨다.
들여쓰기 제한도 있어서 클래스를 분리해서 구현해야겠다고 생각하였다.
<Application.java>
package baseball;
import java.util.List;
public class Application {
public static void main(String[] args) throws IllegalArgumentException{
// TODO: 프로그램 구현
System.out.println("숫자 야구 게임을 시작합니다.");
ComputerNum computer = new ComputerNum();
Init_game init = new Init_game();
Compare compare = new Compare();
Retry retry = new Retry();
boolean again = true;
while(again) {
List<Integer> com_num = computer.ComputerNumber();
String result = "";
while(!result.equals("3스트라이크")) {
result = compare.Classification(com_num, init.player_num());
System.out.println(result);
}
again = retry.retry();
}
}
}
- Application.java는 기본적으로 프로그램 전체 진행을 관리한다.
again을 boolean형으로 선언하여 재시작(retry)을 처리하고, '3스트라이크'일 경우에는 게임을 클리어한 것이므로,
결과를 표시하도록 한다.
게임의 첫시작과 게임의 재시작은 각각 Init_game클래스와 Retry클래스에서 관리한다.
클래스를 분리한만큼 Application 클래스에서 패키지 내 다른 클래스를 참조해서 사용하도록하였다.
>> Ex) ComputerNum computer = new ComputerNum();
<ComputerNum.java>
package baseball;
import camp.nextstep.edu.missionutils.Randoms;
import java.util.ArrayList;
import java.util.List;
public class ComputerNum {
public List<Integer> ComputerNumber(){
List<Integer> com_num = new ArrayList<>();
while (com_num.size() < 3) {
int randomNumber = Randoms.pickNumberInRange(1, 9);
if (!com_num.contains(randomNumber)) {
com_num.add(randomNumber);
}
}
return com_num;
}
}
- ComputerNum은 프리코스의 라이브러리를 Import해서 Random을 이용하여 1부터 9까지의 숫자를 뽑고
>> int randomNumber = Randoms.pickNumberInRange(1, 9);
그 길이는 3으로 제한하도록 하였다.
>> while (com_num.size() < 3)
뽑은 숫자 중에 새로뽑은 랜덤숫자가 포함되지 않는한 정수형 리스트(com_num)에 추가한다.
>>
if (!com_num.contains(randomNumber)){
com_num.add(randomNumber);
}
com_num 리스트가 완성되면 이 값을 반환하는 클래스이다.
<Init_game.java>
package baseball;
import camp.nextstep.edu.missionutils.Console;
import java.util.ArrayList;
import java.util.List;
public class Init_game {
public List<Integer> player_num() {
System.out.println("숫자를 입력해주세요 : ");
List<Integer> player_num = new ArrayList<>();
String input = Console.readLine();
for(String number : input.split("")) {
player_num.add(Integer.parseInt(number));
}
exception_Length(input);
return player_num;
}
// IllegalArgumentException
public void exception_Length(String s){
if(s.length() != 3) {
throw new IllegalArgumentException("세자리 수보다 크거나 작음");
}
}
}
- ComputerNum.java에서는 랜덤 정답 숫자를 만들어냈다면,
Init_game에서는 이 랜덤 정답 숫자를 맞추기위해 사용자로부터 입력을 받게된다.
입력을 받을때 Scanner 클래스를 선언하고 readLine()으로 입력을 받지 않고
프리코스의 라이브러리를 사용하여서 Console.readLine()으로 입력을 받게된다.
>> String input = Console.readLine();
사용자에게 입력받는 숫자는 3자리로 이루어져있어서 각 자리수마다 검증을 해야한다.
>> 예외처리는 exception_Length(String s) 메서드에서 관리
각 자리의 숫자는 100의 자리수, 10의 자리수, 1의 자리수 총 3개로 이루어지는데,
이는 player_num이라는 정수형 리스트에서 관리하도록 구현하였다.
>> player_num.add(Integer.parseInt(number));
각 숫자를 구분하기 위해 split("") 메서드를 사용하였고 인자로는 "" 를 지정하여 공백없이 문자별로 구분하도록하였다.
사용자가 3자리숫자를 입력하지 않는 등의 정해진 형식을 벗어났는지 검증하기 위해서는
exception_Length(String s) 메서드에 입력받은 input 문자열을 넣어 기준에 부합하는지 검사한다.
기준에 부합하지않으면 요구사항이었던 IllegalArgumentException을 발생시킨다.
>> throw new IllegalArgumentException("예외발생");
<Compare.java>
package baseball;
import java.util.List;
public class Compare {
// Compare Computer num and Player num, Count hit processing
public int Hit(List<Integer> computer, List<Integer> player) {
int result = 0;
for(int i = 0; i < player.size(); i++) {
if(computer.contains(player.get(i))) result++;
}
return result;
}
// Only Count hit strike
public int countStrike(List<Integer> computer, List<Integer> player) {
int strike = 0;
for(int i = 0; i < player.size(); i++) {
// Correct num and index = strike
if(computer.get(i) == player.get(i)) strike++;
}
return strike;
}
public String Classification(List<Integer> computer, List<Integer> player) {
int total = Hit(computer, player);
int strike = countStrike(computer, player);
int ball = total - strike;
if(total == 0) return "낫싱";
else if(strike == 0) return ball + "볼";
else if(ball == 0) return strike + "스트라이크";
return ball + "볼 " + strike + "스트라이크";
}
}
- Compare.java 에서는 랜덤 숫자를 사용자가 맞췄을때 발생하는 Hit() 와
그 Hit에서 Strike의 개수를 파악하는 countStrike()
낫싱, 볼, 스트라이크 등으로 결과를 분류하는 Classification() 메서드로 구분을 한다.
Hit() 메서드에서는 사용자가 입력한 숫자 리스트인 List<Integer> player 에서 get(i)를 통해 숫자를 가져온 후
contains메서드를 통해 컴퓨터에서 설정한 랜덤 정답 숫자에 포함되는지 검사한다.
Hit가 발생하면 result를 증가시킨다.
countStrike에서는 컴퓨터의 i번째 인덱스와 사용자의 i번째 인덱스를 get으로 가져와서 검사한 후
strike를 증가시킨다.
<Retry.java>
package baseball;
import camp.nextstep.edu.missionutils.Console;
public class Retry {
public boolean retry() {
System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료");
System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.");
char answer = Console.readLine().charAt(0);
if(answer == '1') return true;
return false;
}
}
- 사용자가 입력하는 1, 2 중에서 가장 처음 문자를 가져와 '1'일 경우 true를 반환한다.
이 true는 다시 Application에서 while(again) 부분에서 처리된다.
이번에 2주차를 진행하면서 하나의 클래스에서만 프로그램을 구현하는 것이 아닌 클래스를 분리하여
객체별로 구현해나가는 과정에서 배운점이 많았다. 클래스 별로 관리하다보니
각 클래스마다 단위테스트(Unit Test)도 진행할 수 있었다.
<Application_Test.java>
package baseball;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
public class Application_Test {
Compare compare;
@BeforeEach
void Init(){
compare = new Compare();
}
@Test
@DisplayName("3볼 테스트")
void check_ball(){
assertThat("3볼").isEqualTo(compare.Classification(Arrays.asList(3,1,2), Arrays.asList(1,2,3)));
}
@Test
@DisplayName("3스트라이크 테스트")
void check_strike(){
assertThat("3스트라이크").isEqualTo(compare.Classification(Arrays.asList(1,2,3), Arrays.asList(1,2,3)));
}
@Test
@DisplayName("낫싱 테스트")
void check_nothing(){
assertThat("낫싱").isEqualTo(compare.Classification(Arrays.asList(1,2,3), Arrays.asList(4,5,6)));
}
}
새롭게 공부한 것
1. assertThat
>> assertThat(T actual, Matcher<? super T> matcher)의 형태로 메서드를 사용하여 두 값을 비교할 수 있다.
여기서는 isEqualTo 메서드를 이용하여 단위테스트를 진행할 수 있었다.다른 클래스들 또한 Compare_Test.java / ComputerNum_Test.java 등 처럼 단위테스트를 진행하여전체프로그램을 Run 하지 않고도 각 기능별로 문제가 생긴 부분이 있는지 알 수 있었다.단위테스트를 진행하게되면 각 기능별로 문제가 발생할때마다 그 지점을 알 수 있어서 코드 리팩토링을적절히 잘 해낼 수 있다는 장점이 있다.
<Readme.md>
## 숫자야구게임 구현하기
### 기능구현단계
* Computer : 1~9 범위의 '서로 다른' 임의의 수 3개 생성
* Computer <-> Player 의 3자리 숫자 비교
#### Tester : <숫자야구게임 룰>
1. Player가 제시한 숫자 중 Computer의 숫자와 같은 자리에 있으면 "스트라이크"
2. Player가 제시한 숫자 중 Computer의 숫자가 포함되어있지만 다른 자리에 있으면 "볼"
3. Player가 제시한 숫자 중 Computer의 숫자와 전혀 일치하지 않으면 "낫싱"
#### Class
1. ComputerNum : 랜덤한 컴퓨터 숫자를 만들어주는 클래스 (서로 다른 3자리 수)
2. Init_game : 플레이어로부터 숫자를 입력받는 클래스 (서로 다른 3자리 수)
3. Compare : 위치 관계없이 일치한 숫자를 판별하고 '스트라이크' 개수를 통해 볼과 스트라이크 분류
4. Retry : 3스트라이크로 게임이 종료되었을때 '재시작 : 1, 게임 종료 : 2' 구현
5. Application : 클래스들의 실행을 관리하고, main함수와 함께 게임을 실행
<Commit Log>
- ComputerNum.java 추가
[Fix] [Add] <ComputerNum> Generate Random Computer Number
Commit 2
- Init_game.java 추가
[Fix] [Add] <Init_game> Input Player Number
Commit 3
- Compare.java 추가
[Fix] [Add] <Compare> Count hit processing, count strike, classification
Commit 4
- Retry.java 추가
[Fix] [Add] <Retry> After all clear, retry or end the game
Commit 5
- Application.java 수정
[Fix] [Feat] <Application> Management game
Commit 6
- ComputerNum_Test.java 추가
[Test] <ComputerNum_Test> Unit Test
Commit 7
- Compare_Test.java 추가
[Test] <Compare_Test> Unit Test
Commit 8
- Application_Test.java 추가
[Test] <Application_Test> Unit Test
<과제 수행 후기>
숫자 야구 게임을 구현하면서
1. 컴퓨터 랜덤 숫자 생성
2. 플레이어 숫자 입력
3. 비교, 판별
4. 재시작 및 게임 시작 구현 등
여러 클래스로 각 기능을 분류하여 구현해보니 코드도 가벼워지고 이에 따라 코드 관리가 쉬워져 유지보수에 용이할 것 같았습니다. 각 기능 별로 Commit을 하는 과정 또한 기능을 차근차근 구현해나갔다는 성취감이 들어 느낌이 색달랐습니다.
이번 주차에서 새롭게 배운점으로는 단위 테스트 기능입니다.
클래스가 많아져 Application의 main함수로 한번에 테스트가 불가능할때는 각 클래스별 단위 테스트 클래스를 만들어서 @Test와 assertThat 기능을 활용하여 단위테스트가 가능하다는 점을 충분히 배울 수 있었습니다.
제출 결과로 모든 테스트를 통과할 수 있었다. 1주차 과제수행에 비해 어려웠던 점이 사실이었지만
하나의 프로그램을 다루다보니 목표의식을 가지고 계속 나아갈 수 있었던 것 같다.
'Recording > 우아한테크코스 5기 Pre-course' 카테고리의 다른 글
[마무리🎆] [우테코 5기] <4주차> 'java-bridge' 회고 (0) | 2023.03.11 |
---|---|
[우테코 5기] <3주차> 'java-lotto' 회고 (0) | 2022.12.14 |
[우테코 5기] <1주차> 'java-onboarding' 회고 (2) | 2022.11.29 |
[우테코 5기] <0주차> OT 후기 (0) | 2022.11.06 |