수족관

권재현·2021년 4월 6일
0

문제풀이

목록 보기
6/8
post-thumbnail

내일 시험을 대비할겸 저번에 지하철을 분석하면서 큰 도움이 되었기에 수족관을 한번 분석 해보면서 마지막으로 이따가 자동차를 한번 더 풀어볼 계획이다. 다행히도 이번문제는 내가 풀었던 문제다 풀이는 많이 다르지만

이번 풀이도 선생님의 풀이를 심층적으로 분석해보자 얻는 것이 굉장히 많다.
문제 조건

1.기본어항 4개

2.물고기는 해당일자가 지나야 판매가능

  • 금붕어 3일,잉어 7일, 구피 5일

3.먹이를 줄 경우 해당일자가 1일씩 줄어듬

4.완전 성장 후 3일 경과 시 사망

5.먹이는 하루 1번 사용가능. 전체 어항적용

6.초기자금 300원

어항 추가 비용 10000원, 먹이 개당 200원

내가 생각한 필요한 기능 과 고려 사항

  1. 물고기 구매 : 자금변동체크, 어항 개수 체크
  2. 물고기 판매: 자금변동 체크,어항개수,물고기 성장일수에 따른 판매 유무
  3. 어항 구매: 자금 상황, 어항 개수 체크
  4. 먹이주기 : 자금상황 및 변동 체크, 성장일 수 체크,하루 1번만 가능
  5. 다음날: 성장일수 체크, 성장 후 3 일 경과 시 하늘나라로...

내가 생각한 기능과 고려 사항이다. 차차 리뷰하면서 확인해보자!!

Scanner sc = new Scanner(System.in);
	List<HashMap<String, Integer>> bowl = 
    	new ArrayList<HashMap<String, Integer>>(); //어항
        
	int bowlCnt = 4; //어항 기본 4개
	int money = 300; //보유금액 300원
	boolean food = false;
	
	String[] fishName = {"금붕어", "잉어", "구피", "상어", "고래"};
	int[] fishBuy = {100, 500, 200, 1000, 5000}; //구매금액
	int[] fishSell = {150, 1000, 350, 3000, 20000}; //판매금액
	int[] fishGrow = {3, 7, 5, 6, 10}; //성장일
	int minFishBuy;//최소금액
	
	int bowlBuy = 10000; //어항 금액
	int foodBuy = 200; //먹이 금액

선생님을 일단 어항을 리스트로 잡고 그안에 넣을 데이터형태는 HashMap으로 시작하셨다.

그리고 각종 데이터는 배열로 만들어 정리 하셨다. 아마 내 생각에는 배열 안에 인덱스번호를 활용하실 것 같다. 그리고 나와 다르게 선생님은 최소금액을 변수로 추가했다. 사실 최소금액이 어떻게 사용 될지 모르겠지만, 선생님은 다 계획이 있을 실 거다. 분명히!!

최소금액 메소드

	public AquaService() {
		//최소금액 금붕어 100원
		minFishBuy = fishBuy[0];
		
		for(int i = 1 ; i < fishBuy.length ; i++) {
//최소금액이 구매금액보다 크면 i번째 구매금액이 최소금액이 되 초기화시켜준다.
			if(minFishBuy > fishBuy[i]) {
				minFishBuy = fishBuy[i];
			}
		}
	}

현재는 금붕어가 100원으로 최소 금액이지만, 만약 금붕어 보다 구매금액이 적은 물고기가 등장할 시 최소금액이 바뀔수 있게 코드을 만드셨다. 이렇게 보면 항상 처음 작성 할때 부터 역시 재사용성이나 안전성을 위해 고려해서 짜야 하는 듯 하다. 정답만 푸는게 답이 아니다.

Run메소드

public void run() {
	boolean runFlag = true;
		
	while(runFlag) {
		System.out.println
        	("메뉴를 선택하시오. (보유금액 : " + money + ")");
		System.out.println("1.물고기구매  2.물고기판매  3.어항구매(" + bowlBuy + "원)  
        	4.상태보기  5.먹이주기(" + foodBuy + ")  6.다음날  7.종료");
			
		switch(sc.nextLine()) {
		case "1" : buyFish(); //물고기 구매
				break;
		case "2" : sellFish();//물고기 판매
				break;
		case "3" : buyBowl();//어항구매
			break;
		case "4" : status();//상태보기
				break;
		case "5" : if(money < foodBuy) {
            	//먹이보다 보유금액이 적음
			System.out.println("먹이 비용이 부족합니다.");
			 } else if(bowl.size() == 0) {
			System.out.println("물고기가 없습니다.");
			} else if(!food) {
				food = true;
				money -= foodBuy;
				grow(1);
				} else {
				System.out.println("이미 먹이를 
                    			제공하였습니다.");
					   }
				break;
		case "6" :  //다음날
				food = false;
				grow(0);
				break;
		case "7" : runFlag = false; //종료
				break;
			default : System.out.println("잘못 입력하셨습니다.");
			}
		}
	}

run 메소드는 살펴보면 특이한 요소가 2가지 있는데, 바로 케이스 5번과 6번이다.

​ 케이스 5번은 먹이주기인데, 처음 if 조건은 금액이 먹이 값 200원이 없으면 발동되고, else if문은 if문 조건이 먼저 불충족되야 실행 된다. 여기서 else if문 조건은 bowl은 물고기을 담는 어항리스트다. 거기서 . size를 사용했고 ,다음 ==0이란 뜻은 어항에 물고기 없다는 이야기다.

​ 그다음 else if문이 드디어 먹이는 주는 조건이다. 일단 food는 전역변수고, 값이 false다. 하지만 가로 안에서 !food 했기에, false가 true로 바뀐다. 그다음 금액이 먹이금액이 마이너스되 자본금이 바뀐다. 그리고 grow는 메소드가 나타는데 , 아직 여기서는 알 수 없다. 이따 밑에 내려가서 살펴보자 느낌이 분명히 중요한 요소다.

케이스 6번 다음날은 food를 false로 초기화시켜주고, 또 다시 의문의 메소드인 grow가 나타난다. 벌써 2곳에 쓰이고 있다. 중요한 요소다.

buyFish메소드 입력 값 받기 전 메소드

public void buyFish() {
	if(money < minFishBuy) {//최소금액
	System.out.println("금액이 부족합니다.");
    
	} else if(bowl.size() == bowlCnt) {//bowl.size 물고기 수
	System.out.println("어항에 공간이 없습니다.");
	} else {
	System.out.println("----- 구입할 물고기를 선택하세요. 
             (보유금액 : " + money + ") -----");
	for(int i = 0 ; i < fishName.length ; i++) {
	System.out.println((i + 1) + "." + fishName[i] 
		+ "(구매가 " + fishBuy[i] + "원, 판매가 " 
		+ fishSell[i] + "원, 성장 " + fishGrow[i] + "일)");
			}
	

buyFish 메소드 조금 길어 한번에 설명하면 이해하기 어려워 나눠서 설명한다.

​ 여기서도 첫 번째 if 조건은 보유자금이 최소금액 보다 없다면이고, else if 조건은 일단 다시 기억 할겸 else if문은 if조건이 false 로 판명나야 실행된다. 두 번째 else if 조건문은 어항에있는 물고기 수를 나타내는 bowl.size 와 어항개수를 카운트하는 bowlcnt가 똑같다는 얘기는 어항이 꽉차서 물고기를 담을 데가 없다는 이야기다.

​ 이제 드디어 if문과 else if문이 false처리가 되면, 구입할 물고기 정보와 선택 하기 좋게 보기를 나타내는 코드가 나온다. 여기서 내가 생각하기에 큰 장점이라고 생각하는데 모든 정보는 정보별로 나눠 배열로 담아 어떤 요소가 추가 된다고 해도 해당배열에 추가 하면 된다.

buyFish 메소드 입력 값 받는 부분

String input = sc.nextLine(); //입력 값
			
	int choice = Integer.parseInt(input) - 1;
		//완전 이런 편한방법이....나는 반대로 했다 ㅠㅠ
	if(choice < 0 || choice >= fishName.length) {//번호 잘못 입력시 발생
	System.out.println("잘못입력하셨습니다.");
    
	} else if(money < fishBuy[choice]) {
			System.out.println("구매금액이 부족합니다.");
            
	} else {
		HashMap<String, Integer> fish 
        	= new HashMap<String, Integer>();
		fish.put("type", choice); 
		int age = fishGrow[choice] * - 1;//성장일수 
		fish.put("age", age);
				
		bowl.add(fish);
				
		money -= fishBuy[choice];
			}
		}
	}

  입력 값을 받고 나서, 입력값을 다시 변수 choice로 초기화시킨다. 여기서 배열의 인덱스 번호 와 같게 -1 해준다. 그리고 코드를 보면 choice가 배열 가로 안에 들어가 있다.

 fish라는 맵을 사용하고 제네릭으로 String과 Integer를 사용하고 있다.     choice가 0이라고 가정하면,

 fish.put("type", 0); 이고 int age= fishGrow[0] * -1 선생님은 성장일수를 -1시켰다.

타입의 value 값이 fishname의 나열된 인덱스 번호가 동일한 물고기이다. 아마 언젠가 value 값으로 물고기를 소환 시 킬 것 같다.

이해하는 게 조금 더 어려워졌다. 그래서 fish("age",-3) 이다. 구매 금액도 자본금에서 마이너스 시킨다.

grow 메소드

드디어 grow 메소드가 나왔다. 엄청 궁금했는 데, 여기서 풀어보자 당장 now!!

// gbn 0은 다음날, 1은 먹이주기 
public void grow(int gbn) {
	for(int i = 0 ; i < bowl.size() ; i++) {//어항에 있는 물고기 숫자
		HashMap<String, Integer> fish = bowl.get(i);
            //bowl에 이미 HashMap 형태로 담겨있음.
		int age = fish.get("age");

		if(gbn == 1 && age < 0) { //먹이주기
			age++;
			fish.put("age", age);
            
		} else if(gbn == 0) {//다음날
			age++;
			fish.put("age", age); //나이 덮어쓰기
			}
			
		if(age > 3) {
		System.out.println(fishName[fish.get("type")] + 
        				"가 죽었습니다.");
				
			bowl.remove(i);
				
			i--;
			}
		}
	}

​ 위에 gbn주석이 없었으면 grow메소드가 해석이 정말 어려울 뻔 했다. for문은 어항에 있는 물고기 숫자 만큼 돌아가고, 어항에 HashMap형태로 담겨있는 fish를 꺼낸다.

​ fish.get("age") 이것은 "age"의 value 값을 꺼내는 것이고, 그 다음 int age 로 초기화 시킨다. 예를 들어서 보여주면 구매 메소드에서 입력값으로 1 을 받는 다면 -1 해서 0이다. 0은 인덱값들이 모두 금붕어를 가리킨다 .그래서 입력 값이 1이면, fish.get("age")= -3 이 나온다.

​ 이제 밑에 단계로 넘어가면 gbn == 1 은 먹기주기를 뜻하고, 논리합(두개 다 충족해야 true) 이기 때문에 age는 선생님이 -1를 곱해기 에 모두 음수다. age++하면 +1이기에 -2로 초기화된다. 그리고 다음 value 값을 덮어 씌어준다. 다음날인 gbn ==0도 같은 방식이다.

​ 마지막 if문은 조건 중 하나인 성장 한후 3일 후에는 안타깝게도 죽는 것을 짠 코드이고, 여기서 중요한 점은 물고기가 죽었는 기에 해당하는 값을 remove로 제거하고, 제거하면 리스트 차례가 하나씩 내려오게 된다. 그때 i++가되면 내려온 값을 건너뛰기에 다시 검사할 수 있게 i--; 는 필수다.

sellFish메소드

public void sellFish() {//뒤에서 부터
	for(int i = bowl.size() - 1 ; i >= 0 ; i--) {
            //뒤에서 부터 하면 아까 내가 설명한 i--; 가 필요가 없다.ㅋ
		HashMap<String, Integer> fish = bowl.get(i);
		int age = fish.get("age");
			
		if(age >= 0) { //음수로 시작해서 0은 성장일수를 다 채운 숫자다
			int type = fish.get("type");
			//type의 value 값이 choice로 들어가 있다.
			money += fishSell[type];
			//type이 인덱스 번호를 가리킨다.
System.out.println(fishName[type] + "가 " + fishSell[type] + "원에 팔렸습니다.");
				
			bowl.remove(i);
			}
		}
	}

​ 이번에는 판매 메소드다. 구매 메소드 처럼 먼저 bowl에 담긴 HashMap형태인 fish 로 초기화시켜준다. age는 아까 말했듯이 -1을 곱해 음수로 시작해 성장일수을 다채우면 0이된다. 그래서 if문 조건이 0보다 크거나 같은 것이고 다음 type를 뜯어보면, 구매메소드에서 fish.put("type", choice)로 value 값이 choice로 들어가 있다.

 value값을 int type으로 초기화 시켜주고, type은 fishsell의 인덱스 번호를 가리켜 배열의 값을 꺼내 자본금과 계산한다. 마지막에 팔렸다는 출력문은 해당 bowl(i)번째 리스트를 제거 해주고, 아까 언급했던 i--; 가없다.

 왜냐하면 처음 for문을 보면 , i 값의 시작이 bowl 리스트 뒷번호 부터 시작하고 그래서 조건식도 0보다 크거나 같을 때 까지다. 리스트를 제거 했을 때 뒤부터 제거 해 앞으로 내려올 일이 없기에 i--; 가 필요없다.

buyBowl메소드

public void buyBowl() {
		if(money < bowlBuy) {
			System.out.println("어항 구매비용이 부족합니다.");
		} else {
			money -= bowlBuy;
			bowlCnt++;
		}
	}

처음 if문은 자본금< 어항구매비용 을 말하는 것이고, else문은 비용이 가능해 돈이 어항금액 만큼 빠지고, 어항개수는 올라간다.

status 메소드

public void status() {
	System.out.println("----- 보유금액 : " + money + "원 -----");
	System.out.println("----- 보유어항 : " + bowlCnt + "개 -----");
		
	System.out.println("----- 어항상태 -----");
		
	for(HashMap<String, Integer> fish : bowl) {
		int type = fish.get("type");
		int age = fish.get("age");
			
		if(age < 0) {
		System.out.println(fishName[type] + "(성장완료까지 " + 
        				(age * - 1) + "일 남음)");
		
        	} else {//나이가 0보다크면
		System.out.println(fishName[type] + "(성장완료 " + 
        						age + "일)");
			}
		}
	}

상태 메소드에 주목할 점은 바로 for 문의 두 번째 활용법이다. 일단 bowl에 있는 데이터들을 fish로 초기화 시킨다. 그 다음 각각 type과 age로 초기화시켜주고 그다음 그 값들을 바탕으로 출력문에 쓰이고있다.

총평

오늘 하나하나 뜯어서 살펴보니 선생님은 bowl 데이터을 fish로 초기화 시킬때 각각 다른 방법을 보여주셨다. 와 처음에 설명을 들을 때 전혀 몰랐는데, 오늘에서 야 발견하다니 이따 car 문제 풀때 한번 적용시킬 수 있게 노력해봐야겠다. 절대 포기는 없다.

profile
호텔리어 출신 비전공자

0개의 댓글