오른손 법칙

이승한·2023년 7월 24일
0

CSharp

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

미로에서 길찾기에서 사용 가능한 오른손 법칙을 알아보자.

어떤 미로에 갇혔을 때, 오른쪽 벽에 손을 대고 계속 가다보면 출구를 찾을 수 있다는 개념이다.

사실, 최단거리를 찾아주는 것도 아니고 그냥 길을 가는 알고리즘이라고 볼 수 있다.
이전에 했던 플레이어 임의의 이동에서 바꿀 것이다.

이전에 했던 플레이어 임의의 이동링크

전체적인 코드

class Board
{
	const char CIRCLE = '\u25cf';
    public TileType[,] Tile { get; private set; }
    public int Size { get; private set; }
    public int DestY { get; private set; }
    public int DestX { get; private set; }
    
    Player _player;
    
    public enum TileType
    {
    	Empty,
        Wall
    }
    
    public void Initialize(int size, Player player)
    {
    	if(size % 2 == 0)
        	return;
        Tile = new TileType[size,size];
        _player = player;
        Size = size;
        
        DestY = Size - 2;
        DestX = Size - 2;
        
        GenerateBySideWinder(); // 이전 맵만들기 참조
    }
    
    public void Render()
    {
    	ConsoleColor prevColor = Console.ForegroundColor;
        for(int y = 0; y < Size; y++)
        {
        	for(int x = 0; x < Size; x++)
            {
            	if(y == _player.PosY && x == _player.PosX) //플레이어좌표 파란색
                {
                	Console.ForegroundColor = ConsoleColor.Blue;
                }
                else if( y == DestY && x == DestX) //목적지좌표 노란색
                {
                	Console.ForegroundColor = ConsoleColor.Yellow;
                }
                else
                {
                	Console.ForegroundColor = GetTileColor(Tile[y,x]);
                }
                Console.Write(CIRCLE);
            }
            Console.WriteLine();
        }
        Console.ForegroundColor = prevColor;
    }
    public ConsoleColor GetTileColor(TileType type)
    {
    	switch(type)
        {
        	case TileType.Empty:
            	return ConsoleColor.Green;
            case TileType.Wall:
            	return ConsoleColor.Red;
            default:
            	return ConsoleColor.Green;
        }
    }
}

이전 플레이어 임의의 이동에서 목적지 좌표를 Player 에서 Board로 옮겨 주었다.
목적지 좌표를 노란색으로 표시

Plaer 클래스

class Pos
{
	public Pos(int y,int x) { Y = y; X = x;}
    public int Y;
    public int X;
}

class Player
{
	public int PosY{ get; private set; }
    public int PosX{ get; private set; }
    Random _random = new Random();
    Board _board;
    
    enum Dir // 내가 바라보는 방향
    {	//반시계방향
    	Up = 0,
        Left = 1,
        Down = 2,
        Right = 3
    }
    
    int _dir = (int)Dir.Up; // (int를 쓰는 이유)방향을 계산할때 용이함
    //기본 위치 추가
    List<Pos> _points = new List<Pos>();
    
    public void Initialize(int PosY,int PosX,Board board)
    {
    	PosY = posY;
        PosX = posX;
        _board = board;
        
        //현재 바라보는 방향을 기준으로 좌표 변화
        int[] frontY = new int[] { -1, 0, 1, 0};
        int[] frontX = new int[] { 0, -1, 0, 1};
        //right축{위 왼쪽 아래 오른쪽} 바라보는 방향으로부터 오른쪽방향의 좌표 변화
        int[] rightY = new int[] { 0, -1, 0, 1};
        int[] rightX = new int[] { 1, 0, -1, 0};
        
        _points.Add(new Pos(PosY,PosX));
        //목적지 좌표에 도착할 때까지 실행
        while(PosY != board.DestY || PosX != board.DestX)
        {
        	if(_board.Tile[PosY + rightY[_dir], PosX + rightX[_dir] == Board.TileType.Empty)
            {
            	//1.오른쪽으로 갈 수 있으니 오른쪽으로 90도 회전
                _dir = (_dir - 1 + 4 ) % 4;
                
                //한칸 전진
                PosY = PosY + frontY[_dir];
                PosX = PosX + frontX[_dir];
                _points.Add(new Pos(PosY,PosX));
            }
            else if(_board.Tile[PosY + frontY[_dir], PosX + frontX[_dir] == Board.TileType.Empty) 
            {
            	//2.현재 바라보는 방향을 기준으로 전진할 수 있으면 한칸 전진
                PosY = PosY + frontY[_dir];
                PosX = PosX + frontX[_dir];
                _points.Add(new Pos(PosY,PosX));
            }
            else 
            {
            	//3.오른쪽도 못가고 앞으로도 못가니  //왼쪽으로 90도 회전
            	_dir = (_dir + 1 + 4 ) % 4;	
            }
        }
    }
    
    const int MOVE_TICK = 100;
    int _sumTick = 0;
    int _lastTick = 0;
    
    public void Update(int deltaTick)
    {
    	_sumTick += deltaTick;
        if(_sumTick >= MOVE_TICK)
        {
        	if(_lastTick >= _points.Count)
            	return;
            _sumTick = 0;
            
            PosY = _points[_lastIndex].Y;
            PosX = _points[_lastIndex].X;
            _lastIndex++;
        }
    }
}

Update 로직은 Initialize에서 목적지좌표에 도착할 때까지 y,x좌표를 List에 저장하여 하나씩 꺼내어 업데이트 해주는 방식으로 바꿔 주었다.

Intialize를 보면,

  1. 방향을 열거형으로 하여 오른쪽으로 갈수 있으면 오른쪽으로 90도 회전 후 앞으로 전진
    _dir = (_dir - 1 + 4 ) % 4;
    실제 계산을 해보면 Up 은 (Up(0) - 1 + 4 ) % 4 = 3 이 나오므로 Right가 제대로 나온다.
    그리고 앞으로 전진은 frontY 와 frontX 의 int배열로 처리하였는데
    enum Dir 바라보는 방향
    {
    Up,
    Left,
    Down,
    Right
    }
    을 기준으로
    frontY를 살펴보면

    1. 바라보는 방향이 Up일때 front는 Up방향이니 Y축의 좌표 변화는 -1
    2. 바라보는 방향이 Left일때 front는 Left방향이니 Y축의 좌표 변화는 0
    3. 바라보는 방향이 Down일때 front는 Down방향이니 Y축의 좌표 변화는 1
    4. 바라보는 방향이 Right일때 front는 Right방향이니 Y축의 좌표 변화는 0

    frontX를 살펴보면

    1. 바라보는 방향이 Up일때 front는 Up방향이니 X축의 좌표 변화는 0
    2. 바라보는 방향이 Left일때 front는 Left방향이니 X축의 좌표 변화는 -1
    3. 바라보는 방향이 Down일때 front는 Down방향이니 X축의 좌표 변화는 0
    4. 바라보는 방향이 Right일때 front는 Right방향이니 X축의 좌표 변화는 1

    rightY를 살펴보면

    1. 바라보는 방향이 UP일때 오른쪽방향은 Right 이니 Y축의 좌표 변화는 0
    2. 바라보는 방향이 Left일때 오른쪽방향은 Up 이니 Y축의 좌표 변화는 -1
    3. 바라보는 방향이 Down일때 오른쪽방향은 Left이니 Y축의 좌표 변화는 0
    4. 바라보는 방향이 Right일때 오른쪽방향은 Down이니 Y축의 좌표 변화는 1

    rightX를 살펴보면

    1. 바라보는 방향이 UP일때 오른쪽방향은 Right 이니 X축의 좌표 변화는 1
    2. 바라보는 방향이 Left일때 오른쪽방향은 Up 이니 X축의 좌표 변화는 0
    3. 바라보는 방향이 Down일때 오른쪽방향은 Left이니 X축의 좌표 변화는 -1
    4. 바라보는 방향이 Right일때 오른쪽방향은 Down이니 X축의 좌표 변화는 0

    이렇게 정리해서 배열로 만들 수 있다.
    int[] frontY = new int[] { -1, 0, 1, 0 }
    int[] frontX = new int[] { 0, -1, 0, 1 }
    int[] rightY = new int[] { 0, -1, 0, 1 }
    int[] rightX = new int[] { 1, 0, -1, 0 }


    class Pos
    {
    	public Pos(int y,int x){Y = y; X = x;}
        public int Y;
        public int X;
    }

    List를 생성하여 목적지 좌표까지 y,x좌표를 저장하고
    Update를 통해 y,x좌표를 갱신하고 메인함수에서 렌더링하여 사용한다.

    그 결과,


    마지막 목적지까지 파란색원이 도착한것을 볼 수 있다.

post-custom-banner

0개의 댓글