본 강의부터 본격적으로 미로만들기 알고리즘인 Hun And Kill을 구현해 봅시다.

 

이전 강의까지의 코드입니다.

Sub MakeMaze()
	worksheets("Sheet2").Select
	Dim map_size As Integer
	Dim maze() 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
End Sub

 

Hunt And Kill이 알고리즘은 아래의 링크에 자세히 설명되어 있습니다.

2020/02/26 - [엑셀 vba 게임] - 엑셀 vba 미로만들기(1) - Hunt And Kill 알고리즘 설명

 

엑셀 vba 미로만들기(1) - Hunt And Kill 알고리즘 설명

본 강의에서는 엑셀 vba를 이용해 미로를 만들어 보겠습니다. 우선 미로를 만드는 알고리즘에는 여러가지가 있지만 저희가 쓸 알고리즘은 Hunt And Kill 알고리즘 입니다. 1. 미로가 아닌 칸을 랜덤으로 선택합니..

comgonghakdo.tistory.com

먼저 알고리즘의 첫번째인 임의의 칸을 설정해 봅시다

 

먼저 난수를 생성해 봅시다. 난수 생성 방법도 아래의 링크에서 자세한 설명을 찾아보실 수 있습니다.

2020/02/21 - [엑셀 vba 기초] - 엑셀 vba 랜덤한 수 생성하는 법

 

엑셀 vba 랜덤한 수 생성하는 법

본 강의에서는 랜덤한 수를 생성하는 법을 알아보겠습니다. 랜덤한 수를 생성할 때는 기본적으로 Rnd가 이용됩니다. Sub CreateRandomNumber() Dim randomNumber As Integer randomNumber = Rnd End Sub 하지만 위..

comgonghakdo.tistory.com

Sub MakeMaze()
	Randomize '난수 Seed값 초기화
	Dim row, col  As Integer
    
	''' 이전 강의의 코드(생략) '''
	
	row = Int(Rnd * map_size) + 2
	col = Int(Rnd * map_size) + 2
	maze(row,col) = True
	Cells(3+row, 3+col).Interior.ColorIndex = 6
End Sub

row와 col의 변수를 새로 생성하고 각 변수에 테두리를 제외한 셀에 해당하는 행과 열을 넣어야 합니다.

 

난수 생성의 범위 크기는 map_size이고 시작값은 테두리를 제외해야 하므로 1이 아닌 2를 사용해야 합니다.

 

만약 map_size가 5라면 2~6까지의 난수를, map_size가 10이라면 2~12까지의 난수를 생성해야 하기 때문입니다.

 

그 후에 랜덤으로 선택된 셀에서 부터 미로를 만들어 갈 것이므로 maze(row,col)의 값을 True로 설정해 줍니다.

 

또한 어떤 셀이 선택되었는지 알아보기 위해서 선택된 셀의 배경색을 노란색으로 바꿔줍니다

 

여기서 유의해야 할 점은 셀을 선택할 때에는 테두리 안의 미로를 선택해야 하므로 조이스틱크기인 3을 더한  Cells(3+row, 3+col)과 같이 표현해 줍니다.

row=9, col=5의 셀이 선택된 모습

이젠 노란색 셀을 움직여 봅시다.

 

Hunt And Kill의 첫번째 로직은 미로가 아닌 칸이 미로를 만들면서 나아갈 수 있을 때까지 나아가는 것입니다.

 

이를 코드로 구현해 봅시다.

 

Sub MakeMaze()
	Randomize '난수 Seed값 초기화
	Dim row, col  As Integer
    
	Dim move As Integer
    
	''' 이전 강의의 코드(생략) '''
	
	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)
		''' 코드 작성 중...'''
	Loop
End Sub

우선 움직일 방향을 정하는 move 변수를 생성해 줍니다.

 

그리고 Do Until Loop 반복문을 사용합니다.

 

maze(row-1, col)은 현재 선택된 미로의 위쪽 셀

maze(row+1,col)은 현재 선택된 미로의 아래쪽 셀

maze(row,col-1)은 현재 선택된 미로의 왼쪽 셀

maze(row,col+1)은 현재 선택된 미로의 오른쪽 셀

 

입니다.

 

그리고 maze()의 배열값이 True라면 그 칸은 이미 미로가 생성되었다는 뜻입니다.

 

따라서 Do Until Loop 반복문에 사용된 조건문은 현재 선택된 미로에서 더이상 나아갈 곳이 없다는 걸 의미합니다.

 

그럼 계속해서 코드를 작성해 봅시다.

 

코드가 길어지면 보기 어려우니 아래의 예제에서는 Do Until Loop 반복문의 안쪽 코드만 작성하겠습니다.

 

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
Loop

move 변수에 1부터 4까지 중 난수를 입력해 줍니다. 그 후에 Select Case 문으로 move의 각 값에 맞는 코드를 실행해 줍니다.

 

move = 1 일 때를 보면,

 

move = 1 은 위쪽으로 이동을 뜻하므로 If maze(row-1, col) = False 를 통해 위쪽 셀이 미로인지 확인합니다.

maze()의 값이 False라면 미로가 아니라는 뜻이므로 우선 Cells().Borders(xlEdgeBottom).LineStyle = xlNone을 이용해 위쪽 셀의 아래쪽 테두리를 지워줍니다.

 

그 후에 row 값에서 1을 빼고 Select Case 문을 빠져나옵니다.

 

Select Case 문 밖에 있는 남은 코드를 실행함으로써 이동한 셀도 미로로 선택이 되고 셀 배경색도 노란색으로 바뀌게 됩니다.

 

매크로 실행 사진

위의 사진과 같이 미로의 생성 과정을 딜레이를 통해 보고싶다면 시간 지연을 이용하시면 됩니다.

 

시간 지연에 대한 자세한 설명은 아래 링크에 있습니다.

2020/02/22 - [엑셀 vba 기초] - 엑셀 vba 시간 지연(딜레이) 넣는 법

 

엑셀 vba 시간 지연(딜레이) 넣는 법

Sleep(x) 시간 지연은 반복문 실행시 너무 빠르게 지나가 진행 과정을 볼 수 없거나, 엑셀을 통한 시각적 효과(게임 등)를 만들 때 사용합니다. 여러가지 방법이 있지만 필자는 가장 편한 Sleep(x) 함수를 사용합..

comgonghakdo.tistory.com

지금까지의 코드를 올리면서 이번 강의를 마치겠습니다.

 

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
	
	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
End Sub

 


WRITTEN BY
컴공학도

,