Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

시작은 0부터

18. 달팽이 형식 배열 만들기 본문

C# 학습일지

18. 달팽이 형식 배열 만들기

0base 2022. 7. 25. 01:36
1 2 3
8 9 4
7 6 5

 

▲ 배열을 위 그림처럼 달팽이 모양으로 순서대로 큰값을 넣는 것이 과제였다.

달팽이 모양 순서로 어떻게 집어넣을지를 생각해보다가 바로 이전에 진행했던 지뢰찾기 게임을 만들 때 주변에 폭탄개수를 새는 원리를 생각해보게 되었다. 가장 먼저 입력받은 정사각형 배열의 크기에서 가운데 위치에다 큰 값을 지정한다. 정사각형의 가운데 위치값은 짝수, 홀수에 따라 두가지의 경우만 존재한다.

 

홀수의 경우 입력값을 2로 나눈 값을 각각의 X 와 Y의 값에 넣으면 정확하게 가운데 위치에 최대값이 입력된다. 최대값은 2차원 배열의 길이 값을 넣었다. [3, 3] 의 2차원 배열을 만들면 배열의 길이는 9이고 결국 그 길이가 최대값이기 때문이다.

가운데를 2로 나누었을 때 정확하게 가운데로 나오는 이유는 배열은 0부터 인덱스가 시작되고, 값을 정수형인 int 로 받으면 소수점은 출력되지 않기 때문에 3을 입력받을 경우 2로 나누면 1.5지만 소수점을 제외한 1이 결과값이 되고, 3의 배열은 0, 1, 2 이기 때문에 가운데인 1이 되어 가운데 위치값을 잡을 수 있었다.

 

짝수의 경우 마찬가지로 입력값을 2로 나누고 거기에 X값을 -1을 주었을 때 달팽이형태의 배열의 가운데에 위치한다.

1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7

짝수크기의 정사각형은 정확한 가운데에 위치할 수 없고, 달팽이 모양으로 크기가 커질 때 최종위치가 위와 같은 곳에 자리잡는다. 크기를 어떻게 하든 최종위치값은 홀,짝의 차이만 있을 뿐 동일하다. 그래서 이 최대값의 위치를 구했으니 이를 역순으로 되돌아가는 방법을 생각했다.

 

처음에는 지뢰찾기 방식으로 구현하려고 했는데 코드 구조가 너무 복잡해졌다. 원리는 최대값 위치를 기준으로 왼쪽부터 시계 반대방향으로 자기 자신의 위치에서 1칸씩 8방향의 칸을 확인한다. 값이 없을 경우 값을 집어넣고, 값이 있다면 다음 방향을 확인한다. 그렇게 해서 한바퀴를 돌면 마지막에 값을 넣은 위치를 기준점으로 잡고 다시 8방향을 체크하는 식을 반복해서 처음 위치로 되돌아가는 원리다. 문제는 이를 코드로 만들려다보니 너무 복잡해졌고, 달팽이 배열의 패턴을 보니 더 간단하게 구현할 수 있는 방법을 찾아서 일단 멈추었다. 이 방법은 아직 구현하지 못한 지뢰찾기 0부분 확인시 주변 0이 다 드러나는 코드를 구현할 때 좀 더 공부해봐야될 것 같다.

 

조금 더 간단한 원리는 다음과 같다. (물론 여전히 길고 복잡한 것 같다..)

최대값의 위치에서 역순으로 돌면, 좌로 1칸, 하 1칸, 우 2칸, 상 2칸, 좌 3칸, 하 3칸, 우 4칸.... 이런 식으로 상하좌우를 기준으로 각 2번씩, 이동칸이 1칸씩 늘어나는 패턴이 있었다. 그리고 마지막 0,0으로 돌아가는 경우에만 상하좌우의 각 2번이 아닌 3번으로 이동하면 0,0이 되는 구조였다. 홀수, 짝수는 상하좌우의 순서만 다를 뿐 결국 패턴은 동일했다. 그래서 이 패턴을 활용해서 홀수, 짝수에 따른 2가지의 코드를 작성했다.

 

            int SetSizeX = int.Parse(Console.ReadLine());
            int SetSizeY = SetSizeX;

            int[,] array = new int[SetSizeY, SetSizeX];
            int endNum = array.Length;
            int num = 0;

            int StartY = SetSizeY / 2;
            int StartX = SetSizeX / 2;
            int count2 = 1;

입력값을 정수형 변수 X, Y에 넣고 그 값으로 배열을 만든다. endNum은 배열 최대크기값, num은 최대 크기에서 뺄 값이다. 최대값을 넣어줄 배열 위치이자 시작 위치는 StartX,Y이고, count2는 달팽이 패턴에서 상하좌우 각각 2번씩 움직일 때마다 늘어나는 값을 넣어준다.

 

	if (SetSizeX % 2 != 0) //홀수
            {
                array[SetSizeY / 2, SetSizeX / 2] = endNum; // 최대값 위치

                while (endNum - num > 0)
                {
                    for (int i = 0; i < count2; i++)
                    {
                        StartX--;
                        if (StartX < 0)
                        { num += 2; break; }
                        num++;
                        array[StartY, StartX] = endNum - num;
                    }

                    if (endNum - num < 0) break;

                    for (int i = 0; i < count2; i++)
                    {

                        StartY++;
                        num++;
                        array[StartY, StartX] = endNum - num;
                    }

                    count2++;

                    for (int i = 0; i < count2; i++)
                    {
                        StartX++;
                        num++;
                        array[StartY, StartX] = endNum - num;
                    }

                    for (int i = 0; i < count2; i++)
                    {
                        StartY--;
                        num++;
                        array[StartY, StartX] = endNum - num;
                    }
                    count2++;
                }
            }
            else // 짝수
                array[SetSizeY / 2, SetSizeX / 2 - 1] = endNum;
            StartX--;
            while (endNum - num > 0)
            {
                for (int i = 0; i < count2; i++)
                {
                    StartX++;
                    num++;
                    array[StartY, StartX] = endNum - num;
                }

                for (int i = 0; i < count2; i++)
                {

                    StartY--;
                    num++;
                    array[StartY, StartX] = endNum - num;
                }

                count2++;

                for (int i = 0; i < count2; i++)
                {
                    StartX--;
                    if (StartX < 0)
                    { num += 2; break; }
                    num++;
                    array[StartY, StartX] = endNum - num;
                }

                if (endNum - num < 0) break;

                for (int i = 0; i < count2; i++)
                {
                    StartY++;
                    num++;
                    array[StartY, StartX] = endNum - num;
                }
                count2++;
            }

배열의 인덱스를 벗어나는 경우를 막기 위해 마지막 0,0의 위치로 돌아가는 부분에서 X값을 빼는 부분에서 0을 넘어서면 num값을 2를 넣고 해당 반복문을 빠져나오고, 바로 뒤에 다음 반복문으로 넘어가기전에 반복문을 빠져나갈 수 있는 조건문을 넣어 인덱스 초과 문제를 해결했다.

 

이게 올바른 접근방법인지는 의문이지만 어쨌든 '정사각형' 배열에 한해서 구현은 했다. 직사각형의 경우에도 코드가 더 길어지겠지만 구현은 가능하다. 다만 경우의 수가 더 늘어나는 만큼 코드가 길어지니 좀 더 간단하게 코드를 짜는 방법을 연습해야겠다.