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) 입출력 예시
2단계 회고
- 코딩과 크게 관련없는 지구, 달의 공전과 같은 개념을 오랜만에 공부해서 나름 재미있었다.
- 어떤 회사의 코테 문제로 시계를 구현하는 문제가 나왔는데 위 메서드에서 sin, cos의 부호만 바꿔주면 시계방향으로도 구현이 가능하다.
- 고등수학의 호도법이나 라디안, 삼각함수 내용을 모른다면 이 문제를 아마 못 풀었을 것이다.
- isLeapYear()이라는 메서드를 가지고 여유롭게 윤년도 따져가면서 2단계 문제를 풀었던 기억이 난다. 애니메이션 만드는 단계가 있는지 마지막 날까지 몰랐다..ㅎㅎ ㅠ
- 사용자가 입력할 때에 대한 에러 처리를 간단하게 처리했다. 모든 경우에 대해 세세하게 처리하지 못한것 같아서 아쉽다. 어떻게 해야 더욱 꼼꼼하게 처리할 수 있을지 고민해봐야겠다.
'Boot Camp > [코드스쿼드] Java 백엔드 테스트' 카테고리의 다른 글
[4단계] 태양계 애니메이션 - 미완성 (0) | 2022.12.08 |
---|---|
[3단계] 콘솔 태양계 출력 프로그램 완성 (0) | 2022.12.08 |
[1단계] 콘솔로 원 출력하기 (0) | 2022.12.08 |