내일배움캠프(Unity)

사천성 마무리 하기 (내일배움캠프 스터디)

빵어 2023. 10. 29. 15:37

사천성 마무리하기

내일부터 본 캠프가 시작된다.

시작되기 전에, 만들고 있던 사천성 게임을 마무리하려고 한다.

 

1. BFS 적용

DFS를 활용해 코드를 짰지만,  사천성은 BFS를 활용하는 것이 더 효율적이라는 생각이 들어 BFS를 적용해봤다.

그리고 매개변수가 너무 많아 복잡해보이던 재귀함수를 정리할 겸 CardInfoForRecursive라는 class를 만들었다.

 

원래 코드

...
	bool IsCardConnected()
    {
        isConneted = false;
        int turnCount = 0;

        Vector3 dir = secondCard.transform.position - firstCard.transform.position;
        
        int[] dx = { 0, 0, 1, -1 };
        int[] dy = { 1, -1, 0, 0 };

        if(dir.x < 0)
            (dx[2], dx[3]) = (dx[3], dx[2]);
        if(dir.y < 0)
            (dy[0], dy[1]) = (dy[1], dy[0]);
        if(Mathf.Abs(dir.x) > Mathf.Abs(dir.y))
            (dx, dy) = (dy, dx);

        cardInfo.dx = dx;
        cardInfo.dy = dy;

        int firstCardNum = firstCard.GetComponent<Card>().cardNum;
        bool[] visited = new bool[cardList.Count];
        visited[firstCardNum] = true;
        int dirNum = 0;

        return CheckCardConnectionRecursive(turnCount, visited, firstCardNum, dirNum, dx, dy);
    }

    bool CheckCardConnectionRecursive(int turnCount, bool[] visited, int tempNum, int dirNum, int[] dx, int[] dy)
    {
        if (isConneted)
            return true;

        int temp = dirNum + 4;
        for (int i = dirNum; i < temp; ++i)
        {
            int index = i % dx.Length;

            int nx = tempNum % xCount + dx[index];
            int ny = tempNum / yCount + dy[index];
            if (nx < 0 || nx >= xCount || ny < 0 || ny >= yCount)
                continue;

            // 카드의 num
            int num = tempNum + dx[index] + (dy[index] * xCount);
            if (num >= cardList.Count || num < 0)
                continue;

            // 방향이 지금까지와 다르다면 ++turnCount
            int tempdir = dirNum;
            int tempTurnCount = turnCount;
            if (tempdir != index)
            {
                if (tempTurnCount < 3)
                    ++tempTurnCount;
                else
                {
                    Debug.Log("회전 횟수 초과 " + num + " dir: " + tempdir);
                    continue;
                }
                tempdir = index;
            }

            // secondCard와 같은 자리라면 true
            if (num == secondCard.GetComponent<Card>().cardNum)
            {
                isConneted = true;
                return true;
            }

            // 카드가 있는 자리라면
            if (cardList[num].activeSelf)
            {
                Debug.Log("자리에 카드 " + num + " dir: " + dirNum);
                continue;
            }

            // 방문한 자리라면
            if (visited[num])
            {
                Debug.Log("방문한 자리 " + num + " dir: " + dirNum);
                continue;
            }
            bool[] tempVisited = (bool[])visited.Clone();
            tempVisited[num] = true;

            if (CheckCardConnectionRecursive(tempTurnCount, tempVisited, num, tempdir, dx, dy))
                return true;
        }

        return false;
    }
    ...

 

 

변경한 코드

class CardInfoForRecursive
{
    public int turnCount;
    public bool[] visited;
    public int num;
    public int dirNum;
    public int[] dx;
    public int[] dy;

    public CardInfoForRecursive(int turnCount, bool[] visited, int num, int dirNum, int[] dx, int[] dy)
    {
        this.turnCount = turnCount;
        this.visited = visited;
        this.num = num;
        this.dirNum = dirNum;
        this.dx = dx;
        this.dy = dy;
    }
}

 

    bool IsCardConnected()
    {
        isConneted = false;

        CardInfoForRecursive cardInfo
            = new(0, new bool[cardList.Count], firstCard.GetComponent<Card>().cardNum, 0, new int[4], new int[4]);

        Vector3 dir = secondCard.transform.position - firstCard.transform.position;
        
        int[] dx = { 0, 0, 1, -1 };
        int[] dy = { 1, -1, 0, 0 };

        if(dir.x < 0)
            (dx[2], dx[3]) = (dx[3], dx[2]);
        if(dir.y < 0)
            (dy[0], dy[1]) = (dy[1], dy[0]);
        if(Mathf.Abs(dir.x) > Mathf.Abs(dir.y))
            (dx, dy) = (dy, dx);

        cardInfo.dx = dx;
        cardInfo.dy = dy;
        
        int firstCardNum = firstCard.GetComponent<Card>().cardNum;
        cardInfo.visited[firstCardNum] = true;

        return CheckCardConnectionRecursive(cardInfo);
    }

    bool CheckCardConnectionRecursive(CardInfoForRecursive cardInfo)
    {
        if (isConneted)
            return true;

        Queue<CardInfoForRecursive> cardQueue = new Queue<CardInfoForRecursive>();

        int temp = cardInfo.dirNum + cardInfo.dx.Length;
        for (int i = cardInfo.dirNum; i < temp; ++i)
        {
            int index = i % cardInfo.dx.Length;

            int nx = cardInfo.num % xCount + cardInfo.dx[index];
            int ny = cardInfo.num / yCount + cardInfo.dy[index];
            if (nx < 0 || nx >= xCount || ny < 0 || ny >= yCount)
                continue;

            // 카드의 num
            int num = cardInfo.num + cardInfo.dx[index] + (cardInfo.dy[index] * xCount);
            if (num >= cardList.Count || num < 0)
                continue;

            // 방향이 지금까지와 다르다면 ++turnCount
            int tempdir = cardInfo.dirNum;
            int tempTurnCount = cardInfo.turnCount;
            if (tempdir != index)
            {
                if (tempTurnCount < 3)
                    ++tempTurnCount;
                else
                {
                    Debug.Log("회전 횟수 초과 " + num + " dir: " + tempdir);
                    continue;
                }
                tempdir = index;
            }

            // secondCard와 같은 자리라면 true
            if (num == secondCard.GetComponent<Card>().cardNum)
            {
                isConneted = true;
                return true;
            }

            // 카드가 있는 자리라면
            if (cardList[num].activeSelf)
            {
                Debug.Log("자리에 카드 " + num + " dir: " + cardInfo.dirNum);
                continue;
            }

            // 방문한 자리라면
            if (cardInfo.visited[num])
            {
                Debug.Log("방문한 자리 " + num + " dir: " + cardInfo.dirNum);
                continue;
            }
            bool[] tempVisited = (bool[])cardInfo.visited.Clone();
            tempVisited[num] = true;

            CardInfoForRecursive card = new(tempTurnCount, tempVisited, num, tempdir, cardInfo.dx, cardInfo.dy);
            cardQueue.Enqueue(card);
        }

        while (cardQueue.Count > 0)
            if(CheckCardConnectionRecursive(cardQueue.Dequeue()))
                return true;

        return false;
    }

바꾼 결과는 굉장히 성공적이다.

재귀함수가 도는 횟수가 현저히 줄었고 카드를 맞출 때 눈에 보이던 버벅거림도 없어졌다.