Zielgruppe: Anfänger ohne Android-Vorkenntnisse

Projekt auf GitHub     ← Zurück

1. Was ist SudokuAndroid?

SudokuAndroid ist eine native Android-App für 2D- und 3D-Sudoku. Sie wurde in Kotlin mit Jetpack Compose geschrieben — dem modernen UI-Framework für Android-Entwicklung.

FeatureBeschreibung
2D-Sudoku (9×9)Das bekannte Puzzle auf dem Smartphone-Bildschirm
3D-SudokuSudoku auf einem Würfel — 3D-Darstellung mit SceneView
Touch-SteuerungZellen per Fingertipp auswählen, Zahlen über Zahlen-Pad eingeben
SchwierigkeitsstufenVerschiedene Schwierigkeitsgrade zur Auswahl
Automatischer LöserDas Rätsel auf Knopfdruck lösen lassen
Undo/RedoZüge rückgängig machen und wiederherstellen
FehlerhervorhebungUngültige Eingaben werden farblich markiert

2. Kotlin statt Java

Android-Apps können in Java oder Kotlin geschrieben werden. Seit 2019 ist Kotlin die offizielle Programmiersprache für Android. Kotlin ist moderner, kürzer und sicherer als Java:

// Dasselbe in Java (verbose):
public class SudokuCell {
    private Integer value;
    private boolean isFixed;

    public SudokuCell(Integer value, boolean isFixed) {
        this.value = value;
        this.isFixed = isFixed;
    }

    public Integer getValue() { return value; }
    public boolean isFixed() { return isFixed; }
}

// Dasselbe in Kotlin (kompakt):
data class SudokuCell(
    val value: Int?,       // Int? bedeutet: Int oder null
    val isFixed: Boolean
)
Null Safety in Kotlin: Einer der größten Vorteile von Kotlin ist die eingebaute Null-Sicherheit. In Java kann jede Variable null sein, was oft zu NullPointerException-Fehlern führt. In Kotlin muss man explizit mit ? angeben, ob eine Variable null sein darf — der Compiler verhindert viele Fehler schon beim Schreiben des Codes.

3. Voraussetzungen & Setup

SoftwareVersionZweck
Android StudioNeueste stabile VersionIDE für Android-Entwicklung (enthält alles)
Android SDKAPI 26+ (Android 8.0), Target/Compile SDK 34Wird von Android Studio automatisch installiert
JDK17+Wird von Android Studio mitgeliefert
Android-Gerät oder EmulatorZum Testen der App
Android Studio ist alles-in-einem: Im Gegensatz zur Desktop-Java-Entwicklung (wo man JDK, Maven und IDE separat installiert), enthält Android Studio alles: JDK, Android SDK, Gradle (Build-Tool), Emulator und IDE. Download unter developer.android.com/studio.
git clone https://github.com/CavdarKemal/SudokuAndroid.git
# Dann: Android Studio → "Open" → SudokuAndroid-Ordner auswählen

4. Bauen und Installieren

Option 1: Android Studio (empfohlen)

  1. Projekt in Android Studio öffnen
  2. Emulator starten (oder Android-Gerät per USB verbinden)
  3. Grünen „Run“-Button klicken (→)
  4. App installiert sich automatisch und startet

Option 2: Kommandozeile (Gradle)

# Debug-APK bauen:
./gradlew assembleDebug

# Die fertige APK befindet sich in:
# app/build/outputs/apk/debug/app-debug.apk

# APK auf verbundenem Gerät installieren:
adb install app/build/outputs/apk/debug/app-debug.apk
Was ist Gradle? Gradle ist das Build-Tool für Android (entspricht Maven in Java-Projekten). Es liest die build.gradle.kts-Dateien (Kotlin DSL) und baut daraus eine APK-Datei (Android Package). ./gradlew ist ein „Gradle Wrapper“ — er lädt automatisch die richtige Gradle-Version herunter. SudokuAndroid verwendet AGP 8.13.2 und Kotlin 1.9.22.

5. Android-App-Grundlagen

Wer mit Desktop-Java-Entwicklung vertraut ist, findet hier die wichtigsten Unterschiede:

KonzeptDesktop JavaAndroid (Kotlin)
HauptfensterJFrameActivity
BildschirmeJPanelComposable Screens
LayoutIm Code (Swing)Jetpack Compose (deklarativ im Code)
NavigationManuell (Fenster)Navigation Compose (NavGraph)
Build-ToolMavenGradle (Kotlin DSL)
PaketformatJARAPK
EingabeMaus + TastaturTouchscreen
DI-FrameworkHilt (Dagger)

Was ist eine Activity?

Eine Activity ist der Grundbaustein einer Android-App — vergleichbar mit einem JFrame-Fenster. SudokuAndroid hat eine MainActivity, die als Einstiegspunkt dient und Jetpack Compose für die UI verwendet:

// Activity mit Jetpack Compose (statt XML-Layouts):
class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SudokuTheme {
                // Navigation zwischen Screens
                NavGraph()
            }
        }
    }
}
Jetpack Compose statt XML: SudokuAndroid verwendet Jetpack Compose — Googles modernes UI-Framework. Statt XML-Layouts wird die UI direkt in Kotlin-Code als @Composable-Funktionen definiert. Das ist deklarativ, typsicher und benötigt kein findViewById.

6. Projektstruktur

SudokuAndroid/
├── app/                              ← Android-App-Modul
│   ├── src/main/
│   │   ├── java/com/sudoku/android/  ← Kotlin-Quellcode
│   │   │   ├── MainActivity.kt       ← Einstiegspunkt (Activity)
│   │   │   ├── SudokuApplication.kt  ← Hilt Application-Klasse
│   │   │   ├── di/
│   │   │   │   └── AppModule.kt      ← Dependency Injection (Hilt)
│   │   │   ├── navigation/
│   │   │   │   ├── NavGraph.kt       ← Compose-Navigation
│   │   │   │   └── Screen.kt         ← Screen-Definitionen
│   │   │   ├── viewmodel/
│   │   │   │   ├── Sudoku2DViewModel.kt  ← ViewModel für 2D
│   │   │   │   └── Sudoku3DViewModel.kt  ← ViewModel für 3D
│   │   │   └── ui/
│   │   │       ├── components/
│   │   │       │   └── NumberPad.kt      ← Zahlen-Pad Composable
│   │   │       ├── screens/
│   │   │       │   ├── home/HomeScreen.kt        ← Startbildschirm
│   │   │       │   ├── sudoku2d/Sudoku2DScreen.kt ← 2D-Spielfeld
│   │   │       │   └── sudoku3d/Sudoku3DScreen.kt ← 3D-Spielfeld
│   │   │       └── theme/            ← Material 3 Theme
│   │   ├── res/                      ← Ressourcen (Strings, Icons)
│   │   └── AndroidManifest.xml       ← App-Konfiguration
│   └── build.gradle.kts              ← App Build-Konfiguration
├── core/                             ← Reines Kotlin-Modul (ohne Android)
│   ├── src/main/kotlin/com/sudoku/core/
│   │   ├── generator/
│   │   │   ├── Generator2D.kt       ← 2D-Rätsel generieren
│   │   │   └── Generator3D.kt       ← 3D-Rätsel generieren
│   │   ├── solver/
│   │   │   ├── Solver2D.kt          ← 2D-Backtracking-Löser
│   │   │   └── Solver3D.kt          ← 3D-Backtracking-Löser
│   │   ├── model/
│   │   │   ├── Difficulty.kt        ← Schwierigkeitsstufen
│   │   │   ├── SudokuSize.kt        ← Würfelgrößen
│   │   │   └── SolveStep.kt         ← Einzelne Lösungsschritte
│   │   └── history/
│   │       └── UndoRedoManager.kt   ← Undo/Redo-Logik
│   └── build.gradle.kts
└── build.gradle.kts                  ← Globale Build-Konfiguration
Multi-Modul-Architektur: Das Projekt ist in zwei Module aufgeteilt: app (Android-UI mit Compose) und core (reine Kotlin-Logik ohne Android-Abhängigkeiten). Das core-Modul enthält Generator, Solver und Spielmodelle — diese könnten auch in einer Desktop-Anwendung wiederverwendet werden.

7. Das Spielfeld zeichnen

Das 9×9-Sudoku-Gitter wird als Composable mit Canvas implementiert — Jetpack Compose zeichnet das Spielfeld deklarativ:

// Sudoku-Spielfeld als Composable:
@Composable
fun SudokuBoard(
    board: Array<IntArray>,
    selectedCell: Pair<Int, Int>?,
    onCellClick: (Int, Int) -> Unit
) {
    Canvas(modifier = Modifier.fillMaxWidth().aspectRatio(1f)
        .pointerInput(Unit) {
            detectTapGestures { offset ->
                val row = (offset.y / cellSize).toInt()
                val col = (offset.x / cellSize).toInt()
                onCellClick(row, col)
            }
        }
    ) {
        drawGrid()       // Gitterlinien zeichnen
        drawNumbers()    // Zahlen einsetzen
        drawHighlight()  // Ausgewählte Zelle hervorheben
    }
}

// Gitterlinien mit dickeren 3x3-Block-Grenzen:
private fun DrawScope.drawGrid() {
    for (i in 0..9) {
        val strokeWidth = if (i % 3 == 0) 4f else 1f
        drawLine(Color.Black, Offset(0f, i * cellSize), Offset(size.width, i * cellSize), strokeWidth)
        drawLine(Color.Black, Offset(i * cellSize, 0f), Offset(i * cellSize, size.height), strokeWidth)
    }
}
Compose Canvas vs. Custom View: In Jetpack Compose wird Canvas als Composable verwendet statt einer Custom View mit onDraw(). Der Vorteil: Die UI reagiert automatisch auf State-Änderungen und wird neu gezeichnet (Recomposition), ohne manuelles invalidate().

8. Spiellogik

Die Spiellogik (Generator und Solver) liegt im separaten core-Modul und ist in reinem Kotlin geschrieben — ohne Android-Abhängigkeiten. Die Algorithmen sind vergleichbar mit den Desktop-Versionen (SudokuApp):

// Backtracking-Solver in Kotlin:
fun solve(board: Array<IntArray>): Boolean {
    val emptyCell = findEmptyCell(board) ?: return true  // ?: = Elvis-Operator

    val (row, col) = emptyCell  // Destructuring

    for (num in 1..9) {
        if (isValid(board, row, col, num)) {
            board[row][col] = num

            if (solve(board)) return true

            board[row][col] = 0  // Backtrack
        }
    }
    return false
}
Kotlin-Besonderheiten:
?: return true (Elvis-Operator): Falls findEmptyCell null zurückgibt, direkt return true — spart eine If-Anweisung
val (row, col) = emptyCell (Destructuring): Mehrere Werte gleichzeitig in Variablen entpacken
1..9: Range-Ausdruck — entspricht einer for-Schleife von 1 bis 9

9. Eingabe auf dem Touchscreen

Auf dem Smartphone gibt es keine Maus — die Eingabe erfolgt per Touch. In Compose wird die Touch-Erkennung deklarativ mit pointerInput realisiert:

// Touch-Erkennung direkt im Canvas-Modifier:
Canvas(modifier = Modifier
    .pointerInput(Unit) {
        detectTapGestures { offset ->
            // Welche Zelle wurde angetippt?
            val row = (offset.y / cellSize).toInt()
            val col = (offset.x / cellSize).toInt()
            if (row in 0..8 && col in 0..8) {
                viewModel.selectCell(row, col)
                // Kein invalidate() nötig — Compose erkennt State-Änderungen automatisch
            }
        }
    }
) { /* zeichnen ... */ }

Zahleneingabe

Nach der Zellauswahl gibt der Spieler die Zahl über ein NumberPad-Composable (1–9-Buttons) ein:

// NumberPad als eigenes Composable (ui/components/NumberPad.kt):
@Composable
fun NumberPad(onNumberClick: (Int) -> Unit, onDelete: () -> Unit) {
    Row(horizontalArrangement = Arrangement.SpaceEvenly) {
        for (num in 1..9) {
            Button(onClick = { onNumberClick(num) }) {
                Text("$num", fontSize = 20.sp)
            }
        }
    }
}

// Verwendung im Screen:
NumberPad(
    onNumberClick = { num -> viewModel.enterNumber(num) },
    onDelete = { viewModel.deleteNumber() }
)

10. Vergleich: Android vs. Desktop

AspektDesktop (Java Swing)Android (Kotlin)
SpracheJavaKotlin 1.9
UI-FrameworkSwing + JavaFXJetpack Compose + Material 3
LayoutIm Java-CodeDeklarativ in Kotlin (Composables)
ArchitekturMDI (MainFrame)MVVM (ViewModel + Compose)
DIHilt (Dagger)
BuildMavenGradle (Kotlin DSL)
DistributionJAR-DateiAPK → Google Play Store
EingabeMaus + TastaturTouchscreen
BildschirmgrößeGroß (beliebig skalierbar)Klein (DP-Einheiten statt Pixel)
LifecycleEinfach (open → close)Komplex (onCreate, onPause, onResume...)
Activity Lifecycle: Android-Apps können jederzeit durch Anrufe, andere Apps oder den Systemspeicher unterbrochen werden. Deshalb hat jede Activity einen Lebenszyklus mit Methoden wie onPause() (App kommt in Hintergrund), onResume() (App kommt wieder in Vordergrund) und onDestroy() (App wird beendet). Spielstand und Daten müssen entsprechend gespeichert/wiederhergestellt werden.

11. Nächste Schritte

Die App kennenlernen

  1. Android Studio installieren und Projekt öffnen
  2. Emulator starten und App laufen lassen
  3. Quellcode lesen — Einstiegspunkt ist MainActivity.kt

Weiterentwickeln

Links