Boot Camp/[코드스쿼드] Java 백엔드 테스트

[2단계] 지구 태양 달의 위치 표시하기

계란💕 2022. 12. 8. 19:34

  Ex) 날짜(m월 d일)를 입력 받아서 지구, 태양, 달의 위치를 표시하기

  • 1월 1일은 태양, 지구, 달이 일직선 상에 위치한다고 가정한다.
  • 문제에 주어지지 않았으나 지구와 달이 모두 반시계 반향으로 공전한다고 가정했다.
  • 그래프 크기 50 * 50, 태양의 지름: 5, 지구: 3, 달 1로 설정한다.

 

 

  Sol)

<hide/>

public class Revolution {

    static final double mercuryOrbitalPeriod = 88;

    public static LocalDate enterDate() throws IOException {
        System.out.println("날짜를 입력하세요. ex) m월 d일");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        String m = st.nextToken();
        String d = st.nextToken();
        int month;
        int day;
        try {
            month = Integer.parseInt(m.substring(0, m.length() - 1));
            day = Integer.parseInt(d.substring(0, d.length() - 1));
        } catch (NumberFormatException e) {
            throw new RuntimeException("[ERROR] 날짜는 m월 d일 형태로만 입력 가능합니다.");
        }
        if (month < 1 || month > 12 || day < 1 || day > 31) {
            throw new RuntimeException("[ERROR] 날짜는 m월 d일 형태로만 입력 가능합니다.");
        }
        return LocalDate.of(0, month, day);
    }

    public static double dateToAngle(LocalDate date) {
        double dayOfYear = date.getDayOfYear();
        double totalDaysOfYear = 365;
        GregorianCalendar gregorianCalendar = new GregorianCalendar();
        boolean leapYear = gregorianCalendar.isLeapYear(date.getYear());
        if (leapYear) {
            totalDaysOfYear = 366;
        }
        return (dayOfYear - 1) / totalDaysOfYear * 360;
    }

    public static double mercuryDateToAngle(LocalDate date) {

        double dayOfYear = date.getDayOfYear();
        return dayOfYear / mercuryOrbitalPeriod * 360;
    }

    public static int[] angleToCoordinate(int[] centerOfSun, double disFromSunToEarth,
        double angle) {
        int[] centerOfEarth = new int[2];
        centerOfEarth[0] = (int) (centerOfSun[0] - disFromSunToEarth * Math.sin(Math.toRadians(angle)));
        centerOfEarth[1] = (int) (centerOfSun[1] + disFromSunToEarth * Math.cos(Math.toRadians(angle)));
        return centerOfEarth;
    }

    public static void main(String[] args) throws IOException {
        LocalDate date = enterDate();
        double angleBetweenSunAndEarth = dateToAngle(date);
        double angleBetweenEarthToMoon = angleBetweenSunAndEarth * 12; // 지구 기준으로 달의 각도
        int[] centerOfSun = {25, 25};
        int diameterOfSun = 5;
        int diameterOfEarth = 3;
        int diameterOfMoon = 1;
        double disFromSunToEarth = 20;
        double disFromEarthToMoon = 4;
        Circle.fillGraphWithCircle(centerOfSun, diameterOfSun);
        int[] centerOfEarth = angleToCoordinate(centerOfSun, disFromSunToEarth, angleBetweenSunAndEarth);   // 태양 기준으로 지구의 각도
        Circle.fillGraphWithCircle(centerOfEarth, diameterOfEarth);
        int[] centerOfMoon = angleToCoordinate(centerOfEarth, disFromEarthToMoon, angleBetweenEarthToMoon);
        Circle.fillGraphWithCircle(centerOfMoon, diameterOfMoon);
        Circle.printCircle(Circle.graph);
    }
}

 

  • 전체적인 흐름: 날짜 입력 => 각도 구하기 => 행, 열 위치 구하기 => 그래프에 그려 넣는다.
  • 1월 1일을 기준으로 하기 때문에 일년 중에서 해당 날짜가 몇 번째 날인지를 구한다.
    • (day - 1) / 365 * 360 = angle
    • toRadians(angle)의 결과를 가지고 아래의  삼각함수 식에 적용해야한다.
  • 행렬의  x , y 좌표 => row, col 으로 변환하는 것이 헷갈렸다. 
    • 거리 d,  점(a, b)을 기준으로 θ 만큼 회전시키면) ==> 점( a + d * cosθ, b + d *sinθ)  으로 이동
    • 거리 d,  태양의 위치 [a, b] 를 기준으로  θ 만큼 회전시키면) ==>  [a - d * sinθ, b + d * cosθ ] 로 이동
  • toRadian() 메서드를 써야하는지 몰라서 헤멨다.
  • 처음에 구현 완료했을 때는 12월 31일에 일직선이 되고 1월 1일은 약간 회전된 상태였다.
    • 그래서 날짜를 각도로 변환하는 함수의 반환값의 분자에서 dayOfYear에서 1을 빼니까 해결됐다.

 

 

  Note) 입출력 예시

1월1일
9월 20일을 입력하면 지구는 270도보다 적게 돈 상태이고 달은 240도 정도 회전한 상태이다.

 

 

2단계 회고

  • 코딩과 크게 관련없는 지구, 달의 공전과 같은 개념을 오랜만에 공부해서 나름 재미있었다.
    • 어떤 회사의 코테 문제로 시계를 구현하는 문제가 나왔는데 위 메서드에서 sin, cos의 부호만 바꿔주면 시계방향으로도 구현이 가능하다.  
  • 고등수학의 호도법이나 라디안, 삼각함수 내용을 모른다면 이 문제를 아마 못 풀었을 것이다.
  • isLeapYear()이라는 메서드를 가지고 여유롭게 윤년도 따져가면서 2단계 문제를 풀었던 기억이 난다. 애니메이션 만드는 단계가 있는지 마지막 날까지 몰랐다..ㅎㅎ ㅠ
  • 사용자가 입력할 때에 대한 에러 처리를 간단하게 처리했다. 모든 경우에 대해 세세하게 처리하지 못한것 같아서 아쉽다. 어떻게 해야 더욱 꼼꼼하게 처리할 수 있을지 고민해봐야겠다.