feat: UNO Kotlin Multiplatform app (Android, Desktop, Web)
- Full UNO game logic with AI opponents - Compose Multiplatform UI with animations - Shared module (commonMain) with game engine - Desktop app (Compose Desktop) - Android app (Compose Multiplatform) - Web app (Kotlin/Wasm) - Nix shell for reproducible dev environment
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:label="UNO"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Material.Light.NoActionBar">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|mnc|colorMode|density|fontScale|fontWeightAdjustment|keyboard|layoutDirection|locale|mcc|navigation|smallestScreenSize|touchscreen|uiMode">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.uno.android
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.uno.game.GameEngine
|
||||
import com.uno.game.model.*
|
||||
import com.uno.shared.UnoGameScreen
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||
UnoApp()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UnoApp() {
|
||||
val engine = remember { GameEngine() }
|
||||
var gameState by remember { mutableStateOf(engine.startGame()) }
|
||||
var gameStarted by remember { mutableStateOf(false) }
|
||||
|
||||
if (!gameStarted) {
|
||||
StartScreen(onStartGame = { playerCount ->
|
||||
gameState = engine.startGame(playerCount)
|
||||
gameStarted = true
|
||||
})
|
||||
} else {
|
||||
UnoGameScreen(
|
||||
state = gameState,
|
||||
onPlayCard = { card, color ->
|
||||
gameState = engine.playCard(gameState, 0, card, color)
|
||||
gameState = runAiTurns(engine, gameState)
|
||||
},
|
||||
onDrawCard = {
|
||||
gameState = engine.drawCard(gameState, 0)
|
||||
gameState = runAiTurns(engine, gameState)
|
||||
},
|
||||
onCallUno = { gameState = engine.callUno(gameState, 0) }
|
||||
)
|
||||
|
||||
if (gameState.isFinished) {
|
||||
GameOverScreen(winner = gameState.winner, onPlayAgain = { gameState = engine.startGame() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun runAiTurns(engine: GameEngine, state: GameState): GameState {
|
||||
var currentState = state
|
||||
while (currentState.currentPlayerIndex != 0 && !currentState.isFinished) {
|
||||
currentState = engine.aiPlay(currentState, currentState.currentPlayerIndex)
|
||||
}
|
||||
return currentState
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StartScreen(onStartGame: (Int) -> Unit) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text("UNO", style = MaterialTheme.typography.displayLarge, color = MaterialTheme.colorScheme.primary)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text("Kotlin Multiplatform", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Button(onClick = { onStartGame(4) }) { Text("Spiel starten (4 Spieler)") }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GameOverScreen(winner: Player?, onPlayAgain: () -> Unit) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onPlayAgain,
|
||||
title = { Text("Spiel vorbei!") },
|
||||
text = { Text(if (winner != null) "${winner.name} hat gewonnen!" else "Unentschieden!") },
|
||||
confirmButton = {
|
||||
Button(onClick = onPlayAgain) { Text("Nochmal spielen") }
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user