이전 강의까지는
1. 랜덤한 셀에서 갈 수 있을 때까지 미로를 만들며 나아간다.
2. 미로가 아니면서 1에서 만든 미로와 맞닿은 셀을 찾는다.
까지 만들었습니다.
아래의 코드는 이전 강의까지의 코드입니다.
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) '시간지연 코드
Sub MakeMaze()
Randomize
worksheets("Sheet2").Select
Dim map_size As Integer
Dim maze() As Boolean '동적배열 선언
Dim row, col As Integer
Dim found As Boolean
map_size = worksheets("Sheet1").Cells(8,2).value
ReDim maze(map_size+2, map_size+2) '배열 크기 선언
'배열값 초기화
For i=1 To map_size+2
For j=1 To map_size+2
If i = 1 Or j = 1 Or i = map_size+2 Or j = map_size+2 Then
maze(i,j) = True
Else
maze(i,j) = False
End If
Next j
Next i
'셀을 정사각형으로 만들기
Range("A1:XFD1048576").ColumnWidth = 1.00
Range("A1:XFD1048576").RowHeight = 10.00
'조이스틱을 위한 셀 만들기
Range(Cells(1,1), Cells(3,3)).ColumnWidth = 0.1
Range(Cells(1,1), Cells(3,3)).RowHeight = 1.0
'필요없는 셀은 숨기기
Rows(map_size+6 & ":" & Rows.Count).EntireRow.Hidden = True
Range(Cells(4,map_size+6), Cells(map_size+5,Columns.Count)).EntireColumn.Hidden = True
'미로 테두리 그리기
Range(Cells(3+1, 3+1), Cells(3+map_size+2, 3+map_size+2)).Interior.ColorIndex = 3
Range(Cells(3+2, 3+2), Cells(3+map_size+1, 3+map_size+1)).Interior.ColorIndex = 0
Range(Cells(3+1, 3+1), Cells(3+map_size+2, 3+map_size+2)).Borders.Weight = xlThick
row = Int(Rnd * map_size) + 2
col = Int(Rnd * map_size) + 2
maze(row,col) = True
Cells(3+row, 3+col).Interior.ColorIndex = 6
'갈 수 있을 때까지 미로를 만들며 랜덤으로 이동하기
Do Until maze(row-1, col) And maze(row+1, col) And maze(row, col-1) And maze(row, col+1)
move = Int(Rnd * 4) + 1
Select Case move
Case 1 '위쪽으로 이동할 때
If maze(row-1, col) = False Then
Cells(3+row - 1, 3+col).Borders(xlEdgeBottom).LineStyle = xlNone
row = row - 1
End If
Case 2 '오른족으로 이동할 때
If maze(row, col+1) = False Then
Cells(3+row, 3+col + 1).Borders(xlEdgeLeft).LineStyle = xlNone
col = col + 1
End If
Case 3 '왼쪽으로 이동할 때
If maze(row, col-1) = False Then
cells(3+row, 3+col - 1).Borders(xlEdgeRight).LineStyle = xlNone
col = col - 1
End If
Case 4 '아래쪽으로 이동할 때
If maze(row+1, col) = False Then
Cells(3+row + 1, 3+col).Borders(xlEdgeTop).LineStyle = xlNone
row = row + 1
End If
End Select
maze(row,col) = True
Cells(3+row, 3+col).interior.ColorIndex = 6
Sleep(50) '시간지연 코드
Loop
'미로와 맞닿은 셀 찾기
found = False
For i=2 To map_size+1
For j=2 To map_size+1
If maze(i,j) = False Then
If maze(i+1,j) = True And Cells(3+i+1, 3+j).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
found = True
ElseIf maze(i-1,j) = True And Cells(3+i-1, 3+j).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
found = True
ElseIf maze(i,j+1) = True And Cells(3+i, 3+j+1).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
found = True
ElseIf maze(i,j-1) = True And Cells(3+i, 3+j-1).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
found = True
End If
End If
If found Then
Exit For
End If
Next j
If found Then
Exit For
End If
Next i
End Sub
이젠 미로와 맞닿은 셀부터 다시 갈 수 있을 때까지 랜덤으로 이동해야 합니다.
그러기 위해서는 위의 코드 중 (갈 수 있을 때까지 랜덤으로 이동)을 다시 재사용 해야합니다.
결국 논리구조는
갈 수 있을 때까지 랜덤으로 미로를 만들며 이동 -> 미로가 아니면서 미로와 맞닿은 셀 찾고 미로와 연결하기 -> 찾은 셀 부터 다시 랜덤으로 미로를 만들며 이동 -> 미로가 아니면서 미로와 맞닿은 셀 찾고 미로와 연결하기 -> .....
으로 반복이 되며 maze() 배열의 모든 값이 True가 될 때까지 실행해야 합니다.
먼저 찾은 셀과 미로를 연결해 봅시다.
이를 위해서는 찾은 셀의 위쪽에 미로가 있는지, 아래쪽에 미로가 있는지 등의 경우를 구분해서 테두리를 없애주어야 합니다.
예를들어 만약 maze(i,j)의 위쪽 maze(i-1,j)가 미로라면(True라면) Cells(3+i,3+j)의 위쪽 테두리를 없애야 합니다.
이는 Cells(3+i,3+j).Borders(xlEdgeTop).LineStyle = xlNone 를 통해 위쪽 테두리를 없앨 수 있습니다.
다른 경우에서도 테두리를 없애는 코드를 추가해 줍시다.
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) '시간지연 코드
Sub MakeMaze()
''' 생략 '''
found = False
For i=2 To map_size+1
For j=2 To map_size+1
If maze(i,j) = False Then
If maze(i+1,j) = True And Cells(3+i+1, 3+j).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
Cells(3+i, 3+j).Borders(xlEdgeBottom).LineStyle = xlNone
found = True
ElseIf maze(i-1,j) = True And Cells(3+i-1, 3+j).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
Cells(3+i, 3+j).Borders(xlEdgeTop).LineStyle = xlNone
found = True
ElseIf maze(i,j+1) = True And Cells(3+i, 3+j+1).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
Cells(3+i, 3+j).Borders(xlEdgeRight).LineStyle = xlNone
found = True
ElseIf maze(i,j-1) = True And Cells(3+i, 3+j-1).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
Cells(3+i, 3+j).Borders(xlEdgeLeft).LineStyle = xlNone
found = True
End If
End If
If found Then
Exit For
End If
Next j
If found Then
Exit For
End If
Next i
End Sub
위의 사진처럼 I5의 셀이 미로와 연결되는 모습을 볼 수 있습니다.
다음에 해야할 것은 찾은 셀부터 다시 미로를 생성해나가는 것입니다.
하지만 생각해보면
랜덤한 셀부터 더이상 갈 곳이 없을 때까지 미로를 만드는 것과,
찾은 셀부터 더이상 갈 곳이 없을 때까지 미로를 만드는 것은
시작하는 셀이 랜덤한 셀이냐, 특정 셀이냐는 것만 다르지 그 이후의 코드는 같습니다.
따라서 반복문을 이용하여 우리가 이미 작성한 코드를 재사용 해 봅시다.
우선 반복문이 끝나는 조건은 maze()의 배열이 모두 True 값일 때 입니다. 이를 확인하기 위한 함수 하나를 작성해 줍니다.
Function completeMakeMaze(maze() As Variant, map_size)
Dim isComplete As boolean
isComplete = True
For i=1 To map_size+2
For j=1 To map_size+2
If maze(i,j) = False Then
isComplete = False
End If
Next j
Next i
completeMakeMaze = isComplete
End Function
completeMakeMaze라는 함수를 만들었습니다.
미로 배열과 미로 크기를 인자로 받아 확인하여 배열값이 하나라도 False라면 False값을 반환합니다.
그럼 반복문을 추가하고 조건에 위의 함수를 이용해 봅시다.
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) '시간지연 코드
Sub MakeMaze()
''' 생략 '''
'반복문 추가
Do Until completeMakeMaze(maze, map_size)
'나아가면서 미로를 만드는 반복문
Do Until maze(row-1, col) And maze(row+1, col) And maze(row, col-1) And maze(row, col+1)
''' 생략 '''
Loop
'미로와 맞닿은 셀을 찾는 반복문
found = False
For i=2 To map_size+1
''' 생략 '''
Next i
Loop '추가된 반복문의 끝
End Sub
Function completeMakeMaze(maze() As Variant, map_size)
Dim isComplete As boolean
isComplete = True
For i=1 To map_size+2
For j=1 To map_size+2
If maze(i,j) = False Then
isComplete = False
End If
Next j
Next i
completeMakeMaze = isComplete
End Function
Do Until completeMakeMaze(maze, map_size) 반복문이 두개의 반복문을 감쌌습니다.
하나는 더이상 갈 수 없을 때까지 미로를 만드는 반복문
다른 하나는 미로와 맞닿은 셀을 찾는 반복문
결국 위에서 말한 언급한 대로
미로를 만들고 -> 미로와 맞닿은 셀을 찾고 -> 찾은 셀부터 미로를 만들고 -> 다시 미로와 맞닿은 셀을 찾고 -> 다시 찾은 셀부터 미로를 만들고 -> .....
가 반복이 되면서 결국 모든 셀을 미로로 만들 수 있게 됩니다.
여기서 하나 빠트린 것이 있습니다. 분명 위의 코드대로 실행을 시키면 무한 루프에서 빠져나오지 못하게 됩니다.
이유는 우리는 그저 반복문만 추가했을 뿐, 찾은 셀부터 다시 미로를 만들도록 하지는 않았습니다.
이를 해결하기 위해서 코드 초반에 사용한 두개의 변수, row 와 col 을 이용할 것입니다.
우리는 row와 col에 랜덤한 수를 집어넣어 미로를 시작했습니다. 하지만 한번 미로를 만들고, 맞닿은 셀을 찾게 되면 row와 col의 값은 랜덤한 수가 아닌 맞닿은 셀의 주소를 가져야 합니다.
코드로 구현해 봅시다.
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) '시간지연 코드
Sub MakeMaze()
''' 생략 '''
'반복문 추가
Do Until completeMakeMaze(maze, map_size)
Do Until maze(row-1, col) And maze(row+1, col) And maze(row, col-1) And maze(row, col+1)
''' 생략 '''
Loop
'미로와 맞닿은 셀을 찾는 반복문
found = False
For i=2 To map_size+1
For j=2 To map_size+1
If maze(i,j) = False Then
If maze(i+1,j) = True And Cells(3+i+1, 3+j).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
Cells(3+i, 3+j).Borders(xlEdgeBottom).LineStyle = xlNone
row = i+1 'row값 수정
col = j 'col갑 수정
found = True
ElseIf maze(i-1,j) = True And Cells(3+i-1, 3+j).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
Cells(3+i, 3+j).Borders(xlEdgeTop).LineStyle = xlNone
row = i-1 'row값 수정
col = j 'col갑 수정
found = True
ElseIf maze(i,j+1) = True And Cells(3+i, 3+j+1).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
Cells(3+i, 3+j).Borders(xlEdgeRight).LineStyle = xlNone
row = i 'row값 수정
col = j+1 'col갑 수정
found = True
ElseIf maze(i,j-1) = True And Cells(3+i, 3+j-1).Interior.ColorIndex <> 3 Then
Cells(3+i, 3+j).Interior.ColorIndex = 6
Cells(3+i, 3+j).Borders(xlEdgeLeft).LineStyle = xlNone
row = i 'row값 수정
col = j-1 'col갑 수정
found = True
End If
End If
If found Then
Exit For
End If
Next j
If found Then
Exit For
End If
Next i
Loop '추가된 반복문의 끝
End Sub
Function completeMakeMaze(maze() As Variant, map_size)
Dim isComplete As boolean
isComplete = True
For i=1 To map_size+2
For j=1 To map_size+2
If maze(i,j) = False Then
isComplete = False
End If
Next j
Next i
completeMakeMaze = isComplete
End Function
맞닿은 셀을 찾을 때마다 row와 col의 값을 수정해 줍니다.
이렇게 되면 맨 처음 MakeMaze 를 실행할 때는 row와 col값이 랜덤의 값을 가지지만 그 이후부터는 특정 셀의 값을 가진 채 반복문을 실행하게 됩니다.
이걸로 Hunt And Kill 알고리즘 구현이 모두 끝이 났습니다. 이젠 GameStart버튼을 누르면 완전한 미로가 생성되는 걸 볼 수 있습니다.
10 x 10의 미로보다 더 큰 사이즈의 미로도 만들 수 있습니다.
이것으로 강의를 마치고 다음에는 유저가 직접 미로를 플레이 하도록 만드는 법을 배워보겠습니다.
고생하셨습니다.~
'엑셀 vba 게임' 카테고리의 다른 글
엑셀 vba 미로만들기(9) - 직접 플레이하기 (1) | 2020.03.10 |
---|---|
엑셀 vba 미로만들기(7) - Hunt And Kill 알고리즘 구현2 (0) | 2020.03.07 |
엑셀 vba 미로만들기(6) - Hunt And Kill 알고리즘 구현1 (0) | 2020.03.05 |
엑셀 vba 미로만들기(5) - 미로 테두리 설정 (0) | 2020.02.29 |
엑셀 vba 미로만들기(4) - 배열 (0) | 2020.02.29 |
WRITTEN BY