Aplikasi Unscramble

Pertemuan 13

Nama: Rayhan Almer Kusumah

NRP: 5025211115

Kelas: Pemrograman Perangkat Bergerak (A)

Tahun: 2025

Aplikasi Unscramble

Dokumentasi

Source Code

package com.example.unscramble.ui

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.unscramble.data.MAX_NO_OF_WORDS
import com.example.unscramble.data.SCORE_INCREASE
import com.example.unscramble.data.allWords
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update

/**
* ViewModel containing the app data and methods to process the data
*/
class GameViewModel : ViewModel() {

// Game UI state
private val _uiState = MutableStateFlow(GameUiState())
val uiState: StateFlow<GameUiState> = _uiState.asStateFlow()

var userGuess by mutableStateOf("")
private set

// Set of words used in the game
private var usedWords: MutableSet<String> = mutableSetOf()
private lateinit var currentWord: String

init {
resetGame()
}

/*
* Re-initializes the game data to restart the game.
*/
fun resetGame() {
usedWords.clear()
_uiState.value = GameUiState(currentScrambledWord = pickRandomWordAndShuffle())
}

/*
* Update the user's guess
*/
fun updateUserGuess(guessedWord: String){
userGuess = guessedWord
}

/*
* Checks if the user's guess is correct.
* Increases the score accordingly.
*/
fun checkUserGuess() {
if (userGuess.equals(currentWord, ignoreCase = true)) {
// User's guess is correct, increase the score
// and call updateGameState() to prepare the game for next round
val updatedScore = _uiState.value.score.plus(SCORE_INCREASE)
updateGameState(updatedScore)
} else {
// User's guess is wrong, show an error
_uiState.update { currentState ->
currentState.copy(isGuessedWordWrong = true)
}
}
// Reset user guess
updateUserGuess("")
}

/*
* Skip to next word
*/
fun skipWord() {
updateGameState(_uiState.value.score)
// Reset user guess
updateUserGuess("")
}

/*
* Picks a new currentWord and currentScrambledWord and updates UiState according to
* current game state.
*/
private fun updateGameState(updatedScore: Int) {
if (usedWords.size == MAX_NO_OF_WORDS){
//Last round in the game, update isGameOver to true, don't pick a new word
_uiState.update { currentState ->
currentState.copy(
isGuessedWordWrong = false,
score = updatedScore,
isGameOver = true
)
}
} else{
// Normal round in the game
_uiState.update { currentState ->
currentState.copy(
isGuessedWordWrong = false,
currentScrambledWord = pickRandomWordAndShuffle(),
currentWordCount = currentState.currentWordCount.inc(),
score = updatedScore
)
}
}
}

private fun shuffleCurrentWord(word: String): String {
val tempWord = word.toCharArray()
// Scramble the word
tempWord.shuffle()
while (String(tempWord) == word) {
tempWord.shuffle()
}
return String(tempWord)
}

private fun pickRandomWordAndShuffle(): String {
// Continue picking up a new random word until you get one that hasn't been used before
currentWord = allWords.random()
return if (usedWords.contains(currentWord)) {
pickRandomWordAndShuffle()
} else {
usedWords.add(currentWord)
shuffleCurrentWord(currentWord)
}
}
}

Struktur ViewModel dan State Game

Kelas GameViewModel merupakan turunan dari ViewModel milik Android Jetpack. Di dalamnya terdapat state utama bernama _uiState, yang bertipe MutableStateFlow. Ini adalah state internal yang dapat diperbarui dari dalam ViewModel, dan dibungkus dengan properti publik uiState menggunakan asStateFlow() agar hanya dapat dibaca dari luar. State ini mencerminkan seluruh status permainan, seperti skor, kata saat ini, jumlah kata yang telah ditebak, dan apakah permainan sudah selesai.

Selain itu, terdapat properti userGuess bertipe mutableStateOf, yang menyimpan tebakan terbaru dari pemain. Kata kunci by memungkinkan properti ini digunakan secara langsung, namun dengan kontrol akses private set, sehingga hanya GameViewModel yang dapat mengubah nilainya.

Validasi Jawaban Pemain

Pemain dapat mengirimkan tebakan mereka melalui UI, dan ViewModel akan menerima input tersebut menggunakan fungsi updateUserGuess(). Saat pemain menekan tombol "Submit", fungsi checkUserGuess() dipanggil. Fungsi ini akan memeriksa apakah tebakan pemain sama dengan kata asli yang belum diacak (currentWord), tanpa memperhatikan huruf kapital.

Jika tebakan benar, skor pemain akan ditingkatkan menggunakan konstanta SCORE_INCREASE, dan permainan dilanjutkan ke kata berikutnya melalui updateGameState(). Jika tebakan salah, UI akan diberi tahu bahwa tebakan salah, dengan memperbarui isGuessedWordWrong menjadi true di dalam uiState. Setelah itu, tebakan pemain direset agar UI siap menerima input baru.

Pengelolaan State Permainan

Fungsi updateGameState() adalah jantung dari alur permainan. Di dalamnya, sistem akan mengecek apakah jumlah kata yang sudah digunakan telah mencapai batas maksimal (MAX_NO_OF_WORDS). Jika ya, maka permainan dinyatakan selesai dengan menyetel isGameOver menjadi true. Jika belum, maka kata acak baru akan dipilih dan currentWordCount ditambah satu. Skor juga diperbarui jika jawaban sebelumnya benar.

Proses Pemilihan dan Pengacakan Kata

Dua fungsi bantu, shuffleCurrentWord() dan pickRandomWordAndShuffle(), bertanggung jawab untuk memilih dan mengacak kata. Fungsi shuffleCurrentWord() mengacak huruf-huruf dalam kata hingga susunannya berbeda dari kata asli, memastikan tidak ada kata yang ditampilkan dalam bentuk aslinya. Fungsi pickRandomWordAndShuffle() akan terus memilih kata secara acak dari daftar allWords hingga menemukan satu yang belum pernah dipakai, sehingga tidak ada kata yang muncul dua kali dalam satu sesi permainan.

Kesimpulan

GameViewModel adalah komponen inti yang mengatur seluruh logika permainan Unscramble. Dengan menggunakan ViewModel, StateFlow, dan mutableStateOf, seluruh data permainan dikelola dengan aman dan efisien, sekaligus terintegrasi secara reaktif dengan UI Compose. Pendekatan ini tidak hanya menjaga kestabilan data saat rotasi layar atau navigasi, tetapi juga memastikan arsitektur aplikasi tetap bersih dan terpisah antara logika dan tampilan. Kode ini merupakan contoh yang ideal untuk belajar bagaimana mengelola state dan interaksi dalam aplikasi Compose berbasis arsitektur modern Android.

Comments

Popular posts from this blog

Aplikasi Dice Roller

Aplikasi Kalkulator

Aplikasi Hello Android