Tugas Pertemuan 13: Membuat Aplikasi Unscramble
github: https://github.com/ilomimo/unscramble-ppb
referensi: https://github.com/google-developer-training/basic-android-kotlin-compose-training-unscramble/tree/starter
demo (klik tombol play)
Laporan Pengembangan Aplikasi Unscramble - Tugas 13
Pada tugas ke-13 ini, Pak Fajar memberikan tugas mengembangkan aplikasi unscramble berdasarkan kode baseline yang telah disediakan. Aplikasi baseline merupakan implementasi sederhana dari permainan menyusun kata yang diacak, dan kami melakukan pengembangan lebih lanjut dengan menambahkan berbagai fitur dan peningkatan fungsionalitas.
Perbandingan Baseline vs Enhanced Version
1. Perubahan Struktur Data
Baseline:
// Data state sederhana hanya melacak informasi dasar
val gameUiState by gameViewModel.uiState.collectAsState()
Enhanced Version:
data class GameUiState(
val currentScrambledWord: String = "",
val currentWordCount: Int = 1,
val score: Int = 0,
val isGuessedWordWrong: Boolean = false,
val isGameOver: Boolean = false,
val difficulty: Difficulty = Difficulty.EASY,
val timeRemaining: Int = 60,
val streak: Int = 0,
val bestStreak: Int = 0,
val hintsRemaining: Int = 3,
val currentHint: String = "",
val showHint: Boolean = false,
val category: WordCategory = WordCategory.GENERAL,
val bonusPoints: Int = 0
)
Pada versi yang dikembangkan, struktur data GameUiState diperluas secara signifikan untuk mendukung fitur-fitur baru seperti sistem kesulitan, timer, hint, dan kategori kata. Baseline hanya melacak informasi dasar seperti skor, jumlah kata, dan status permainan, sedangkan enhanced version menambahkan tracking untuk streak, waktu tersisa, hint yang tersedia, dan bonus poin.
2. Implementasi Sistem Difficulty dan Category
Baseline: Tidak memiliki sistem tingkat kesulitan atau kategorisasi kata.
Enhanced Version:
enum class Difficulty(val displayName: String, val timeLimit: Int, val pointMultiplier: Double, val maxWords: Int) {
EASY("Mudah", 60, 1.0, 8),
MEDIUM("Sedang", 45, 1.5, 10),
HARD("Sulit", 30, 2.0, 12)
}
enum class WordCategory(val displayName: String, val emoji: String) {
GENERAL("Umum", "📝"),
ANIMALS("Hewan", "🐾"),
FOOD("Makanan", "🍕"),
TECHNOLOGY("Teknologi", "💻"),
NATURE("Alam", "🌿")
}
Sistem difficulty memberikan variasi tantangan dengan batasan waktu yang berbeda, multiplier poin, dan jumlah kata maksimal. Setiap tingkat kesulitan memiliki karakteristik yang berbeda untuk meningkatkan engagement pemain. Sistem category memungkinkan pemain memilih tema kata yang diinginkan, memberikan variasi dan konteks yang lebih spesifik pada permainan.
3. Implementasi Timer System
Baseline: Tidak memiliki batasan waktu dalam permainan.
Enhanced Version:
private var gameTimer: CountDownTimer? = null
fun startTimer() {
gameTimer?.cancel()
gameTimer = object : CountDownTimer(
_uiState.value.timeRemaining * 1000L,
1000
) {
override fun onTick(millisUntilFinished: Long) {
_uiState.update { currentState ->
currentState.copy(timeRemaining = (millisUntilFinished / 1000).toInt())
}
}
override fun onFinish() {
skipWord()
}
}.start()
}
Timer system menambahkan elemen time pressure yang membuat permainan lebih menantang. CountDownTimer memberikan feedback real-time kepada pemain tentang waktu yang tersisa, dan secara otomatis melakukan skip kata ketika waktu habis. Implementasi ini juga mempertimbangkan lifecycle management dengan membatalkan timer yang ada sebelum membuat yang baru.
4. Peningkatan Sistem Scoring
Baseline:
_uiState.update { currentState ->
currentState.copy(score = currentState.score + SCORE_INCREASE)
}
Enhanced Version:
val basePoints = currentWord.length * 10
val streakBonus = _uiState.value.streak * 5
val timeBonus = _uiState.value.timeRemaining * 2
val difficultyBonus = (basePoints * _uiState.value.difficulty.pointMultiplier).toInt()
val totalBonus = streakBonus + timeBonus + difficultyBonus - basePoints
val totalPoints = basePoints + totalBonus
Sistem scoring yang dikembangkan menggunakan formula yang lebih kompleks yang mempertimbangkan multiple factors. Base points dihitung berdasarkan panjang kata, memberikan insentif untuk kata yang lebih panjang. Streak bonus mendorong konsistensi, time bonus memberikan reward untuk kecepatan, dan difficulty bonus memberikan kompensasi untuk tingkat kesulitan yang lebih tinggi.
5. Sistem Hint
Baseline: Tidak menyediakan sistem bantuan untuk pemain.
Enhanced Version:
fun useHint() {
if (_uiState.value.hintsRemaining > 0) {
val hint = generateHint()
_uiState.update { currentState ->
currentState.copy(
hintsRemaining = currentState.hintsRemaining - 1,
currentHint = hint,
showHint = true
)
}
}
}
private fun generateHint(): String {
return when (_uiState.value.hintsRemaining) {
3 -> "Huruf pertama: ${currentWord.first()}"
2 -> "Huruf terakhir: ${currentWord.last()}"
1 -> "Panjang kata: ${currentWord.length} huruf"
else -> ""
}
}
Sistem hint memberikan tiga level bantuan yang berbeda, dengan informasi yang semakin spesifik seiring berkurangnya hint yang tersedia. Implementasi ini menggunakan resource management dimana setiap hint yang digunakan akan mengurangi jumlah hint yang tersisa, mendorong pemain untuk menggunakan hint secara strategis.
6. Redesign User Interface
Baseline:
Text(text = stringResource(R.string.app_name), style = typography.titleLarge)
GameStatus(score = gameUiState.score, modifier = Modifier.padding(20.dp))
Enhanced Version:
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Unscramble Game",
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
Button(
onClick = { showSettings = !showSettings },
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondary
)
) {
Text("Settings")
}
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
StatCard(title = "Skor", value = gameUiState.score.toString(), icon = "🏆")
StatCard(title = "Streak", value = "${gameUiState.streak}/${gameUiState.bestStreak}", icon = "🔥")
StatCard(title = "Kata", value = "${gameUiState.currentWordCount}/${gameUiState.difficulty.maxWords}", icon = "📝")
}
Interface yang dikembangkan menggunakan card-based design dengan informasi yang lebih komprehensif. Header section menampilkan title dan tombol settings, sementara statistics section menggunakan row of cards untuk menampilkan berbagai metrik permainan secara visual. Pendekatan ini memberikan information hierarchy yang lebih baik dan visual feedback yang lebih kaya.
7. Panel Pengaturan
Baseline: Tidak memiliki opsi pengaturan yang dapat diakses pemain.
Enhanced Version:
@Composable
fun SettingsPanel(
currentDifficulty: Difficulty,
currentCategory: WordCategory,
onDifficultyChanged: (Difficulty) -> Unit,
onCategoryChanged: (WordCategory) -> Unit,
onClose: () -> Unit
) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant)
) {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text("Tingkat Kesulitan:", fontWeight = FontWeight.Medium)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Difficulty.values().forEach { difficulty ->
FilterChip(
selected = currentDifficulty == difficulty,
onClick = { onDifficultyChanged(difficulty) },
label = { Text(difficulty.displayName) }
)
}
}
Text("Kategori:", fontWeight = FontWeight.Medium)
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
items(WordCategory.values().size) { index ->
val category = WordCategory.values()[index]
FilterChip(
selected = currentCategory == category,
onClick = { onCategoryChanged(category) },
label = { Text("${category.emoji} ${category.displayName}") }
)
}
}
}
}
}
Settings panel memberikan interface yang user-friendly untuk mengubah konfigurasi permainan secara real-time. Penggunaan FilterChip memberikan visual feedback yang jelas tentang pilihan yang aktif, sementara LazyRow memungkinkan scrolling horizontal untuk kategori yang banyak tanpa mengambil terlalu banyak vertical space.
8. Enhanced Game Layout
Baseline:
Card(modifier = modifier, elevation = CardDefaults.cardElevation(defaultElevation = 5.dp)) {
Text(text = currentScrambledWord, style = typography.displayMedium)
OutlinedTextField(...)
}
Enhanced Version:
Card(...) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Surface(color = MaterialTheme.colorScheme.primary, shape = RoundedCornerShape(12.dp)) {
Text(
text = "${category.emoji} ${category.displayName}",
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.labelMedium
)
}
Surface(color = MaterialTheme.colorScheme.secondary, shape = RoundedCornerShape(12.dp)) {
Text(
text = difficulty.displayName,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
color = MaterialTheme.colorScheme.onSecondary,
style = MaterialTheme.typography.labelMedium
)
}
}
Text(
text = currentScrambledWord,
style = MaterialTheme.typography.displayMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
}
Game layout yang dikembangkan menambahkan contextual information melalui badge system yang menampilkan kategori dan tingkat kesulitan yang sedang aktif. Scrambled word ditampilkan dengan styling yang lebih prominent menggunakan bold font weight dan primary color untuk meningkatkan readability dan focus.
9. Organized Word Management
Baseline:
private val allWords: Set<String> = setOf("animal", "auto", "anagram", ...)
Enhanced Version:
private val wordLists = mapOf(
WordCategory.GENERAL to mapOf(
Difficulty.EASY to listOf("KUCING", "ANJING", "RUMAH", "MOBIL"),
Difficulty.MEDIUM to listOf("KOMPUTER", "SEKOLAH", "KELUARGA"),
Difficulty.HARD to listOf("PENDIDIKAN", "TEKNOLOGI", "PERPUSTAKAAN")
),
WordCategory.ANIMALS to mapOf(
Difficulty.EASY to listOf("SAPI", "KAMBING", "AYAM"),
Difficulty.MEDIUM to listOf("GAJAH", "HARIMAU", "JERAPAH"),
Difficulty.HARD to listOf("ORANGUTAN", "KOMODO", "BADAK")
)
)
Sistem manajemen kata yang dikembangkan menggunakan nested map structure yang memungkinkan pengorganisasian kata berdasarkan kategori dan tingkat kesulitan. Struktur ini memudahkan seleksi kata yang appropriate berdasarkan pengaturan permainan yang sedang aktif, dan memungkinkan ekspansi yang mudah untuk kategori atau tingkat kesulitan baru.
10. Bug Fix dan Security Improvement
Baseline:
val activity = (LocalContext.current as Activity)
onClick = { activity.finish() }
Enhanced Version:
val activity = LocalActivity.current
onClick = { activity?.finish() }
Perbaikan ini mengatasi potential crash yang bisa terjadi karena unsafe casting. Penggunaan LocalActivity.current dengan safe call operator memastikan aplikasi tidak crash ketika activity context tidak tersedia, meningkatkan reliability dan user experience secara keseluruhan.
Fitur-Fitur Baru yang Diimplementasikan
Berdasarkan pengembangan dari baseline, telah ditambahkan delapan fitur utama, yaitu Timer System dengan countdown dan progress visualization, Difficulty Levels dengan tiga tingkatan yang mempengaruhi waktu dan scoring, Word Categories untuk variasi tema permainan, Hint System dengan tiga tipe bantuan berbeda, Streak System untuk bonus konsistensi, Settings Panel untuk kustomisasi real-time, Enhanced UI Design dengan card-based layout dan visual feedback, serta Advanced Scoring yang mempertimbangkan multiple factors dalam perhitungan poin.
Comments
Post a Comment