본문 바로가기
algorithm/SW Expert Academy

[SWEA] 5650.핀볼 게임 / 시뮬레이션

by buddev 2020. 1. 27.

@시뮬레이션 / 4h 이상

처음에는 오 인덱스 조절만 잘 하면 쉽겠다 하고 풀었는데

이게 웬걸.. 테케도 안맞아서 그때부터 멘붕되고

값을 넣는 족족 stackoverflow로 터져버려서 내 멘탈도 같이 터진 문제였다..

ㅎㅎ

 

나중에는 swea의 댓글을 참고하면서 코드를 고쳐나갔는데

테케 40->42->43->46->49 순으로 맞아서 진짜 인내심 테스트 하는 줄 알았다.

마지막에 pass 떴을 때에는 되는건 좋은데 왜 되는지 스스로 이해가 안돼서 더 괴로웠다 ㅠㅠ

 

알고보니 문제를 잘못 이해했던 거였다. 주어진 대로만 풀면 적당한 문제였는데..

 

 

 

문제 링크

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AWXRF8s6ezEDFAUo

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

 

 

구현 포인트

1. 블럭에 부딪혀서 방향이 바뀌는 부분

일단 기본 방향을 상 0, 좌 1, 하 2, 우 3 으로 둔다 ((방향 + 2) % 4 == 반대방향이 되게 하기 위해)

그리고 2차원 배열을 만들었다. dir[블럭번호][지금 방향] = 바뀔 방향 값을 넣어준다

  1번블록 2번블록 3번블록 4번블록 5번블록
상0 하 2 우 3 좌 1 하 2 하 2
하1 상 0 하 2 우 3 우 3 우 3
좌2 우 3 상 0 싱 0 좌 1 상 0
우3 좌 1 좌 1 히 2 상 0 좌 1

이렇게 하면 방향을 편리하게 바꿀 수 있다.

단점) 단점은, 문제가 풀리지 않을 때 혹시 여기서 실수한 게 아닐까 하고 불안해져서 자꾸 확인하게 된다는 점..?

 

 

2. 가장자리 처리

이 부분은 다른 분의 댓글을 보고 알았는데, 꿀팁이라 추가했다.

 

가장자리에 부딪히면 반대방향으로 튕기는데, 원래같으면 따로 처리를 해 줘야 하겠지만

이 경우는 맵을 [N+2][N+2] 크기로 선언하고 가장자리 칸을 다 5로 채워버리면 된다.

예외 처리(아예 벗어날 경우)도 더이상 필요하지 않다. 벗어날 일이 없으니까.

(그래도 걱정돼서 물어봤더니 논리가 만약을 이기면 된다고 하더라..ㅠㅠ..)

 

 

3. 웜홀 처리

웜홀을 위한 3차원 배열을 사용했다. warmhole[웜홀번호][1번째,2번째웜홀][x면 0, y면 1]

그래서 만약 6번 웜홀에 들어간다면, 반대편 웜홀의 좌표는 이 배열로 바로 구할 수 있다.

 

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Solution {
	
	private static int N, maxScore, sx, sy, sd, map[][], wormhole[][][];
	private static int[] dx = {-1, 0, 1, 0}, dy = {0, -1, 0, 1};
	private static int[][] dir = {{0, 0, 0, 0}, {2, 0, 3, 1}, {3, 2, 0, 1}, {1, 3, 0, 2}, {2, 3, 1, 0}, {2, 3, 0, 1}};
	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		StringBuilder sb = new StringBuilder();
		
		int T = Integer.parseInt(br.readLine().trim());
		for (int t = 1; t <= T; t++) {
			maxScore = 0;
			wormhole = new int[11][2][2];
			
			N = Integer.parseInt(br.readLine().trim());
			map = new int[N + 2][N + 2];
			
			boolean[] wormholeVisit = new boolean[11];
			for (int i = 1; i <= N; i++) {
				st = new StringTokenizer(br.readLine().trim());
				
				for (int j = 1; j <= N; j++) {
					map[i][j] = Integer.parseInt(st.nextToken().trim());
					
					if (map[i][j] >= 6) {
						if (!wormholeVisit[map[i][j]]) {
							wormhole[map[i][j]][0][0] = i;
							wormhole[map[i][j]][0][1] = j;
							wormholeVisit[map[i][j]] = true;
						} else {
							wormhole[map[i][j]][1][0] = i;
							wormhole[map[i][j]][1][1] = j;
						}
					}
				}
			}
			
			for (int i = 0; i < N + 2; i++)
				map[0][i] = map[N + 1][i] = map[i][0] = map[i][N + 1] = 5;
			
			startPoint();
			sb.append("#" + t + " " + (maxScore) + "\n");
		}
		System.out.println(sb);
	}
	
	private static void startPoint() {
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				if (map[i][j] == 0) {
					for (int d = 0; d < 4; d++) {
						sx = i; sy = j; sd = d;
						move();
					}
				}
			}
		}
		
	}
	private static void move() {
		int x = sx, y = sy, d = sd, score = 0, nx, ny;
		boolean overlap = false;
		
		while (true) {
//			if (x < 0 || x >= N + 2 || y < 0 || y >= N + 2) return;
			//원래라면 해 줘야 하지만, 가장자리를 전부 5로 채웠기 때문에 벗어날 일이 없으므로 안 해줘도 된다.
			
			if (overlap) {	//처음 들어왔을 때를 제욓하고 종료 조건 체크 
				if ((x == sx && y == sy) || map[x][y] == -1) {
					if (maxScore < score)
						maxScore = score;
					return;
				}
			}
	
			nx = x + dx[d];
			ny = y + dy[d];
			overlap = true;

//			if (nx < 0 || nx >= N + 2 || ny < 0 || ny >= N + 2) {
//				return;
//			}
			//원래라면 해 줘야 하지만, 가장자리를 전부 5로 채웠기 때문에 벗어날 일이 없으므로 안 해줘도 된다.
			
			if ((nx == sx && ny == sy) || map[nx][ny] == -1) {	//종료 조건 
				if (maxScore < score)
					maxScore = score;
				return;
			}
			
			if (map[nx][ny] >= 6) {			//웜홀 
				if (wormhole[map[nx][ny]][0][0] == nx && wormhole[map[nx][ny]][0][1] == ny) {
					x = wormhole[map[nx][ny]][1][0];
					y = wormhole[map[nx][ny]][1][1];
				} else {
					x = wormhole[map[nx][ny]][0][0];
					y = wormhole[map[nx][ny]][0][1];
				}
				
			} else if (map[nx][ny] >= 1) {	//블럭 
				x = nx;
				y = ny;
				d = dir[map[nx][ny]][d];
				score++;
				
			} else if (map[nx][ny] == 0){
				x = nx;
				y = ny;
			}
		}
	}
}

 

ㅎㅎ...

 

삽질 포인트

1. 핀볼이 맨 윗줄에서 시작하고, 윗방향으로 출발하면 출발하자마자 벽을 만나서 제자리로 돌아오고, 방향만 반대로 바뀌게 된다.

이 때는 게임을 종료해야 한다.

이 부분을 이해하지 못해서 꽤나 오래 고생했다.

사실 간단하게 생각해 보면, 벽에 부딪혔다가 제자리로 돌아오면 게임이 끝난다고 애초에 명시되어 있는데, 왜 그렇게 오래 고민했는지 모르겠다.

 

 

2. 가장 헷갈렸던 부분은 한 턴에서 어디까지 처리를 해 줘야 하는 것인가? 이다.

그게 너무 헷갈려서 처음에는 x = 1, y = 1 칸에서 우측으로 이동해서 nx = 1, ny = 2가 되었을 때,

벽을 만나서 아래로 내려가야 한다면, 내려가는 처리까지 이 턴에서 한번에 처리해 줬다.

그러니 될 리가 없지..ㅎ

 

반복문을 돌 때는, 한번 이동하는 것에 대한 처리만 해 주면 된다.

다음 턴은, 그 턴에서 알아서 하게 두면 된다(대신, 이렇게 구현한다면 시작 부분에서 경계 체크등의 코드를 넣어야겠지).

문제에서 시키는 대로 하자!!!

 

 

 

힘들었지만 풀어서 뿌듯하다.

그렇지만 다시는 하고 싶지 않은 고생이었다..ㅎㅎ

끝!!

댓글