develope_kkyu

[Kotlin] 안드로이드 스도쿠 앱 제작부터 스토어 출시까지 - 2 본문

Android/Kotlin

[Kotlin] 안드로이드 스도쿠 앱 제작부터 스토어 출시까지 - 2

developekkyu37 2024. 1. 4. 22:34
728x90

[Kotlin] 안드로이드 스도쿠 앱 제작부터 스토어 출시까지 - 1

https://developerkkyu37.tistory.com/93

 

[Kotlin] 안드로이드 스도쿠 앱 제작부터 스토어 출시까지 - 1

평소 잠깐잠깐 쉴 때 핸드폰으로 스도쿠 게임 하는 것을 좋아해서 나도 스도쿠 어플 하나 만들어 보면 어떨까라는 생각을 가지고 있었다. 안드로이드 코틀린을 취미로 개발하면서 스도쿠 게임

developerkkyu37.tistory.com

 

1. 스도쿠 문제 배열

 

기본 스도쿠 배열을 만들어보고 출력까지 해봤으니 몇개의 숫자를 가리는 스도쿠 문제 배열을 만들어 본다.

 

MainActivity.kt

var riddleGrid = Array(9) { IntArray(9) }

 

문제의 정답이 되는 solvedgrid를 복사하고 piecesToErase 파라미터를 받아 해당되는 숫자만큼 가리는 CreateRiddleGrid 함수를 만든다. 일단은 40개의 숫자를 가리고 추후 난이도를 선택해 몇개의 숫자를 가릴지 결정하게 될 것이다.

val piecesToErase = 40

fun CreateRiddleGrid(piecesToErase:Int){
        for (i in 0 until 9){
            for (j in 0 until 9){
                riddleGrid[i][j] = solvedgrid[i][j]
            }
        }

        for(i in 0 until piecesToErase){
            var x1 = Random.nextInt(0, 9);
            var y1 = Random.nextInt(0, 9);

            while (riddleGrid[x1][y1] == 0) {
                x1 = Random.nextInt(0, 9);
                y1 = Random.nextInt(0, 9);
            }

            riddleGrid[x1][y1] = 0;

        }
    }

 

일단 이 상태로 출력하게 된다면 가릴 숫자는 0으로 표시될 것이다.

 

2. 스도쿠 문제 배열 디자인

먼저 0은 출력되면 안되기 때문에 DisplayGrid 함수에서 0이 아닌 수들만 텍스트뷰에 담았다.

if (value != 0) {
	textView.text = value.toString()
}

 

그리고 배열의 테두리도 만들어 준다.

기본적인 스도쿠 문제의 테두리를 만들어주고 싶었다. 근데 아직 실력이 부족한 탓에 조금 무식한 방법을 사용해서 표현했다.

기본적인 스도쿠 문제 형태

 

가장 바깥에 있는 굵은 테두리를 보여주기 위해서 GridLayout의 테두리를 만들어 주었다.

 

activity_main.xml

    <GridLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/grid_sudoku"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:rowCount="9"
        android:columnCount="9"
        android:layout_margin="16dp"
        
        // 바깥 테두리 추가
        android:background="@drawable/grid_layout_border"
        
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">
    </GridLayout>

 

grid_layout_border.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#00000000" />
    <stroke
        android:width="3dp"
        android:color="#000000" />
</shape>

 

GridLayout의 너비와 높이를 맞춰 주기 위해서 DisplayGrid 함수에 다음과 같이 작성한다.

 

MainActivity.kt

    private fun DisplayGrid() {
        val gridLayout: GridLayout = findViewById(R.id.grid_sudoku)

		// 한칸의 길이
        var layout_side : Int = 0

        for (i in 0 until 9) {
        	// 총 9개의 칸
            layout_side += 110
            for (j in 0 until 9) {
            	...
                생략
                ...
            }
        }
        // 9개 칸의 길이 만큼 GridLayout의 너비와 높이 지정
        gridLayout.layoutParams.width = layout_side
        gridLayout.layoutParams.height = layout_side
    }

 

안 쪽의 얇은 선들과 굵은 선들을 만들어주기 위해 여기서 조금 무식한 방법을 사용했다.

먼저 배열기준 (3, 3), (3, 6), (6, 3), (6, 6) 칸의 굵은 선은 오른쪽 변과 아래쪽 변이고

매 (3, X)과 (6, X) 칸의 굵은 선은 오른쪽 변,

매 (X, 3)과 (X, 6) 칸의 굵은 선은 아래쪽 변,

그외 나머지 변들은 얇은 선으로 표현했다.

 

right_bottom_border.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:left="-2dp"
        android:top="-2dp">
        <shape android:shape="rectangle">
            <solid android:color="#00000000" />
            <stroke
                android:width="2dp"
                android:color="#000000" />
        </shape>
    </item>

    <item
        android:bottom="-2dp"
        android:right="-2dp">
        <shape android:shape="rectangle">
            <solid android:color="#00000000" />
            <stroke
                android:width="0.5dp"
                android:color="#000000" />
        </shape>
    </item>

</layer-list>

 

right_border.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:bottom="-2dp"
        android:left="-2dp"
        android:top="-2dp">
        <shape android:shape="rectangle">
            <solid android:color="#00000000" />
            <stroke
                android:width="2dp"
                android:color="#000000" />
        </shape>
    </item>

    <item android:right="-2dp">
        <shape android:shape="rectangle">
            <solid android:color="#00000000" />
            <stroke
                android:width="0.5dp"
                android:color="#000000" />
        </shape>
    </item>

</layer-list>

 

bottom_border.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:right="-2dp"
        android:left="-2dp"
        android:top="-2dp">
        <shape android:shape="rectangle">
            <solid android:color="#00000000" />
            <stroke
                android:width="2dp"
                android:color="#000000" />
        </shape>
    </item>

    <item android:bottom="-2dp">
        <shape android:shape="rectangle">
            <solid android:color="#00000000" />
            <stroke
                android:width="0.5dp"
                android:color="#000000" />
        </shape>
    </item>

</layer-list>

 

border.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#00000000" />
    <stroke
        android:width="0.5dp"
        android:color="#000000" />
</shape>

 

이제 출력을 위해 DisplayGrid 함수의 FrameLayout을 추가했다.

 

MainActivity.kt

    private fun DisplayGrid() {
    	...

        for (i in 0 until 9) {
            for (j in 0 until 9) {
            	...

                val frameLayout = FrameLayout(this)
                frameLayout.layoutParams = params
				
                // 테두리 설정
                val borderView = View(this)
                borderView.layoutParams = FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                )
                
                // 오른쪽 아래쪽 굵은선
                if((i==2||i==5)&&(j==2||j==5)){
                    borderView.setBackgroundResource(R.drawable.right_bottom_border)
                    frameLayout.addView(borderView)
                }
                // 오른쪽 굵은선
                else if(j==2 || j==5) {
                    borderView.setBackgroundResource(R.drawable.right_border)
                    frameLayout.addView(borderView)
                }
                // 아래쪽 굵은선
                else if(i==2 || i==5){
                    borderView.setBackgroundResource(R.drawable.bottom_border)
                    frameLayout.addView(borderView)
                }
                // 나머지 얇은선
                else{
                    borderView.setBackgroundResource(R.drawable.border)
                    frameLayout.addView(borderView)
                }

                gridLayout.addView(frameLayout)
            }
        }
        ...
    }

 

최종 DisplayGrid 함수

private fun DisplayGrid() {
        val gridLayout: GridLayout = findViewById(R.id.grid_sudoku)

        var layout_side : Int = 0

        for (i in 0 until 9) {
            layout_side += 110
            for (j in 0 until 9) {
                val value = riddleGrid[i][j]

                val textView = TextView(this)

                if (value != 0) {
                    textView.text = value.toString()
                }

                textView.textAlignment = TextView.TEXT_ALIGNMENT_CENTER
                textView.textSize = 30f

                val params = GridLayout.LayoutParams()
                params.width = 110
                params.height = 110
                params.rowSpec = GridLayout.spec(i)
                params.columnSpec = GridLayout.spec(j)
                textView.layoutParams = params
                gridLayout.addView(textView)

                val frameLayout = FrameLayout(this)
                frameLayout.layoutParams = params

                val borderView = View(this)
                borderView.layoutParams = FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                )

                if((i==2||i==5)&&(j==2||j==5)){
                    borderView.setBackgroundResource(R.drawable.right_bottom_border)
                    frameLayout.addView(borderView)
                }
                else if(j==2 || j==5) {
                    borderView.setBackgroundResource(R.drawable.right_border)
                    frameLayout.addView(borderView)
                }
                else if(i==2 || i==5){
                    borderView.setBackgroundResource(R.drawable.bottom_border)
                    frameLayout.addView(borderView)
                }
                else{
                    borderView.setBackgroundResource(R.drawable.border)
                    frameLayout.addView(borderView)
                }

                gridLayout.addView(frameLayout)
            }
        }
        gridLayout.layoutParams.width = layout_side
        gridLayout.layoutParams.height = layout_side
    }

 

4. 함수 실행 및 화면 출력

MainActivity.kt

 

OnCreate 함수에서 만들어두었던 함수를 실행하고 최종적으로 DisplayGrid 함수를 실행해 화면에 스도쿠 문제 배열을 보여준다.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        InitGrid(solvedgrid)
        ShuffleGrid(solvedgrid, 3)
        CreateRiddleGrid(piecesToErase)
        setContentView(R.layout.activity_main)
        DisplayGrid()
    }
  • 애뮬레이터 실행 모습

728x90