6 분 소요

아래 코드가 실행되면 상단에 시간이 00:00:00나타난다. 그리고 그 아래에 Start버튼이 눌리면 나타날 미로를 위한 빈 공간이 배치된다. 그 빈 공간아래에 Start, Quit버튼이 나타난다.

beg

1

Start버튼을 누르면 body안 id가 “start”인 input태그에서 start 함수가 실행된다.

<script type="text/javascript">
	...
	function start() 
	{			
		wall_builder();
		...
	}
</script>

Start함수에 미로의 벽을 만들어주는 wall_builder()가 실행된다.

<script type="text/javascript">
	...
	function wall_builder()
	{		
		/*
		jsp에서 자바로 코딩한 아래의 내용들은 페이지 로드시 처음에 해석되고
		이후에는 찾아볼수 없다.(f12를 눌러 개발자 도구창에서 maze.jsp를 보면
		이클립스에서와 다르게 자바코드는 보여지지 않는다). 
		
		즉 wall_builder()를 다시 실행하더라도, 새로 만들어진 미로의 모양에 맞게 table의 색상 변경이 불가능하다. 
		그런데. function end()에서 location.replace(location.href);라고 코딩하면, 'Quit'버튼을 눌렀을시
		페이지가 다시 로드된다. 그 결과 jsp페이지에 있던 자바코드를 새로이 해석하게 되고, wall_builder()실행시 
		새로운 모양의미로가 만들어진다.
		*/
		<%
		int[][] maze;
		int maze_size;		
		maze = new maze_builder().wall_const();
		...
		%>	
	}
</script>	

maze_builder()클래스의 객채를 생성하고, 생성자 함수를 이용해 아래의 그림과 같이 maze를 초기화해 준다.

2

package games;
...
public class maze_builder 
{
	//불필요한 메모리할당을 방지하기 위해 static을 붙여서
	//변수를 선언한다.
	
	static int row;
	static int col;	
	
	static int n_row;
	static int n_col;	
	
	static int[] coordi;
	
	static int removal;
	static int loc;	
	
	static int next_start;
	static int maze_size=51;

	static Random random = new Random();	
	static int[][] moving = new int[4][2];		  						
	static int[][] destruction = new int[4][2];	
	
	static Stack<Integer> locs = new Stack<Integer>();
	static Stack<ArrayList<int[]>> directions = new Stack<ArrayList<int[]>>();
	
	static ArrayList<Integer> check = new ArrayList<Integer>();

	static int[][] maze = new int[maze_size][maze_size];
	
	//static int leng = (maze.length-2)/2;
	//static int total = (leng+1)*(leng+1);
	
	static int leng = maze.length/2;
	static int total = leng*leng;
	
	public maze_builder()
	{
		for(int i=0; i<maze_size; i++)
		{
			Arrays.fill(maze[i], 1);
			if(i%2 == 1)
			{
				for(int j=1; j<maze_size; j+=2)
				{
					maze[i][j] -=1;
				}				
			}		
		}
        
        moving[0] = new int[]{0,-2};
        moving[1] = new int[]{0,2};
        moving[2] = new int[]{-2,0};
        moving[3] = new int[]{2,0};
        
        destruction[0] = new int[]{0,-1};
        destruction[1] = new int[]{0,1};
        destruction[2] = new int[]{-1,0};
        destruction[3] = new int[]{1,0};
	}
    ...
}

maze_builder()클래스의 객채의 wall_const함수를 실행한다.(깊이우선 탐색 알고리즘 이용)

package games;
...
public class maze_builder 
{
	...
	public static int[][] wall_const()
	{		
		//(0 1 2 3 4)*2+1 => (1 3 5 7 9)	
		//row = 1+(2*random.nextInt(leng));
		//col = 1+(2*random.nextInt(leng));	
		row = 1;
		col = 1;			
		locs.add(row*maze_size + col);

		boolean esc = true;
		
		//깊이우선 탐색 알고리즘 사용
		while(esc)
		{
			forwarding(row,col);
			...
		}		
	}	
	...
}

forwarding함수에는 더 이상 통로를 뚫을수 없을때(=아래 코드에서 while문 탈출) 까지 계속 실행된다.

package games;
...
public class maze_builder 
{
    ...
	public static void forwarding(int row, int col)
	{			
		//4면이 막혀서 더 이상 이동이 불가능해질 때 까지 이동한다.
		while(true)
		{
			ArrayList<int[]> dir = new ArrayList<int[]>();	
			ArrayList<Integer> idx = new ArrayList<Integer>();
			
			//현위치를 방문기록에 추가
			loc = row*maze_size + col;
			if(!check.contains(loc))
			{
				check.add(loc);					
			}

			for(int i=0; i<moving.length; i++)
			{
				//이동후 maze의 범위안에 있나 확인해 본다.
				if(0<row + moving[i][0] && row + moving[i][0] < maze_size
				   && 0<col +moving[i][1] && col + moving[i][1] <maze_size)
				{
					loc = maze_size*(row + moving[i][0]) + col +moving[i][1];
					if(!check.contains(loc))
					{		
						idx.add(i);
						dir.add(new int[]{row+ moving[i][0],col+moving[i][1]});	
					}
				}
			}
			
			//dir의 크기가 2이상인 경우
			if(dir.size() > 1)
			{
				//random.nextInt(N) => 0~N-1에 해당하는 정수 생성
				
				//다음 이동할 위치 선정
				removal = random.nextInt(dir.size());
	
				//다음 이동을 위해 막혀있던 장벽 부수기
				maze[row+destruction[idx.get(removal)][0]][col+destruction[idx.get(removal)][1]] = 0;						
				
				locs.add(row*maze_size + col);
				
				//row, col값을 다음 이동할 위치로 바꿔준다.
				row = dir.get(removal)[0];
				col = dir.get(removal)[1];				
						
				
				//이동할 위치에 해당하는 곳의 좌표를 dir에서 삭제한다.
				dir.remove(removal);
						
				//dir를 스택directions에 추가해준다.
				directions.push(dir);									
			}
			
			//dir의 크기가 1인경우
			else if(dir.size() == 1)
			{
				//다음 이동을 위해 막혀있던 장벽 부수기
				maze[row+destruction[idx.get(0)][0]][col+destruction[idx.get(0)][1]] = 0;								
				
				//row, col값을 다음 이동할 위치로 바꿔준다.
				row = dir.get(0)[0];
				col = dir.get(0)[1];					
			}
			
			//dir의 크기가 0인경우
			else
			{
				break;		
			}
		}
	}
	...
}

3

새로운 통로를 뚫을수 있는 곳이 나올때 까지, forwarding함수에서 뚫었던 통로를 거슬로 올라간다.(bacwarding함수의 기능).

package games;
...
public class maze_builder 
{
	public static int[][] wall_const()
	{		
		...
		//깊이우선 탐색 알고리즘 사용
		while(esc)
		{
			...
			if(check.size() < total)
			{
                if(directions.size() > 0)
                {
                    coordi=backwarding();					
                    row = coordi[0];
                    col = coordi[1];								
                }
                else
                {
                    esc=false;	
                }			
			...
		}	
		...	
	}
    ...
}
package games;
...
public class maze_builder 
{
	public static int[] backwarding()
	{	
		int [] temp_r_w = {row ,col};
		ArrayList<int[]> temp_dirs = directions.pop();	
		loc = locs.pop();	
		
		row = loc/maze_size;
		col = loc%maze_size;		
		
		for(int i=0; i<temp_dirs.size(); i++)
		{
			n_row=temp_dirs.get(i)[0];
			n_col=temp_dirs.get(i)[1];	
			
			//temp_dirs에 담겨있는 값에 해당하는 곳이 
			//방문했던 곳이라면 제거해 준다.
			if(check.contains(n_row*maze_size+n_col))
			{
				temp_dirs.remove(i);
			}
		}
		
		//다른곳으로 이동이 가능한 경우
		if(temp_dirs.size() > 0)
		{
			next_start=random.nextInt(temp_dirs.size());
			n_row=temp_dirs.get(next_start)[0];
			n_col=temp_dirs.get(next_start)[1];			

			//뒤로 돌아와 다른 방향으로 이동하려는 장소가 처음가는 위치인 경우.
			if(!check.contains(n_row*maze_size+n_col))
			{
				//backwarding시 파괴할 벽넘어에 있는 칸이 check에 포함되었는지 확인한다.
				//새로이 갈라질 통로를 만들기 위한 벽부수기
				//WEST,EAST
				if(row == n_row)
				{
					//WEST
					if(n_col < col)
					{
						maze[row+destruction[0][0]][col+destruction[0][1]] = 0;					
					}			
					//EAST
					else
					{
						maze[row+destruction[1][0]][col+destruction[1][1]] = 0;						
					}
				}			
				//NORTH,SOUTH
				else 
				{
					//NORTH
					if(row > n_row)
					{
						maze[row+destruction[2][0]][col+destruction[2][1]] = 0;	
					}			
					//SOUTH
					else
					{
						maze[row+destruction[3][0]][col+destruction[3][1]] = 0;	
					}			
				}
				return new int[]{n_row,n_col};				
			}				
		}
		
		//다른곳으로 이동이 불가능한 경우
		else
		{
			//재귀호출 실행(한번더 bakcwarding한다)
			temp_r_w = backwarding();
			return temp_r_w;
		}			

		return new int[]{temp_r_w[0], temp_r_w[1]};	
	}
	...
}

모든 장소를 방문할 때 까지 while문에서 조건에 따라 forwarding, backwarding함수를 반복해준다.

package games;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.Stack;

public class maze_builder 
{
	...
	public static int[][] wall_const()
	{		
		...
		boolean esc = true;
		
		//깊이우선 탐색 알고리즘 사용
		while(esc)
		{
			forwarding(row,col);
			if(check.size() < total)
			{
                if(directions.size() > 0)
                {
                    coordi=backwarding();					
                    row = coordi[0];
                    col = coordi[1];								
                }
                else
                {
                    esc=false;	
                }			
			}
			else
			{
				esc=false;
			}
		}	
        ...
	}
}

4

wall_const()에서 while문 탈출을 했다는 것은 미로를 완성했다는 것이다. 그 때는 입구와 출구를 만들어 주고, 해당 데이터(maze배열)를 반환해 준다.

public class maze_builder 
{
	...
	public static int[][] wall_const()
	{		
		...
		if(!esc)
		{
			maze[1][0] = 0;						//=> 입구
			maze[maze_size-2][maze_size-1] = 0;	//=> 출구
			esc = true;
			
			//반드시 ArrayList<Integer> check안에 있는 값을 없애줘야 한다.
			//안그러면 기록이 남아서, maze.jsp에서 시작버튼을 누르더라도, 미로가 
			//원하는대로 생성되지 않는다.(static변수의 특성)
			check.clear();		
		}
		return maze;		
	}
	...
}	

maze_builder클래스의 wall_const()함수 실행결과 리턴된 2차원 배열의 원소값에 따라 table칸의 색들을 달리해서 미로를 만든다.

err

5

미로가 생성된 후 시간을 알려주는 부분을 setInterval함수를 이용해 만들어준다.

<script type="text/javascript">
	...
	var time = 0;
	var maze_size=51;
	var oper_author = false;
	var timer;
	var hour = 0;
	var sec = 0;
	var min = 0;
	...
	function start() 
	{			
		...
		$("#start").attr("disabled", true);
		$("#end").attr("disabled", false);
	    timer = setInterval
	    (
	    function()
		{
	       time++;
	       min = Math.floor(time/60);
	       hour = Math.floor(min/60);
	       sec = time%60;
	       min = min%60;
	
	       var th = hour;
	       var tm = min;
	       var ts = sec;
	       if(th<10){
	       th = "0" + hour;
	       }
	       if(tm < 10){
	       tm = "0" + min;
	       }
	       if(ts < 10){
	       ts = "0" + sec;
	       }
	       $('#time').html(th + ":" + tm + ":" + ts);
	     }, 1000);		
	    oper_author = true;
	}
	...
<script>	

키보드 화살표를 이용해 빨간색 사각형 이동이 가능하게끔 코딩한다.

<script type="text/javascript">
	...	
	//방향키 감지를 위한 코딩
	$(document).keydown(function(event) 
	{
		/*Key Code Reference Table참조
		...
			37	Arrow Left
			38	Arrow Up
			39	Arrow Right
			40	Arrow Down	
		...
		*/
		if(oper_author)
		{
		    if (event.keyCode == 37 || event.which == 37 ) 
		    {
		    	//alert("Arrow Left가 입력되었습니다.")
	        	relocation(-1);
		    }		
		    else if (event.keyCode == 39 || event.which == 39 ) 
		    {
		        //alert("Arrow Right가 입력되었습니다.")       	
	        	relocation(1);
		    }
		    
		    else if (event.keyCode == 38 || event.which == 38 ) 
		    {
		        //alert("Arrow Up가 입력되었습니다.")      	
	        	relocation(-maze_size);
		    }
		    else if (event.keyCode == 40 || event.which == 40 ) 
		    {
		        //alert("Arrow Down가  입력되었습니다.")      	
	        	relocation(maze_size);
		    }	    
		}
	});
	...
<script>		
<script type="text/javascript">
	...
	//맨처음 시작위치 세팅
	var cardinal_points = maze_size;
	function relocation(mov)
	{
		var next_loc = cardinal_points+mov;
		
		//이동하려는 위치에 벽의 존재여부를 판단한다.
		if($('#'+next_loc).attr('bgcolor') != 'black')
		{	
			$('#'+cardinal_points).attr('bgcolor', 'white');	
			$('#'+next_loc).attr('bgcolor', 'red');
			cardinal_points = next_loc;
		}
        ...
	}
	...
<script>

6

만약 빨간색 사각형이 미로 오른쪽 하단의 출구에 도착했다면, ajax로 비동기 통신을 해서 오라클에 경과시간을 기록해준다.(숫저퍼즐 페이지에서 비동기 통신 참고)

<script type="text/javascript">
	...
	function relocation(mov)
	{
		...
		if($('#'+next_loc).attr('bgcolor') != 'black')
		{	
			...	
			if(cardinal_points==(maze_size*(maze_size-1)-1))
			{
				$.ajax
				({
					type: 'POST',
					url: './Time_record',
					data: 
					{
						id : '<%=id%>',
						gname : 'maze',
						time : time,
						mod : 0
					}		
				});	
				...
			}
		}
	}
	...
<script>	

경과시간을 모달창으로 보여준다.

<script type="text/javascript">
	...
	function relocation(mov)
	{
		...
		if($('#'+next_loc).attr('bgcolor') != 'black')
		{	
			...	
			if(cardinal_points==(maze_size*(maze_size-1)-1))
			{
				...
		 	 	var time_r = $('#time').html()
		 	 	
			 	setTimeout(function() 
			 	{	 		
		 			//alert("정답입니다.");
		 			//alert("기록 : " + time);
					$('#messageType').html('골인지점에 도착했습니다.');
					$('#messageContent').html("기록 : " + time_r);
					$('#messageCheck').attr('class', 'modal-content panel-success'); 
					//팝업 창을 띄운다.
					$('#messageModal').modal('show');	
					
					//아래와 같이 코딩해서 모달창 닫히는 이벤트를 감지한다.
					$('#messageModal').on('hidden.bs.modal', function (e) 
					{
						end();	 	 		
					})	 				
			 	}, 100);				
			}
		}
	}
	...
<script>	

7

모달창을 닫으면 미로를 지운다.

<script type="text/javascript">
	...
	function end() 
	{
		//미로가 없는 깨끗한 상태로 만든다.
		//location.replace()를 이용해 현재 페이지로 이동하면,
		//히스토리에 저장되지 않아 이전페이지 이동이 불가능하게 된다.
		//(아래와 같이 하지 않으면 시작 버튼 누르고, 미로가 만들어 질때
		//계속 같은 모양만 나오게 된다.)
		//location.href => 현재 페이지의 주소
		location.replace(location.href);
	}
	...
<script>	

동작영상

※전체 코드는 https://github.com/qlqlzh/Storage에서 my_project.zip과 my_project.z01를 다운받은 후 my_project_mybatis.zip의 압축을 풀으면 볼 수 있다.

댓글남기기