update competition
This commit is contained in:
parent
98474010d0
commit
b354d2c24b
|
@ -33,7 +33,7 @@ class SharedPrefHelper(context: Context) {
|
|||
fun saveMaterialList(filterMaterial: String, materialList: List<MaterialDataModel>) {
|
||||
val editor = sharedPreferences.edit()
|
||||
val json = Gson().toJson(materialList)
|
||||
editor.putString("material_list_$filterMaterial", json)
|
||||
editor.putString(" $filterMaterial", json)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,28 @@ class SharedPrefHelper(context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getCombinedMaterialList(): List<MaterialDataModel> {
|
||||
val categories = listOf("animal", "limb", "family", "house")
|
||||
val combinedList = mutableListOf<MaterialDataModel>()
|
||||
|
||||
for (category in categories) {
|
||||
getMaterialList(category)?.let { combinedList.addAll(it) }
|
||||
}
|
||||
|
||||
return combinedList
|
||||
}
|
||||
|
||||
fun getRandomMaterials(nData: Int): List<MaterialDataModel> {
|
||||
val combinedList = getCombinedMaterialList()
|
||||
|
||||
return if (combinedList.size <= nData) {
|
||||
combinedList // Jika jumlah data kurang dari nData, kembalikan semua
|
||||
} else {
|
||||
combinedList.shuffled().take(nData) // Acak dan ambil sejumlah nData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun clearMaterialList(filterMaterial: String) {
|
||||
sharedPreferences.edit().remove("material_list_$filterMaterial").apply()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package com.example.lexilearn.data.model
|
||||
|
||||
sealed class QuizState {
|
||||
data class QuestionLoaded(val question: MaterialDataModel, val totalScore: Int) : QuizState()
|
||||
data class AnswerCorrect(val score: Int, val totalScore: Int) : QuizState()
|
||||
data class AnswerWrong(val wrongAttempts: Int) : QuizState()
|
||||
data class QuizFinished(val totalScore: Int) : QuizState()
|
||||
data class Error(val message: String) : QuizState()
|
||||
}
|
|
@ -36,6 +36,44 @@ class FirebaseHelper(private val context: Context) {
|
|||
})
|
||||
}
|
||||
|
||||
fun fetchAllMaterials(callback: (List<MaterialDataModel>) -> Unit) {
|
||||
val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("data")
|
||||
|
||||
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
|
||||
override fun onDataChange(snapshot: DataSnapshot) {
|
||||
val allMaterials = mutableListOf<MaterialDataModel>()
|
||||
|
||||
for (categorySnapshot in snapshot.children) { // Loop setiap kategori (animal, limb, dll.)
|
||||
val categoryKey = categorySnapshot.key ?: continue
|
||||
val materialList = mutableListOf<MaterialDataModel>()
|
||||
|
||||
for (materialSnapshot in categorySnapshot.children) { // Loop setiap item dalam kategori
|
||||
val material = materialSnapshot.getValue(MaterialDataModel::class.java)
|
||||
material?.let {
|
||||
allMaterials.add(it) // Tambahkan ke list utama
|
||||
materialList.add(it) // Tambahkan ke list per kategori
|
||||
}
|
||||
}
|
||||
|
||||
// Simpan berdasarkan kategori (misalnya "animal", "limb", dll.)
|
||||
sharedPrefHelper.saveMaterialList(categoryKey, materialList)
|
||||
}
|
||||
|
||||
// Simpan semua data ke SharedPreferences dengan key "all"
|
||||
sharedPrefHelper.saveMaterialList("all", allMaterials)
|
||||
|
||||
// Callback dengan seluruh data sebagai satu List<MaterialDataModel>
|
||||
callback(allMaterials)
|
||||
}
|
||||
|
||||
override fun onCancelled(error: DatabaseError) {
|
||||
Log.e("FirebaseHelper", "Error fetching all materials: ${error.message}")
|
||||
callback(emptyList()) // Jika terjadi error, kembalikan list kosong
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// ✅ 🔥 Fungsi Login - Mencari user berdasarkan email dan password
|
||||
fun loginUser(email: String, password: String, callback: (Boolean, String?) -> Unit) {
|
||||
val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("users")
|
||||
|
|
|
@ -27,6 +27,22 @@ class MaterialRepository(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getAllMaterialData(filterMaterial: String, callback: (List<MaterialDataModel>) -> Unit) {
|
||||
val cachedData =
|
||||
sharedPrefHelper.getMaterialList(filterMaterial) // Ambil data dengan filter
|
||||
if (cachedData != null) {
|
||||
callback(cachedData)
|
||||
} else {
|
||||
firebaseHelper.fetchAllMaterials { materials ->
|
||||
sharedPrefHelper.saveMaterialList(
|
||||
filterMaterial,
|
||||
materials
|
||||
)
|
||||
callback(materials)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createUser(user: UserModel, callback: (Boolean, String?) -> Unit) {
|
||||
val databaseReference = FirebaseDatabase.getInstance().getReference("users")
|
||||
|
||||
|
|
|
@ -13,7 +13,11 @@ class QuizRepository {
|
|||
/**
|
||||
* Fungsi ini akan membuat soal dengan mode soal & jawaban yang DITENTUKAN oleh parameter.
|
||||
*/
|
||||
fun generateQuestion(material: MaterialDataModel, questionMode: Int, answerMode: Int): QuizQuestion {
|
||||
fun generateQuestion(
|
||||
material: MaterialDataModel,
|
||||
questionMode: Int,
|
||||
answerMode: Int
|
||||
): QuizQuestion {
|
||||
val questionType = when (questionMode) {
|
||||
1 -> listOf(QuestionType.TEXT, QuestionType.IMAGE).random() // Text atau Image
|
||||
2 -> QuestionType.AUDIO // Hanya Audio
|
||||
|
@ -47,8 +51,23 @@ class QuizRepository {
|
|||
*/
|
||||
fun generateRandomQuestion(material: MaterialDataModel): QuizQuestion {
|
||||
val randomQuestionMode = Random.nextInt(1, 3) // Acak antara 1 (Text/Image) atau 2 (Audio)
|
||||
val randomAnswerMode = Random.nextInt(1, 3) // Acak antara 1 (FULL_WORD) atau 2 (SHUFFLED_LETTERS)
|
||||
val randomAnswerMode =
|
||||
Random.nextInt(1, 3) // Acak antara 1 (FULL_WORD) atau 2 (SHUFFLED_LETTERS)
|
||||
|
||||
return generateQuestion(material, randomQuestionMode, randomAnswerMode)
|
||||
}
|
||||
|
||||
fun getRandomMaterials(
|
||||
nData: Int,
|
||||
materials: List<MaterialDataModel>,
|
||||
callback: (List<MaterialDataModel>) -> Unit
|
||||
) {
|
||||
if (materials.isNotEmpty()) {
|
||||
val randomMaterials = materials.shuffled().take(nData)
|
||||
callback(randomMaterials)
|
||||
} else {
|
||||
callback(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,15 +3,21 @@ package com.example.lexilearn.ui.views.pHome
|
|||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
|
@ -35,6 +41,7 @@ import androidx.navigation.NavController
|
|||
import com.example.lexilearn.R
|
||||
import com.example.lexilearn.ui.components.AutoSizeText
|
||||
import com.example.lexilearn.ui.components.ButtonHome
|
||||
import com.example.lexilearn.ui.theme.cAccent
|
||||
import com.example.lexilearn.ui.theme.cGray
|
||||
import com.example.lexilearn.ui.theme.cTextPrimary
|
||||
import com.example.lexilearn.ui.theme.cprimary
|
||||
|
@ -48,6 +55,7 @@ import com.example.lexilearn.ui.theme.cwhite
|
|||
fun HomeScreen(navController: NavController) {
|
||||
val viewModel: HomeViewModel = viewModel()
|
||||
val scrollState = rememberScrollState()
|
||||
val leaderBoard = listOf("Tono (987)", "Budi (965)")
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
@ -116,24 +124,80 @@ fun HomeScreen(navController: NavController) {
|
|||
)
|
||||
)
|
||||
) {
|
||||
Column(Modifier.padding(14.dp)) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(14.dp)
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = "Materi Terselesaikan",
|
||||
text = "Leaderboard hari ini :",
|
||||
color = ctextWhite,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.bg_children),
|
||||
contentDescription = "image",
|
||||
modifier = Modifier.size(80.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
LazyColumn {
|
||||
itemsIndexed(leaderBoard) { index, player ->
|
||||
Text(
|
||||
text = "${index + 1}. $player",
|
||||
color = cAccent,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = cprimary,
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.width(160.dp)
|
||||
.clickable {
|
||||
viewModel.prepareCompetitionQuiz {dataMaterial->
|
||||
navController.currentBackStackEntry
|
||||
?.savedStateHandle
|
||||
?.set("selectedMaterialList", dataMaterial)
|
||||
navController.currentBackStackEntry
|
||||
?.savedStateHandle
|
||||
?.set("typeQuiz", "competition")
|
||||
navController.currentBackStackEntry
|
||||
?.savedStateHandle
|
||||
?.set("indexValue", 0)
|
||||
navController.navigate("quizScreen")
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Competition",
|
||||
color = cAccent,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.trophy),
|
||||
contentDescription = "image",
|
||||
modifier = Modifier
|
||||
.size(60.dp)
|
||||
.padding(4.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Tantang dirimu dan raih peringkat tertinggi di leaderboard hari ini!",
|
||||
color = ctextWhite,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 9.sp
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Column(
|
||||
|
|
|
@ -1,6 +1,25 @@
|
|||
package com.example.lexilearn.ui.views.pHome
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.lexilearn.data.model.MaterialDataModel
|
||||
import com.example.lexilearn.data.model.QuizState
|
||||
import com.example.lexilearn.data.repository.MaterialRepository
|
||||
import com.example.lexilearn.data.repository.QuizRepository
|
||||
|
||||
class HomeViewModel: ViewModel() {
|
||||
class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||
private val _materialRepository = MaterialRepository(application)
|
||||
private val _quizRepository = QuizRepository()
|
||||
|
||||
fun prepareCompetitionQuiz(callback: (List<MaterialDataModel>) -> Unit) {
|
||||
_materialRepository.getAllMaterialData("all") { materials ->
|
||||
_quizRepository.getRandomMaterials(10, materials) { randomData ->
|
||||
randomData.forEach { println(it) }
|
||||
callback(randomData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -96,6 +96,7 @@ fun QuizScreen(
|
|||
val minSize = 70.dp
|
||||
val dataQuiz by quizViewModel.questionShuffled.collectAsState()
|
||||
val listAnswer by quizViewModel.shuffledAnswerLetters.collectAsState()
|
||||
val score by quizViewModel.totalScore.observeAsState(0)
|
||||
|
||||
val quizXOffset = remember {
|
||||
mutableStateMapOf<Int, Float>()
|
||||
|
@ -138,21 +139,23 @@ fun QuizScreen(
|
|||
|
||||
LaunchedEffect(Unit) {
|
||||
quizViewModel.initMaterialData(materials)
|
||||
quizViewModel.startQuiz(materials)
|
||||
}
|
||||
|
||||
LaunchedEffect(indexQuiz) {
|
||||
println("indexQuizValue = $indexQuiz")
|
||||
if (indexQuiz < materials.size) {
|
||||
clearOffsets()
|
||||
quizViewModel.randomQuestion()
|
||||
} else {
|
||||
navController.getBackStackEntry("navMaterial/$typeQuiz")
|
||||
.savedStateHandle
|
||||
.set("numberUnlock", indexValue + 1)
|
||||
println("printUpdateUnlock-quiz $indexValue")
|
||||
// Kemudian kembali ke halaman sebelumnya
|
||||
navController.popBackStack()
|
||||
navController.popBackStack()
|
||||
if (typeQuiz != "competition") {
|
||||
navController.getBackStackEntry("navMaterial/$typeQuiz")
|
||||
.savedStateHandle
|
||||
.set("numberUnlock", indexValue + 1)
|
||||
println("printUpdateUnlock-quiz $indexValue")
|
||||
// Kemudian kembali ke halaman sebelumnya
|
||||
navController.popBackStack()
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(snackbarMessage) {
|
||||
|
@ -171,7 +174,7 @@ fun QuizScreen(
|
|||
) {
|
||||
GradientQuiz(
|
||||
navController = navController,
|
||||
headerText = stringResource(id = R.string.spelltitle),
|
||||
headerText = "${typeQuiz}-(${score})",
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
ConstraintLayout {
|
||||
|
|
|
@ -11,6 +11,7 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.lexilearn.data.model.MaterialDataModel
|
||||
import com.example.lexilearn.data.model.QuizQuestion
|
||||
import com.example.lexilearn.data.model.QuizState
|
||||
import com.example.lexilearn.data.repository.QuizRepository
|
||||
import com.example.lexilearn.domain.models.ModelAnswerRead
|
||||
import com.example.lexilearn.domain.models.ModelSpell
|
||||
|
@ -19,12 +20,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Locale
|
||||
import kotlin.random.Random
|
||||
|
||||
class QuizViewModel(application: Application) : AndroidViewModel(application),
|
||||
TextToSpeech.OnInitListener {
|
||||
private val quizRepository = QuizRepository()
|
||||
|
||||
|
||||
private var materialList: List<MaterialDataModel> = emptyList()
|
||||
|
||||
private val _currentQuestion = MutableStateFlow<QuizQuestion?>(null)
|
||||
|
@ -78,53 +79,35 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
|
|||
materialList = material;
|
||||
}
|
||||
|
||||
// fun randomQuestion() {
|
||||
// _currentQuestion.value = quizRepository.generateQuestion(materialList[_indexQuiz.value], 2, 2)
|
||||
// val dataQuest =
|
||||
// _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
||||
// ModelSpell(index + 1, true, data = "coba $char", showCard = false)
|
||||
// } ?: emptyList()
|
||||
// _questionShuffled.value = dataQuest
|
||||
//
|
||||
// val listAnswer =
|
||||
// _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
||||
// ModelAnswerRead(index + 1, char.toString())
|
||||
// } ?: emptyList()
|
||||
// val shuflledAnswer = listAnswer.shuffled()
|
||||
// _shuffledAnswerLetters.value = shuflledAnswer
|
||||
// println("testlistanswer ${_shuffledAnswerLetters.value}")
|
||||
// }
|
||||
// Di dalam fungsi randomQuestion() pada QuizViewModel
|
||||
fun randomQuestion() {
|
||||
_currentQuestion.value = quizRepository.generateQuestion(materialList[_indexQuiz.value], 2, 2)
|
||||
|
||||
// Generate unique IDs berdasarkan indexQuiz dan indeks karakter
|
||||
val dataQuest = _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
||||
ModelSpell(
|
||||
id = (_indexQuiz.value * 100) + index + 1, // ID unik per pertanyaan
|
||||
type = true,
|
||||
data = "?",
|
||||
showCard = false
|
||||
)
|
||||
} ?: emptyList()
|
||||
_questionShuffled.value = dataQuest
|
||||
fun randomQuestion() {
|
||||
val randomQuestionMode = Random.nextInt(1, 3)
|
||||
|
||||
val listAnswer = _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
||||
ModelAnswerRead(
|
||||
id = (_indexQuiz.value * 100) + index + 1, // ID unik per pertanyaan
|
||||
data = char.toString()
|
||||
)
|
||||
} ?: emptyList()
|
||||
_shuffledAnswerLetters.value = listAnswer.shuffled()
|
||||
}
|
||||
_currentQuestion.value =
|
||||
quizRepository.generateQuestion(materialList[_indexQuiz.value], randomQuestionMode, 2)
|
||||
|
||||
val dataQuest =
|
||||
_currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
||||
ModelSpell(
|
||||
id = (_indexQuiz.value * 100) + index + 1,
|
||||
type = true,
|
||||
data = "?",
|
||||
showCard = false
|
||||
)
|
||||
} ?: emptyList()
|
||||
_questionShuffled.value = dataQuest
|
||||
|
||||
fun submitAnswer(answer: String) {
|
||||
_userAnswer.value = answer
|
||||
_isAnswerCorrect.value =
|
||||
_currentQuestion.value?.correctAnswer?.correctWord?.equals(answer, ignoreCase = true)
|
||||
val listAnswer =
|
||||
_currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
||||
ModelAnswerRead(
|
||||
id = (_indexQuiz.value * 100) + index + 1, // ID unik per pertanyaan
|
||||
data = char.toString()
|
||||
)
|
||||
} ?: emptyList()
|
||||
_shuffledAnswerLetters.value = listAnswer.shuffled()
|
||||
}
|
||||
|
||||
|
||||
private var tts: TextToSpeech? = null
|
||||
private val _isTTSInitialized = MutableLiveData(false)
|
||||
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
|
||||
|
@ -171,8 +154,10 @@ fun randomQuestion() {
|
|||
val checkData = checkAnswer(answerString);
|
||||
if (checkData) {
|
||||
triggerSnackbar("Jawaban Benar")
|
||||
submitAnswer(true)
|
||||
incrementIndexQuiz()
|
||||
} else {
|
||||
submitAnswer(false)
|
||||
triggerSnackbar("Jawaban Salah")
|
||||
}
|
||||
_isButtonVisible.value = true
|
||||
|
@ -197,4 +182,90 @@ fun randomQuestion() {
|
|||
tts?.shutdown()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
// competition ====================================================================
|
||||
private val _quizState = MutableLiveData<QuizState>()
|
||||
val quizState: LiveData<QuizState> get() = _quizState
|
||||
|
||||
private val _currentScore = MutableLiveData(0) // Skor untuk soal saat ini
|
||||
val currentScore: LiveData<Int> get() = _currentScore
|
||||
|
||||
private val _totalScore = MutableLiveData(0) // Total skor sepanjang sesi
|
||||
val totalScore: LiveData<Int> get() = _totalScore
|
||||
|
||||
private var questionList = listOf<MaterialDataModel>()
|
||||
private var currentQuestionIndex = 0
|
||||
private var wrongAttempts = 0
|
||||
private var startTime: Long = 0
|
||||
|
||||
// Konstanta untuk scoring
|
||||
private val baseScore = 100
|
||||
private val safeTime = 5
|
||||
private val timePenalty = 3
|
||||
private val wrongPenalty = 5
|
||||
private val minScore = 50
|
||||
private val bonusQuick = 10
|
||||
|
||||
/** 1️⃣ Memulai Kuis **/
|
||||
fun startQuiz(materials: List<MaterialDataModel>) {
|
||||
if (materials.isNotEmpty()) {
|
||||
questionList = materials
|
||||
currentQuestionIndex = 0
|
||||
_totalScore.value = 0 // Reset skor saat mulai
|
||||
_currentScore.value = 0
|
||||
wrongAttempts = 0
|
||||
startTime = System.currentTimeMillis()
|
||||
_quizState.value =
|
||||
QuizState.QuestionLoaded(questionList[currentQuestionIndex], _totalScore.value ?: 0)
|
||||
} else {
|
||||
_quizState.value = QuizState.Error("Tidak ada soal tersedia")
|
||||
}
|
||||
}
|
||||
|
||||
/** 2️⃣ Dipanggil saat jawaban dikirim **/
|
||||
fun submitAnswer(isCorrect: Boolean) {
|
||||
val responseTime =
|
||||
((System.currentTimeMillis() - startTime) / 1000).toInt() // Hitung waktu otomatis
|
||||
|
||||
if (isCorrect) {
|
||||
val score = calculateScore(responseTime, wrongAttempts)
|
||||
_currentScore.value = score
|
||||
_totalScore.value = (_totalScore.value ?: 0) + score // Update total skor
|
||||
moveToNextQuestion()
|
||||
} else {
|
||||
wrongAnswer()
|
||||
}
|
||||
}
|
||||
|
||||
/** 3️⃣ Dipanggil saat jawaban salah **/
|
||||
fun wrongAnswer() {
|
||||
wrongAttempts += 1
|
||||
_quizState.value = QuizState.AnswerWrong(wrongAttempts)
|
||||
}
|
||||
|
||||
/** 4️⃣ Berganti ke soal berikutnya **/
|
||||
private fun moveToNextQuestion() {
|
||||
if (currentQuestionIndex < questionList.size - 1) {
|
||||
currentQuestionIndex++
|
||||
wrongAttempts = 0
|
||||
startTime = System.currentTimeMillis()
|
||||
_quizState.value =
|
||||
QuizState.QuestionLoaded(questionList[currentQuestionIndex], _totalScore.value ?: 0)
|
||||
} else {
|
||||
_quizState.value = QuizState.QuizFinished(_totalScore.value ?: 0)
|
||||
}
|
||||
}
|
||||
|
||||
/** 🔢 Perhitungan Skor **/
|
||||
private fun calculateScore(responseTime: Int, wrongAttempts: Int): Int {
|
||||
val penaltyTime = maxOf(0, responseTime - safeTime) * timePenalty
|
||||
val penaltyWrong = wrongAttempts * wrongPenalty
|
||||
var score = baseScore - penaltyTime - penaltyWrong
|
||||
|
||||
if (responseTime <= 3) {
|
||||
score += bonusQuick
|
||||
}
|
||||
|
||||
return maxOf(minScore, score)
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
Loading…
Reference in New Issue