Map 만들기

이승한·2023년 7월 20일
0

CSharp

목록 보기
16/25
post-custom-banner

이전에 간단히 렌더링 하는 것을 해봤는데 본격적으로 Map 만드는 연습


기본적인 테두리만 막힌 MAP

메인 함수에서 Board의 size가 [25,25]인 Map

class Board
{
	const char CIRCLE = '\u25cf' //원모양
    public TileType[,] _tile;
    public int _size;
    
    public enum TileType
    {
    	Empty,
        Wall
    }
    
    public void Initialize(int size)
    {
    	_tile = new TileType[size,size];
    	_size = size;
        
        for(int y = 0; y < _size; y++)
        {
        	for(int x = 0; x < _size ; x++)
            {
            	if(x == 0 || x == _size-1 || y == 0 || y == _size -1)
                	_tile[y,x] == TileType.Wall;
                else
                	_tile[y,x] == TileType.Empty;
            }
        }
    }
    
    public void Render()
    {
    	ConsoleColor prevColor = Console.ForegroundColor;
    	for(int y = 0; y < _size; y++)
        {
        	for(int x = 0; x <_size; x++)
            {
            	Console.ForegroundColor = GetTileColor(_tile[y,x]);
                Console.Write(CIRCLE);
            }
        }
        ConsoleForegrounderColor = prevColor;
    }
    
    public ConsoleColor GetTileColor(TileType tile)
    {
    	switch(tile)
        {
        	case TileType.Empty:
            	return ConsoleColor.Green;
            case TileType.Wall:
            	return ConsoleColor.Red;
            default:
            	return ConsoleColor.Green;
        }
    }
}

결과 :

Binary Tree 미로

Mazes for Programmers 책에서 나온 가장 기본적인 미로 생성 알고리즘 중 하나

위에서 기본적인 맵을 기반으로 알아보려 한다.

1단계 : 길을 x좌표,y좌표의 짝수번째를 다 막는다.

public void Initialize(int size)
{
    _tile = new TileType[size,size];
    _size = size;
    //길을 다 막아버리는 작업
    for(int y = 0; y < _size; y++)
    {
        for(int x = 0; x < _size ; x++)
        {
            if(x % 2 == 0 || y % 2== 0) // 1단계 : x나 y가 짝수인 경우 벽으로 막는다.
                _tile[y,x] == TileType.Wall;
            else
                _tile[y,x] == TileType.Empty;
        }
    }
}

결과 :

2단계 : 그래서 비어있는 초록색 점마다 랜덤으로 우측 혹은 아래로 길을 만든다.

public void Initialize(int size)
{
    _tile = new TileType[size,size];
    _size = size;
    //1단계 : 길을 다 막아버리는 작업
    for(int y = 0; y < _size; y++)
    {
        for(int x = 0; x < _size ; x++)
        {
            if(x % 2 == 0 || y % 2== 0) // 1단계 : x나 y가 짝수인 경우 벽으로 막는다.
                _tile[y,x] == TileType.Wall;
            else
                _tile[y,x] == TileType.Empty;
        }
    }
    //2단계 : Empty에서 랜덤으로 우측 혹은 아래로 길을 뚫는 작업
    Random rand = new Random();
    for (int y = 0; y < _size; y++)
    {
         for (int x = 0; x < _size; x++)
         {
             if (x % 2 == 0 || y % 2 == 0)
                  continue;
             if(rand.Next(0,2) == 0)
                 _tile[y, x+1] = TileType.Empty;
             else
                 _tile[y+1,x] = TileType.Empty;
         }
     }
}

결과:

결과를 보면,


  1. 지금 만들고 있는 미로 생성 알고리즘은 벽으로 둘러 쌓였다는 가정을 해야하기에
    Board의 size가 홀수여야 하니 Initialize함수에서 size가 짝수로 들어왔을 때 return을 해준다.
if(size % 2 ==0)
	return;

  1. 가장자리까지도 길을 만들어주니 수정이 필요하다.
public void Initialize(int size)
{
    _tile = new TileType[size,size];
    _size = size;
    //1단계 : 길을 다 막아버리는 작업
    for(int y = 0; y < _size; y++)
    {
        for(int x = 0; x < _size ; x++)
        {
            if(x % 2 == 0 || y % 2== 0) // 1단계 : x나 y가 짝수인 경우 벽으로 막는다.
                _tile[y,x] == TileType.Wall;
            else
                _tile[y,x] == TileType.Empty;
        }
    }
    //2단계 : Empty에서 랜덤으로 우측 혹은 아래로 길을 뚫는 작업
    Random rand = new Random();
    for (int y = 0; y < _size; y++)
    {
         for (int x = 0; x < _size; x++)
         {
             if (x % 2 == 0 || y % 2 == 0)
                  continue;
                  
             //추가해서 가장자리 이전 y좌표는 오른쪽으로 길을 내주고 x좌표는 밑으로 길을 내준다.
             if(y == _size -2 && x == _size -2)
             	continue;
             if( y == _size - 2)
             {
             	_tile[y,x+1] = TileType.Empty;
                continue;
             }
             if( x == _size -2)
             {
             	_tile[y+1,x] = TileType.Empty;
                continue;
             }
             if(rand.Next(0,2) == 0)
                 _tile[y, x+1] = TileType.Empty;
             else
                 _tile[y+1,x] = TileType.Empty;
         }
     }
}

결과 :

이 알고리즘의 단점은 마지막 y좌표와 x좌표의 마지막 줄들이 일자로 계속 길이 있는것으로 볼 수 있다.

SideWinder 미로 생성 알고리즘

위에 만들어봤던 binaryTree 알고리즘 처럼 미로 알고리즘 중에서는 간단한 편에 속하는데
간단할수록 미로가 단조롭게 나온다는 것을 알고 있어야한다.

SideWinder의 아이디어는

아까처럼 다시 막은 상태에서

  1. 초록점을 기준으로 오른쪽으로 랜덤으로 길을 만든 다음
  1. 가로의 초록줄에서 랜덤으로 홀수번째 초록을 다시 선택하여 밑으로 랜덤으로 길을 만든다.
for (int y = 0; y < _size; y++)
{
    for (int x = 0; x < _size; x++)
    {
        if (x % 2 == 0 || y % 2 == 0)
            _tile[y, x] = TileType.Wall;
        else
            _tile[y, x] = TileType.Empty;
    }
}

Random rand = new Random();
for (int y = 0; y < _size; y++)
{
    int count = 1;
    for (int x = 0; x < _size; x++)
    {
        if (x % 2 == 0 || y % 2 == 0)
            continue;

        if (rand.Next(0, 2) == 0) //가로로 길을 내는 부분
        {
            _tile[y, x + 1] = TileType.Empty;
            count++;              //가로로 몇번 길을 냈는지 count
        }
        else //세로로 길을 내는 부분
        {
            int randomIndex = rand.Next(0,count);
            _tile[y + 1, x - randomIndex * 2] = TileType.Empty;
            count = 1;
        }
    }
}

세로로 길을 내는 부분에서 x - randomIndex * 2 부분은 아까 아이디어 2번부분에서
지금까지 길을 낸 가로줄 초록에서 랜덤으로 하나를 선택해야하니까 [공간][벽][공간][벽]
으로 이루어져있으니 벽을 넘어 이전 공간으로 가기 위해 x좌표를 2씩 뒤로 가야하기 때문이다.

결과:

아까 BinaryTree처럼 마지막 이전부분은 똑같은 코드로 처리해준다.

for (int y = 0; y < _size; y++)
{
    for (int x = 0; x < _size; x++)
    {
        if (x % 2 == 0 || y % 2 == 0)
            _tile[y, x] = TileType.Wall;
        else
            _tile[y, x] = TileType.Empty;
    }
}

Random rand = new Random();
for (int y = 0; y < _size; y++)
{
    int count = 1;
    for (int x = 0; x < _size; x++)
    {
        if (x % 2 == 0 || y % 2 == 0)
            continue;
		
        if( y == _size -2 && x == _size -2)
        	continue;
        if( y == _size - 2)
        {	
        	_tile[y,x+1] = TileType.Empty;
        	continue;
        }
        if( x == _size -2)
        {	
        	_tile[y+1,x] = TileType.Empty;
        	continue;
        }
        
        
        if (rand.Next(0, 2) == 0) //가로로 길을 내는 부분
        {
            _tile[y, x + 1] = TileType.Empty;
            count++;              //가로로 몇번 길을 냈는지 count
        }
        else //세로로 길을 내는 부분
        {
            int randomIndex = rand.Next(0,count);
            _tile[y + 1, x - randomIndex * 2] = TileType.Empty;
            count = 1;
        }
    }
}

BinaryTree처럼 SideWinder 방법도 마지막 이전 줄은 모두 공간으로 단조롭게 나타는 단점이 있다.

post-custom-banner

0개의 댓글