미로(개인 프로젝트)
아래 코드가 실행되면 상단에 시간이 00:00:00나타난다. 그리고 그 아래에 Start버튼이 눌리면 나타날 미로를 위한 빈 공간이 배치된다. 그 빈 공간아래에 Start, Quit버튼이 나타난다.
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를 초기화해 준다.
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;
}
}
}
...
}
새로운 통로를 뚫을수 있는 곳이 나올때 까지, 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;
}
}
...
}
}
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칸의 색들을 달리해서 미로를 만든다.
미로가 생성된 후 시간을 알려주는 부분을 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>
만약 빨간색 사각형이 미로 오른쪽 하단의 출구에 도착했다면, 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>
모달창을 닫으면 미로를 지운다.
<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의 압축을 풀으면 볼 수 있다.
댓글남기기