본 강의부터 본격적으로 미로만들기 알고리즘인 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
컴공학도

,

본 강의에서는 엑셀 vba에서 셀을 오름차순 또는 내림차순으로 정렬하는 법을 다루겠습니다.

 

일단 기본적으로 Sort라는 함수를 사용합니다.

 

Sort의 인자로는 기본적으로 Key와 Order가 있습니다.

 

Key는 말 그대로 정렬의 기준을 정하는 것이고 값은 Cell 또는 Range 등이 들어갈 수 있습니다.

 

Order는 오름차순 또는 내림차순을 정할 수 있습니다. 오름차순은 "xlAscending", 내림차순은 "xlDescending" 입니다.

 

아래 예제를 통해 더 자세히 알아보겠습니다.

 

Sub Macro()
	Range("A1:A9").Sort Key1:=Cells(1,1), Order1:=xlAscending
End Sub

Key1값을 1행1열로 하였고 정렬은 오름차순인 xlAscending으로 설정하여 실행한 결과입니다.

 

Cells(1,1)이 아니라 Range("A1")으로 하여도 무방합니다.

 

여기서 Key의 변수 이름이 Key1로 뒤에 숫자가 붙는 이유는 Key값과 Order의 값은 3개까지 가능하며 Key1, Key2, Key3 로 사용할 수 있습니다. Order도 동일합니다.

 

아래 예제를 통해 여러개의 키의 값으로 정렬한걸 보여드리겠습니다.

 

Sub macro2()
	Range("A1:B20").Sort Key1:=Range("B1"), Order1:=xlAscending, Key2:=Range("A1"), Order2:=xlDescending
End Sub

우선 Range("A1:B20")으로 두개의 열을 포함시켜 줍니다. 이럴경우 한 개의 열을 기준으로 정렬하면 다른 열의 값들도 따라오게 됩니다.

 

첫번째 정렬기준은 알파벳 오름차순이고, 두번째 기준을 숫자의 내림차순입니다.

 

먼저 첫번째 기준대로 알파벳 오름차순이 된 후에, 같은 알파벳을 가진 범위안에서 숫자 내림차순이 됩니다.

 

이렇듯 여러개의 기준으로 정렬을 할 수 있습니다.

 

이것으로 Sort함수 사용법을 마치겠습니다.


WRITTEN BY
컴공학도

,

엑셀에는 Sub 프로시저만이 아니라 Function 프로시저도 존재합니다.

 

Sub는 단순히 어떤 동작을 시행할 뿐이지만, Function는 Sub처럼 동작을 시행한 후 결과값을 반환하게 됩니다.

 

우리가 엑셀에서 많이 쓰는 =SUM(A1,A2)도 Function이라 할 수 있습니다.

 

사용법은 아래에 아주 간단한 예제를 통해 보여드리겠습니다.

 

Function test(num As Integer)
	test = num + 3
End Function

 

정수형 값을 하나 받아 3을 더한 뒤 반환하는 함수입니다.

 

반환하는 방법은

 

함수이름 = 반환값

 

입니다. 위의 예제에서는 t가 함수이름, num + 3 이 반환값입니다.

 

이 함수를 사용하는 방법은 두가지 있습니다.

 

1. 다른 프로시저에서 부르는 법

 

Sub callTest()
	Dim give, take As Integer
	give = 1
	take = test(give)
End Sub

위의 예제에서는 take라는 변수의 값에 test(give)의 반환값인 4가 들어오게 됩니다.

 

2. 셀에서 부르는 법

 

우리가 보통 엑셀 함수라고 하면 셀에 입력하는 =SUM() 또는 =AVERAGE() 를 떠올리실 겁니다. 이들은 엑셀의 기본 내장 함수로써 이미 Function SUM() 이나 Function AVERAGE() 같이 정의되어 있습니다.

 

우리가 만든 함수도 이와 같은 방식으로 쓸 수 있습니다.

 

=test(A1)을 입력하면 A1의 값인 4에 3을 더해 7이 된 것을 볼 수 있습니다.

 

위와 같은 방식들을 사용하여 Function을 통해 많은 기능을 직접 만들 수 있습니다.

 

Function의 괄호() 안에 들어가는 인수(argument)들도 여러방식으로 받을 수 있습니다.

 

Function test() 처럼 인수가 아예 없어도 되고,

Function test(a,b,c,d) 처럼 뒤에 As Integer 를 붙이지 않아도 됩니다. (이럴경우 컴퓨터가 자동적으로 변수의 타입을 정해야 하기 때문에 시행 속도가 아주아주 약간 느려질 수 있습니다.)

 

Function test(a,b, Optional c = 0, Optional d = 1) 처럼 변수 앞에 Optional 을 붙여 선택적으로 인수를 줄 수 있습니다.

 

아래의 예제를 통해 더 자세히 알아봅시다.

 

Sub callTest()
	Dim num1, num2, result As Integer
    num1 = 1
    num2 = 2
	result = test(num1, num2)
    MsgBox result
End Sub

Function test(a, b, Optional c = 3)
	Dim d As Integer
	d = a + b + c
	test = d
End Function

 

callTest()의 실행값

 

test 함수에서 받는 인자는 a, ,b, c로 3개이지만 callTest에서 test 함수를 부를 때에는 num1 과 num2, 2개의 인자만 넘겨주었음에도 오류가 뜨지 않습니다.

 

이는 인자 c의 성질이 Optional로써 필수적으로 받아야하는 인자가 아니기 때문입니다.

 

인자를 넘겨준다면 그 인자의 값대로 설정이 되겠지만, 위의 예제처럼 인자를 넘겨주지 않는다면 기본값인 3이 됩니다.

 

여기서 주의할 점은 Optional 설정의 변수는 가장 오른쪽에 와야됩니다.

 

Function test(Optional a, b, c) 이런 식으로 왼쪽에 위치하게 되면 오류가 뜨게 됩니다.

 

Optional 성질의 인자를 여러개 생성할 수 있지만 배열을 이용해 무한대로 생성할 수도 있습니다. 이는 전달할 변수의 개수가 미정일 때 이용합니다.

 

Function test(ParamArray list()) 식으로 ParamArray설정을 이용합니다.

 

Sub Main()
	Dim result As Integer
	result = sum(1,2,3,4,5,6,7,8,9,10)
	MsgBox result
End Sub

Function sum(ParamArray nums())
	Dim temp As Integer
	temp = 0
    
	For Each num in nums
		temp = temp + num
	Next num
    
	sum = temp
End Function

 

Main()의 실행값

숫자 1부터 10까지 인수로 주었을 때, sum 함수의 num() 배열에 들어가게 됩니다.

 

그 후 For 반복문에서 차례차례 더한 후 반환하게 됩니다.

 

이상으로 Function 사용법을 마치겠습니다.


WRITTEN BY
컴공학도

,

본 강의에서는 미로의 테두리들을 구성해 봅시다.

 

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

 

Sub MakeMaze()
	worksheets("Sheet2").Select
	Dim map_size As Integer
	Dim maze() As Boolean '동적배열 선언
    
	map_size = worksheets("Sheet1").Cells(8,2).value
	ReDim maze(1 To map_size+2, 1 To 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
End Sub

 

먼저 이전 강의를 통해 우리가 미로를 만들 워크시트 왼쪽 위 3x3의 범위에 조이스틱을 만들었고 숨겼습니다.

 

위의 사진과 같이 셀의 시작이 Cells(4,4)부터입니다. 따라서 항상 특정 행과 열을 선택할 때에는 조이스틱의 크기인 3을 더해주어야 합니다.

 

먼저 미로를 구현할 판을 그려봅시다. 미로의 테두리 색은 빨간색으로 지정하겠습니다.

 

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

Cells()에 행과 열에 3씩 더한 것을 보실 수 있습니다. 또한 Range().Interior.ColorIndex의 값을 변경함으로써 셀의 배경색을 바꿀수 있습니다.

 

먼저 테두리를 포함한 미로의 모든 셀 배경색을 빨간색으로 지정합니다. 그리고나서 테두리를 제외한 미로의 셀 배경색을 지웁니다.

 

이렇게 되면 아래의 그림과 같이 셀이 만들어 집니다.

 

이후에 미로생성을 위해 각 셀에 테두리의 두께를 지정해 줍시다. 테두리의 두께는 Range().Borders.Weight로 지정할 수 있습니다.

 

xlHairline: 가장 가는 실선

xlThin: 가는 실선

xlMedium: 보통굵기의 선

xlThick: 굵은 실선

 

Range(Cells(4,4), Cells(3+map_size+2, 3+map_size+2)).Borders.Weight = xlThcik

위의 코드를 추가하면 아래의 셀에 두꺼운 테두리가 적용됩니다.

 

이젠 본격적으로 Hunt And Kill 알고리즘을 구현할 준비를 마쳤습니다. 다음 강의부터 미로 알고리즘을 구현해 봅시다!


WRITTEN BY
컴공학도

,